Содержание урока · 10 разделов
Задача: через полчаса иметь работающую утилиту, которая отвечает на своё имя в терминале. По пути решим три вопроса, на которых обычно застревают. Первый — что именно ставить: поиск по «cobra» выдаёт и библиотеку, и генератор, и это разные вещи. Второй — куда девается бинарник после go install: если папка ~/go/bin не прописана в PATH, шелл честно ответит command not found, и будет прав. Третий — что генератор создал в проекте и что из этого менять, а что не трогать никогда. Установленный Go — единственная предпосылка: всё остальное делается командами из этого урока. Проверьте себя заранее: go version в терминале должен напечатать что-то вроде go1.22.5 linux/amd64. Если не печатает — сначала поставьте Go с официального сайта, остальное не заработает в принципе. И добавим в план ещё один пункт — привычку проверять справку новой утилиты через --help: она станет главным зеркалом того, что вы поменяли в дереве.
Установка Cobra: библиотека и генератор — две разные вещи#
Установка Cobra начинается с разведки, потому что под этим словом продаются два продукта. Библиотека github.com/spf13/cobra — код, который попадёт в ваш исполняемый файл и будет разбирать командную строку при каждом запуске. Генератор cobra-cli — отдельная утилита для вашей машины: она создаёт каркас проекта и заготовки подкоманд, а в собранном бинарнике не остаётся ни строчки. Ставится она одной командой go install github.com/spf13/cobra-cli@latest — и вот здесь важно понять механику. Имя бинарника задаёт последняя часть пути импорта, поэтому утилита в терминале называется cobra-cli: исторически генератор жил внутри репозитория библиотеки под именем cobra, а потом выделился в отдельный проект. go install не «устанавливает программу в систему» по виндовым меркам: он компилирует пакет и кладёт бинарник в $GOBIN, а если тот не задан — в $GOPATH/bin, то есть по умолчанию в ~/go/bin. Сама библиотека cobra при этом не ставится вовсе: она появится в go.mod вашего проекта позже, когда генератор её туда добавит. Разделение странное ровно до первого запуска — потом становится очевидным.
Ставим cobra-cli и чиним PATH
GO# ставим генератор каркасов
go install github.com/spf13/cobra-cli@latest
# бинарник должен оказаться здесь
ls ~/go/bin | grep cobra-cli
# если шелл его не находит — добавьте папку в PATH
export PATH=$PATH:$(go env GOPATH)/binПервая команда компилирует генератор и кладёт бинарник cobra-cli в $GOPATH/bin — по умолчанию ~/go/bin. Вторая проверяет, что файл на месте. Третья добавляет папку в PATH текущей сессии: чтобы работало всегда, допишите export-строку в ~/.bashrc или ~/.zshrc и перезапустите терминал. На Windows путь другой: %USERPROFILE%\go\bin — его добавляют в переменную среды Path через настройки системы. Если задан $GOBIN, бинарник уедет туда, и искать его в ~/go/bin бессмысленно — go env GOBIN расставит вопросы по местам.
cobra-cli init: первый проект#
Генератор работает внутри существующего модуля Go, поэтому порядок такой: папка, go mod init, cobra-cli init. Пропустить второй шаг нельзя — генератор проверяет наличие go.mod и без него падает с ошибкой go.mod file not found, что новичков сбивает с толку: каркас-то ещё не создан, а ругаться начинают уже на модуль. Имя модуля берите осмысленное — оно станет префиксом импортов во всём проекте: github.com/you/tasker означает, что пакет команд будет импортироваться как github.com/you/tasker/cmd. У init есть необязательные флаги: --author вписывает автора в справку, --license добавляет файл лицензии. Я их обычно пропускаю на прототипах и добавляю перед публикацией — менять автора в сгенерированных файлах потом приходится руками, что мелочь, но неприятная.
Сессия в терминале: от папки до каркаса
GOmkdir tasker && cd tasker
go mod init github.com/you/tasker
cobra-cli init
# ответ генератора:
# Your Cobra application is ready at
# /home/you/code/tasker
#
# Please give more information consider changing
# the short description of your app inside the cmd/root.go
go run main.goСоздали папку, инициализировали модуль с именем github.com/you/tasker, развернули каркас. Генератор подсказывает, что Short-описание в cmd/root.go стоит заменить на своё, — мы это сделаем через минуту. go run компилирует пакет на лету и запускает: утилита уже живая.
Что сгенерировано: карта проекта#
Загляните в папку — файлов мало, и каждый на своём месте. main.go — точка входа из трёх строк, cmd/root.go — корень дерева команд, go.mod — манифест модуля с зависимостями, go.sum — контрольные суммы скачанных пакетов. Такой расклад не случайность, а конвенция, которая держится во всех Cobra-проектах от pet-репозиториев до kubectl: логика команд живёт в пакете cmd, точка входа остаётся тонкой. Когда подкоманд станет много, каждая получит свой файл в cmd — add.go, list.go, done.go — и дерево будет собираться из них, как из кубиков. Обновите Short и Long в root.go прямо сейчас: эти строки пользователь увидит первым делом в справке, и «A brief description of your application» там смотрится хуже, чем честное описание вашей утилиты. Заодно инициализируйте git-репозиторий в этой же папке: go.mod и go.sum коммитятся, собранный бинарник отправляйте в .gitignore.
Файлы нового проекта#
- main.go — точка входа: вызывает cmd.Execute() и ничего не знает о командах
- cmd/root.go — корневая команда: имя утилиты, описания, флаги уровня всего приложения
- go.mod — манифест модуля: имя модуля, версия Go, зависимость на github.com/spf13/cobra
- go.sum — контрольные суммы зависимостей; в git-репозиторий включать обязательно
- cmd/ — сюда же лягут будущие подкоманды: одна подкоманда — один файл
main.go: три строки точки входа
GOpackage main
import "github.com/you/tasker/cmd"
func main() {
cmd.Execute()
}Точка входа сводится к вызову cmd.Execute(). Импорт github.com/you/tasker/cmd — это имя модуля из go.mod плюс папка пакета; такой же приём с тонким main вы видели в структуре программы базового курса. Вся логика CLI живёт в cmd, поэтому main.go не разрастается никогда.
cmd/root.go: сердце приложения
GOpackage cmd
import (
"os"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "tasker",
Short: "Менеджер задач в терминале",
Long: `Tasker — CLI для личных задач.
Пока команда одна — запуск без аргументов печатает справку.
Подкоманды добавим в следующем уроке.`,
}
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
rootCmd.Flags().BoolP("toggle", "t", false, "toggle help message")
}Пакет cmd объявлен отдельным от main — так его импортирует точка входа. rootCmd — корень будущего дерева: Use станет именем исполняемого файла, Long — развёрнутой справкой. Execute оборачивает rootCmd.Execute() и превращает ошибку в код выхода 1 — скрипты вызова скажут вам спасибо. init() регистрирует флаг toggle, оставленный генератором как образец: разберём его в уроке про флаги.
Первый запуск: go run и сборка бинарника#
Два способа запустить — и они отвечают на разные вопросы. go run main.go компилирует на лету и сразу исполняет: удобно на каждой итерации разработки, бинарник при этом не сохраняется. go build собирает настоящий исполняемый файл в текущую папку: ./tasker — и утилиту можно копировать, добавлять в PATH и отдавать другим людям. Проверьте оба, затем вызовите ./tasker --help: вместо заглушки из генератора должна напечататься ваша справка с Long-текстом. Если go build вдруг не находит main.go — вы не в той папке: генератор кладёт файлы в директорию, где лежит go.mod. Тот же вопрос возникает про cmd/root.go: удалять функцию Execute или переименовывать rootCmd не стоит — main импортирует пакет и зовёт cmd.Execute() по этому имени, каркас рассчитан именно на него. И повторю то, о чём будет напоминать каждый библиотечный урок курса: это всё выполняется локально — браузерная песочница сайта не умеет ни os.Args, ни бинарники, поэтому привычки гонять код постранично здесь не сработают. Терминал рядом с редактором — обязательный инструмент этого курса.
go.mod: зачем генератору файл модуля#
go.mod появился не ради Cobra — это механизм модулей Go, где хранится имя модуля, версия языка и список зависимостей с версиями. Генератору он нужен по двум причинам. Первая техническая: без go.mod не компилируется пакет cmd, потому что импорт внутри проекта строится от имени модуля. Вторая практическая: cobra-cli init добавляет в зависимости github.com/spf13/cobra с актуальной версией — вручную вводить go get не требуется. Если хотите глубже понять механику модулей и файл go.mod, в уроке про пакеты и модули продвинутого курса она разобрана целиком. Здесь достаточно одного правила: имя модуля, которое вы передали go mod init, должно быть валидным путём импорта, иначе на шаге cobra-cli init получите невнятную ошибку. Версию библиотеки генератор фиксирует свежей стабильной; поднять её позже можно командой go get -u github.com/spf13/cobra — руками править go.mod не нужно.
Самая частая ошибка после установки. Бинарник лежит в ~/go/bin, но эта папка не входит в PATH по умолчанию. Проверка: ls ~/go/bin. Лечение: добавить export PATH=$PATH:$(go env GOPATH)/bin в профиль шелла. Вторая по частоте — забытый go mod init перед cobra-cli init: генератор отвечает go.mod file not found.
cobra-cli ставится с суффиксом @latest — это фиксация на последний релиз на момент установки. Со временем генератор стоит обновлять той же командой: шаблоны проектов изредка меняются, например вместе с форматом Execute. Уже созданный проект при этом не затрагивается — генератор работает только по созданию.
Проверка: установка
Какой командой ставится генератор каркасов Cobra?
Проверка: структура проекта
Где генератор размещает корневую команду приложения?
Что напечатает только что сгенерированный проект
Прочитайте код и выберите, что он выведет:
var rootCmd = &cobra.Command{
Use: "tasker",
Short: "Менеджер задач в терминале",
Long: `Tasker — CLI для личных задач.
Подкоманды добавим в следующем уроке.`,
}
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}Восстановите установку с нуля
Впишите путь импорта генератора, имя модуля, команду каркаса и файл точки входа.
Соберите последовательность первого проекта
Расставьте команды терминала в правильном порядке: от пустой папки до справки собранной утилиты.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: первый проект
Что дальше#
Каркас стоит, утилита отвечает на --help — пора наполнять её командами. В третьем уроке построим дерево команд: добавим tasker add и tasker list, разберём rootCmd.AddCommand и поля Use, Short, Long, из которых собирается справка. Четвёртый урок научит команды принимать аргументы и валидировать их количество. Следом займёмся флагами — тем самым toggle из root.go наконец получит работу. Домашнее задание ниже закрепляет механику установки: она должна стать рефлексом, потому что новые проекты Cobra вы будете создавать ещё не раз.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Установка Cobra — это два разных действия: генератор cobra-cli на машину и библиотека cobra в go.mod проекта.
- go install кладёт бинарник в $GOPATH/bin — папку нужно добавить в PATH, иначе command not found.
- Порядок первого проекта: mkdir, go mod init, cobra-cli init — без go.mod генератор падает.
- Каркас минимален: тонкий main.go, пакет cmd с root.go, go.mod с зависимостью на cobra.
- У корневой команды без Run запуск печатает справку — паники нет, Run нужен только исполняющим командам.
- go run — для итераций, go build — для бинарника, который можно распространять.
- Всё это выполняется локально с установленным Go — песочница сайта Cobra не запускает.
Домашнее задание
Пройдите путь урока с нуля в другой папке: установите cobra-cli, создайте модуль с собственным именем, например github.com/<ваш ник>/deck, и выполните cobra-cli init. Затем замените Short и Long в cmd/root.go на описание вашей будущей утилиты, соберите бинарник go build и вызовите ./deck --help. В конце удалите файл cmd/root.go строку с флагом toggle и убедитесь, что проект всё ещё собирается. Заодно выполните go env GOPATH и сверьте, что в этой папке лежит cobra-cli: привычка проверять окружение перед установкой экономит десять минут на каждом новом проекте.
Личные заметкиПусто
Вопросы и ответы по уроку
Нужно ли ставить саму библиотеку cobra через go get перед init?
Нет. Генератор cobra-cli init сам добавляет github.com/spf13/cobra в go.mod проекта. Вручную go get нужен только если вы строите проект без генератора или обновляете версию библиотеки.
Куда установился бинарник cobra-cli?
В $GOBIN, а если он не задан — в $GOPATH/bin, по умолчанию это ~/go/bin. Если шелл не находит команду, добавьте эту папку в PATH в профиле шелла и перезапустите терминал.
Можно ли работать с Cobra без генератора?
Да: библиотека не зависит от cobra-cli. Создайте go.mod, папку cmd с rootCmd и Execute руками — генератор лишь экономит минуты и служит справочником канонической структуры, но не обязателен.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Сравните с альтернативами
Практический вопрос после первых уроков: а почему именно эта библиотека? Мы разобрали конкурентов по одним критериям, с кодом одной задачи рядом и персональным подбором под ваши приоритеты.
Изучайте дальше
Следующая ступень учебного пути
Bubble Tea: TUI-приложения в терминале
Интерактивный курс Bubble Tea — TUI-фреймворка на Go: Elm-архитектура, bubbles, Lip Gloss, мышь и клавиатура. 25 уроков с примерами и итоговым приложением.
- Понимать Elm-архитектуру: Model, Update, View, Init
- Обрабатывать клавиатуру и мышь в терминале
- Писать асинхронные операции через tea.Cmd, Batch и Sequence
- Использовать bubbles: list, textinput, textarea, table, viewport, progress, spinner