«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Подробный технический обзор: как мы создали CLI Pizza с помощью Go и Cobra

Подробный технический обзор: как мы создали CLI Pizza с помощью Go и Cobra

Опубликовано 1 ноября 2024 г.
Просматривать:813

Technical Deep Dive: How We Built the Pizza CLI Using Go and Cobra

На прошлой неделе команда разработчиков OpenSauced выпустила Pizza CLI, мощный и компонуемый инструмент командной строки для создания файлов CODEOWNER и интеграции с платформой OpenSauced. Создание надежных инструментов командной строки может показаться простой задачей, но без тщательного планирования и продуманных парадигм интерфейсы командной строки могут быстро превратиться в запутанный код, который трудно поддерживать и который пронизан ошибками. В этом сообщении блога мы подробно рассмотрим, как мы создали этот интерфейс командной строки с помощью Go, как мы организуем наши команды с помощью Cobra и как наша команда бережливых инженеров быстро выполняет итерации для создания мощной функциональности.

Использование Go и Cobra

CLI Pizza — это инструмент командной строки Go, который использует несколько стандартных библиотек. Простота, скорость и ориентированность на системное программирование Go делают его идеальным выбором для создания интерфейсов командной строки. По своей сути Pizza-CLI использует spf13/cobra, библиотеку начальной загрузки CLI в Go, для организации и управления всем деревом команд.

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

Структурирование кодовой базы

Одна из первых (и самых больших) проблем при создании интерфейса командной строки Go на базе Cobra — как структурировать весь ваш код и файлы. Вопреки распространенному мнению, нет предписанного способа сделать это в Go. Ни команда go build, ни утилита gofmt не будут жаловаться на то, как вы называете свои пакеты или организуете свои каталоги. Это одна из лучших частей Go: его простота и мощь позволяют легко определять структуры, которые подойдут вам и вашей команде разработчиков!

В конечном счете, на мой взгляд, лучше всего представить и структурировать кодовую базу Go на основе Cobra как дерево команд:

├── Root command
│   ├── Child command
│   ├── Child command
│   │   └── Grandchild command

В основании дерева находится корневая команда: это якорь для всего вашего приложения CLI, и она получит имя вашего CLI. В качестве дочерних команд вы получите дерево логики ветвления, которое определяет структуру работы всего потока CLI.

Одна из вещей, которую очень легко упустить из виду при создании CLI, — это пользовательский опыт. Обычно я рекомендую людям следовать парадигме «коренного глагола-существительного» при построении команд и структур дочерних команд, поскольку это логично и приводит к превосходному пользовательскому опыту.

Например, в Kubectl вы повсюду увидите эту парадигму: «kubectl get pods», «kubectl apply…» или «kubectl label pods…». Это обеспечивает разумный порядок взаимодействия пользователей с вашей командной строкой. приложение и очень помогает при разговоре о командах с другими людьми.

В конце концов, эта структура и предложения могут помочь вам понять, как вы организуете свои файлы и каталоги, но опять же, в конечном итоге вам решать, как вы структурируете свой CLI и представляете поток конечным пользователям.

В интерфейсе командной строки Pizza у нас есть четко определенная структура, в которой живут дочерние команды (и последующие внуки этих дочерних команд). В каталоге cmd в своих пакетах каждая команда получает свою собственную реализацию. Структура корневых команд находится в каталоге pkg/utils, поскольку полезно рассматривать корневую команду как утилиту верхнего уровня, используемую main.go, а не как команду, которая может потребовать длительного обслуживания. Как правило, в реализации корневой команды Go у вас есть множество шаблонных настроек, которые вы не будете особо трогать, поэтому неплохо бы убрать эти вещи с дороги.

Вот упрощенное представление структуры наших каталогов:

├── main.go
├── pkg/
│   ├── utils/
│   │   └── root.go
├── cmd/
│   ├── Child command dir
│   ├── Child command dir
│   │   └── Grandchild command dir

Эта структура позволяет четко разделить задачи и упрощает обслуживание и расширение интерфейса командной строки по мере его роста и добавления новых команд.

Использование go-git

