В данной статье расскажу как на Go создать консольную TUI утилиту с помощью готовых компонентов, покажу примеры интерфейсов и соберем приложение с помощью Goreleaser

То есть пройдем весь цикл создания приложения до его релиза

Все что будет описано в данной статье, я собрал в данном репозитории
https://github.com/deniskorbakov

Предыстория

Мне очень нравятся TUI, что с помощью него утилиты в терминале начинают играть новыми красками и повышается удовольствие от использования данных программ

Недавно с командой https://жыбийрыр.рф/ участвовали на хакатоне, где создавали утилиту (Go + Ansible) для автоматического деплоя и билда на сервере всех возможных приложений (антиутопическая задача)

Для данного решения так же использовал TUI компоненты для утилиты, что помогло нам больше выделиться среди участников, а в конечном итоге и победить

Что используем для создания утилиты

  1. Cobra - https://github.com/spf13/cobra

  2. Fang - https://github.com/charmbracelet/fang

  3. Huh - https://github.com/charmbracelet/huh

Cobra

Данная библиотека используется для создания консольных приложений на Go

Она предоставляет необходимый набор инструментария для создания утилит

  • Удобное API для создания команд и описание логики

  • Поддержка флагов

  • Подсказки при введении неправильной командой и тд

Fang

Данная библиотека изменяет описание утилиты делая ее еще более информативнее и ярче, с коробки интегрируется с Cobra

При добавлении Fang вы получаете следующий интерфейс:


Huh

Данная библиотека предоставляет набор готовых компонентов TUI и прекрасно вписываются в цветовую палитру Fang

Вот такие компоненты вы можете получить при использовании данной библиотеки:

Создание утилиты

Клонируем шаблон

git clone https://github.com/deniskorbakov/skeleton-cli-go.git  

Переходим в папку с проектом

cd skeleton-cli-go  

Собираем наше приложение

make build  

Запускаем шаблонную утилиту

./cli  

Архитектура приложения

cmd/cli - содержит директорию с названием нашего приложения в ней находится main файл, который инициализирует cobra

Скрытый текст
package main  
  
import "github.com/deniskorbakov/skeleton-cli-go/internal/command"  
  
func main() {  
    command.Run()  
}

configs/constname - содержит константы, которые описывают (название, короткое и длинное описание команд)

example.go

Скрытый текст
package constname  
  
const (  
    // UseExampleCmd Name example command    
    UseExampleCmd = `example`  
  
    // ShortExampleCmd Short description example command
    ShortExampleCmd = `Example test command`  
    ...
)

root.go

Скрытый текст
package constname  
  
const (  
    // UseRootCmd App name and default name command    
    UseRootCmd = `cli`  
  
    // LongRootCmd Long description root command    
    LongRootCmd = `cli is a example util`  
    ...
)

internal/command - содержит команды приложения

example.go

Скрытый текст
package command  
  
import (  
	"github.com/deniskorbakov/skeleton-cli-go/configs/constname"  
	"github.com/deniskorbakov/skeleton-cli-go/internal/component/form"
	"github.com/deniskorbakov/skeleton-cli-go/internal/component/output"
	"github.com/spf13/cobra"
)  
  
// exampleCmd Command for build pipeline 
var exampleCmd = &cobra.Command{  
    Use:   constname.UseExampleCmd,  
    Short: constname.ShortExampleCmd,  
    Long:  constname.LongExampleCmd,  
    RunE: func(cmd *cobra.Command, args []string) error {  
       fields, err := form.Run()  
       if err != nil {  
          return err  
       }  
  
       output.Green("Success green output: ", fields.ExampleInput)  
       output.Red("This command will be run successfully")  
  
       return nil  
    },  
}

Здесь пример команды которую мы должны будем добавить в root.go

root.go

Скрытый текст
package command  
  
import (  
    "context"  
    "os"  
    "github.com/charmbracelet/fang"
    "github.com/deniskorbakov/skeleton-cli-go/configs/constname"
    "github.com/deniskorbakov/skeleton-cli-go/internal/component/output"
    "github.com/deniskorbakov/skeleton-cli-go/internal/version"
    "github.com/spf13/cobra"
)  
  
// Run Start app with cobra cmd
func Run() {  
    cmd := &cobra.Command{  
       Use:     constname.UseRootCmd,  
       Long:    constname.LongRootCmd,  
       Example: constname.ExampleRootCmd,  
    }  
  
    // Disable default options cmd  
    cmd.Root().CompletionOptions.DisableDefaultCmd = true  
  
    // Add all command in your app  
    cmd.AddCommand(  
       exampleCmd,  
    )  
  
    if err := fang.Execute(  
       context.Background(),  
       cmd,  
       fang.WithVersion(version.Get()),  
    ); err != nil {  
       output.Red("The app is broken")  
       os.Exit(1)  
    }  
}

Данный файл содержит логику описания главной команды, настройку утилиты, список добавленных команд и инициализацию Cobra

internal/component - содержит компоненты приложения

internal/component/form/form.go

Скрытый текст
package form  
  
import "github.com/charmbracelet/huh"  
  
// Run Main function that runs an interactive form
func Run() (*Fields, error) {  
    fields := &Fields{}  
  
    err := huh.NewForm(  
       huh.NewGroup(  
          huh.NewInput().  
             Title("Example Title").  
             Description("Example description input").  
             Value(&fields.ExampleInput),  
       ),  
    ).WithShowHelp(true).Run()  
    if err != nil {  
       return nil, err  
    }  
  
    return fields, nil  
}

Здесь мы создаем форму с помощью huh

internal/component/form/fields.go

Скрытый текст
package form  
  
// Fields struct for huh form
type Fields struct {  
    ExampleInput string  
}

Данная структура содержит поля которые мы указываем при создании формы

В своем приложении вы можете убрать данную реализацию если считаете ее излишней и использовать любой другой вывод

internal/component/output/output.go

Скрытый текст
package output  
  
import (  
    "fmt"  
  
    "github.com/charmbracelet/lipgloss/v2")  
  
var (  
    green = lipgloss.Color("#04B575")  
    red   = lipgloss.Color("#D4634C")  
)  
  
func output(colorText string) {  
    fmt.Println(colorText)  
}  
  
func Green(str ...string) {  
    output(lipgloss.NewStyle().Foreground(green).Render(str...))  
}  
  
func Red(str ...string) {  
    output(lipgloss.NewStyle().Foreground(red).Render(str...))  
}

Данный компонент служит для вывода текста в консоль

Так же вы можете спокойно расширять данный компонент, добавляя свои цвета, для этого используется - lipgloss

internal/version - находится логика парсинга версии приложения

internal/version/version.go

Скрытый текст
package version  
  
import (  
    "regexp"  
    "runtime/debug")  
  
var (  
    regexVersion = `^v?(\d+\.\d+\.\d+)`  
    emptyVersion = "none version"  
)  
  
// Get return version from debug main version
func Get() string {  
    if info, ok := debug.ReadBuildInfo(); ok {  
       version := info.Main.Version  
  
       re := regexp.MustCompile(regexVersion)  
       if matches := re.FindStringSubmatch(version); len(matches) > 1 {  
          return matches[1]  
       }  
       return emptyVersion  
    } else {  
       return emptyVersion  
    }  
}

Получаем указанную версию при билде приложения и через регулярку получаем значения версии, если ее не находим то выводим, что версия не найдена

Изменение название утилиты и ее описание

Сейчас в нашем проекте утилита называется cli - давайте заменим ее на ваше название утилиты

Поменяем путь к вашему приложению на имя вашей утилиты cmd/cli -> cmd/your_name_cli

В файле .goreleaser.yaml вам нужно изменить название cli на имя вашей утилиту

Скрытый текст
version: 2  
  
env:  
  - GO111MODULE=on  
project_name: your_name_cli  

И так же надо заменить имя приложения в Makefile

Скрытый текст
build:  
    go mod vendor    
    go build -ldflags "-w -s" -o your_name_cli cmd/your_name_cli/main.go  

Меняем описание приложения в команде root - configs/constname/root.go

Goreleaser

Это инструмент для автоматизации процесса создания релизов проектов под разные дистрибутивы и OS

В проекте уже есть файл .goreleaser.yaml и так же настроен GitHub Action

Вам останется только добавить секрет в репозиторий с именем GO_RELEASER который будет содержать личный GitHub токен

Итог

В данной статье я показал вам как с помощью шаблона вы можете создать TUI свою утилиту на Go

GitHub - буду рад вашей подписки на меня

Благодарю вас за то, что прочитали данную статью

Комментарии (1)


  1. alexs963
    06.10.2025 09:01

    Для Go есть аналог TurboVision?