Одной из основных библиотек, которые мы используем в Pizza-CLI, является библиотека go-git, чистая реализация git в Go, обладающая широкими возможностями расширения. Во время генерации CODEOWNERS эта библиотека позволяет нам перебирать журнал git ref, просматривать различия в коде и определять, какие авторы git связаны с настроенными атрибутами, определенными пользователем.

Итерация журнала git ref локального репозитория git на самом деле довольно проста:

// 1. Open the local git repository
repo, err := git.PlainOpen("/path/to/your/repo")
if err != nil {
        panic("could not open git repository")
}

// 2. Get the HEAD reference for the local git repo
head, err := repo.Head()
if err != nil {
        panic("could not get repo head")
}

// 3. Create a git ref log iterator based on some options
commitIter, err := repo.Log(&git.LogOptions{
        From:  head.Hash(),
})
if err != nil {
        panic("could not get repo log iterator")
}

defer commitIter.Close()

// 4. Iterate through the commit history
err = commitIter.ForEach(func(commit *object.Commit) error {
        // process each commit as the iterator iterates them
        return nil
})
if err != nil {
        panic("could not process commit iterator")
}

Если вы создаете приложение на основе Git, я определенно рекомендую использовать go-git: он быстрый, хорошо интегрируется в экосистему Go и может использоваться для самых разных задач!

Интеграция телеметрии Posthog

Наша команда инженеров и разработчиков продуктов глубоко заинтересована в том, чтобы предоставить нашим конечным пользователям наилучшие возможности командной строки: это означает, что мы предприняли шаги по интеграции анонимной телеметрии, которая может сообщать Posthog об использовании и ошибках в реальных условиях. Это позволило нам в первую очередь исправлять наиболее важные ошибки, быстро обрабатывать популярные запросы функций и понимать, как наши пользователи используют CLI.

Posthog имеет собственную библиотеку на Go, которая поддерживает именно эту функциональность. Сначала мы определяем клиента Posthog:

import "github.com/posthog/posthog-go"

// PosthogCliClient is a wrapper around the posthog-go client and is used as a
// API entrypoint for sending OpenSauced telemetry data for CLI commands
type PosthogCliClient struct {
    // client is the Posthog Go client
    client posthog.Client

    // activated denotes if the user has enabled or disabled telemetry
    activated bool

    // uniqueID is the user's unique, anonymous identifier
    uniqueID string
}

Затем, после инициализации нового клиента, мы можем использовать его с помощью различных методов структуры, которые мы определили. Например, при входе на платформу OpenSauced мы собираем конкретную информацию об успешном входе в систему:

// CaptureLogin gathers telemetry on users who log into OpenSauced via the CLI
func (p *PosthogCliClient) CaptureLogin(username string) error {
    if p.activated {
        return p.client.Enqueue(posthog.Capture{
            DistinctId: username,
            Event:      "pizza_cli_user_logged_in",
        })
    }

    return nil
}

Во время выполнения команды вызываются различные функции «перехвата», чтобы зафиксировать пути ошибок, счастливые пути и т. д.

Для анонимных идентификаторов мы используем великолепную библиотеку UUID Go от Google:

newUUID := uuid.New().String()

Эти UUID хранятся локально на компьютерах конечных пользователей в формате JSON в их домашнем каталоге: ~/.pizza-cli/telemtry.json. Это дает конечному пользователю полные права и автономию удалять эти данные телеметрии, если он хочет (или полностью отключить телеметрию с помощью параметров конфигурации!), чтобы гарантировать, что он останется анонимным при использовании CLI.

Итеративная разработка и тестирование

Наша команда бережливых инженеров придерживается итеративного процесса разработки, уделяя особое внимание быстрой доставке небольших, тестируемых функций. Обычно мы делаем это через задачи GitHub, запросы на включение, контрольные точки и проекты. Мы широко используем встроенную среду тестирования Go, пишем модульные тесты для отдельных функций и интеграционные тесты для целых команд.

К сожалению, стандартная библиотека тестирования Go не имеет отличной функциональности утверждений «из коробки». Использовать «==» или другие операнды достаточно просто, но в большинстве случаев, возвращаясь назад и читая тесты, приятно иметь возможность увидеть, что происходит с такими утверждениями, как «assert.Equal» или «assert.Nil». ».

Мы интегрировали отличную библиотеку свидетельств с ее функцией «утверждения», чтобы обеспечить более плавную реализацию тестирования:

config, _, err := LoadConfig(nonExistentPath)
require.Error(t, err)
assert.Nil(t, config)

Использование Just

Мы активно используем Just at OpenSauced, утилиту запуска команд, очень похожую на GNU make, для простого выполнения небольших скриптов. Это позволило нам быстро привлечь новых членов команды или сообщества к нашей экосистеме Go, поскольку сборка и тестирование так же просты, как «просто собрать» или «просто протестировать»!

Например, чтобы создать простую утилиту сборки в Just в файле justfile, мы можем использовать:

build:
  go build main.go -o build/pizza

При этом двоичный файл Go будет собран в каталог build/. Теперь локальная сборка так же проста, как выполнение «просто» команды.

Но мы смогли интегрировать больше функций в использование Just и сделали его краеугольным камнем всей нашей среды сборки, тестирования и разработки. Например, чтобы создать двоичный файл для локальной архитектуры с введенными переменными времени сборки (например, sha, для которого был создан двоичный файл, версия, дата и т. д.), мы можем использовать локальную среду и выполнить дополнительные шаги в скрипте. перед выполнением «go build»:

build:
    #!/usr/bin/env sh
  echo "Building for local arch"

  export VERSION="${RELEASE_TAG_VERSION:-dev}"
  export DATETIME=$(date -u  "%Y-%m-%d-%H:%M:%S")
  export SHA=$(git rev-parse HEAD)

  go build \
    -ldflags="-s -w \
    -X 'github.com/open-sauced/pizza-cli/pkg/utils.Version=${VERSION}' \
    -X 'github.com/open-sauced/pizza-cli/pkg/utils.Sha=${SHA}' \
    -X 'github.com/open-sauced/pizza-cli/pkg/utils.Datetime=${DATETIME}' \
    -X 'github.com/open-sauced/pizza-cli/pkg/utils.writeOnlyPublicPosthogKey=${POSTHOG_PUBLIC_API_KEY}'" \
    -o build/pizza

Мы даже расширили это, чтобы обеспечить кросс-архитектуру и сборку ОС: Go использует переменные среды GOARCH и GOOS, чтобы знать, на какой архитектуре ЦП и какой операционной системе следует строить. Чтобы создать другие варианты, мы можем создать для этого специальные команды Just:

# Builds for Darwin linux (i.e., MacOS) on arm64 architecture (i.e. Apple silicon)
build-darwin-arm64:
  #!/usr/bin/env sh

  echo "Building darwin arm64"

  export VERSION="${RELEASE_TAG_VERSION:-dev}"
  export DATETIME=$(date -u  "%Y-%m-%d-%H:%M:%S")
  export SHA=$(git rev-parse HEAD)
  export CGO_ENABLED=0
  export GOOS="darwin"
  export GOARCH="arm64"

  go build \
    -ldflags="-s -w \
    -X 'github.com/open-sauced/pizza-cli/pkg/utils.Version=${VERSION}' \
    -X 'github.com/open-sauced/pizza-cli/pkg/utils.Sha=${SHA}' \
    -X 'github.com/open-sauced/pizza-cli/pkg/utils.Datetime=${DATETIME}' \
    -X 'github.com/open-sauced/pizza-cli/pkg/utils.writeOnlyPublicPosthogKey=${POSTHOG_PUBLIC_API_KEY}'" \
    -o build/pizza-${GOOS}-${GOARCH}

Заключение

Создание интерфейса командной строки Pizza с использованием Go и Cobra было захватывающим путешествием, и мы рады поделиться им с вами. Сочетание производительности и простоты Go с мощным структурированием команд Cobra позволило нам создать инструмент, который не только надежный и мощный, но также удобный и удобный в обслуживании.

Мы приглашаем вас изучить репозиторий Pizza CLI на GitHub, опробовать этот инструмент и поделиться с нами своими мыслями. Ваши отзывы и вклад неоценимы, поскольку мы работаем над тем, чтобы упростить управление владением кодом для команд разработчиков во всем мире!

Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/opensauced/technical-deep-dive-how-we-built-the-pizza-cli-using-go-and-cobra-oad?1 При наличии каких-либо нарушений , пожалуйста, свяжитесь с Study_golang @163.comdelete
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3