首頁 後端開發 Golang 技術深入探討:我們如何使用 Go 和 Cobra 來建立 Pizza CLI

技術深入探討:我們如何使用 Go 和 Cobra 來建立 Pizza CLI

Sep 24, 2024 am 06:18 AM

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

上週,OpenSauced 工程團隊發布了 Pizza CLI,這是一個功能強大且可組合的命令列工具,用於生成 CODEOWNER 檔案並與 OpenSauced 平台整合。建立強大的命令列工具看起來很簡單,但如果沒有仔細的規劃和深思熟慮的範例,CLI 很快就會變成難以維護且充滿錯誤的混亂程式碼。在這篇文章中,我們將深入探討如何使用 Go 建立此 CLI、如何使用 Cobra 組織命令,以及我們的精實工程團隊如何快速迭代以建立強大的功能。

使用 Go 和 Cobra

Pizza CLI 是一個 Go 命令列工具,它利用了多個標準函式庫。 Go 的簡單性、速度和系統程式設計重點使其成為建立 CLI 的理想選擇。 Pizza-CLI 的核心是使用 spf13/cobra(Go 中的 CLI 引導庫)來組織和管理整個命令樹。

您可以將 Cobra 視為使命令列介面本身正常工作的腳手架,使所有標誌能夠一致地運行,並透過幫助訊息和自動文件處理與使用者的通訊。

建構程式碼庫

建構基於 Cobra 的 Go CLI 時的首要(也是最大)挑戰之一是如何建立所有程式碼和檔案。與普遍的看法相反,在 Go 中沒有沒有規定的方法來做到這一點。 go build 命令和 gofmt 實用程式都不會抱怨您如何命名套件或組織目錄。這是 Go 最好的部分之一:它的簡單性和強大功能使您可以輕鬆定義適合您和您的工程團隊的結構!

最終,在我看來,最好將基於 Cobra 的 Go 程式碼庫視為命令樹:

├── Root command
│   ├── Child command
│   ├── Child command
│   │   └── Grandchild command
登入後複製

樹的底部是根命令:這是整個 CLI 應用程式的錨點,並將取得 CLI 的名稱。作為子命令附加,您將擁有一個分支邏輯樹,它告知整個 CLI 流程如何運作的結構。

建構 CLI 時最容易忽略的事情之一就是使用者體驗。我通常建議人們在建立命令和子命令結構時遵循「根動詞名詞」範例,因為它邏輯流暢並帶來出色的使用者體驗。

例如,在Kubectl 中,您會隨處看到這種範例:「kubectl get pods」、「kubectl apply …」或「kubectl label pods …」這確保了使用者如何與命令列互動的合理流程應用程式,並且在與其他人談論命令時有很大幫助。

最後,此結構和建議可以告訴您如何組織文件和目錄,但最終還是由您決定如何建立 CLI 並將流程呈現給最終用戶。

在 Pizza CLI 中,我們有一個定義良好的結構,子命令(以及這些子命令的後續子命令)所在的結構。在各自套件的 cmd 目錄下,每個指令都有自己的實作。 root 命令腳手架存在於 pkg/utils 目錄中,因為將 root 命令視為 main.go 使用的頂級實用程式非常有用,而不是可能需要大量維護的命令。通常,在你的根命令 Go 實作中,你會有很多樣板設定你不會太多接觸的東西,所以最好把這些東西去掉。

這是我們目錄結構的簡化視圖:

├── main.go
├── pkg/
│   ├── utils/
│   │   └── root.go
├── cmd/
│   ├── Child command dir
│   ├── Child command dir
│   │   └── Grandchild command dir
登入後複製

這種結構可以清晰地分離關注點,並且隨著 CLI 的增長以及我們添加更多命令,可以更輕鬆地維護和擴展 CLI。

使用 go-git

我們在 Pizza-CLI 中使用的主要函式庫之一是 go-git 函式庫,它是 Go 中的一個純 git 實現,具有高度可擴展性。在 CODEOWNERS 生成過程中,該程式庫使我們能夠迭代 git ref 日誌、查看程式碼差異並確定哪些 git 作者與使用者定義的配置屬性相關聯。

迭代本機 git 儲存庫的 git ref 日誌其實非常簡單:

// 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 生態系統整合良好,並且可用於執行各種操作!

Integrating Posthog telemetry

Our engineering and product team is deeply invested in bringing the best possible command line experience to our end users: this means we’ve taken steps to integrate anonymized telemetry that can report to Posthog on usage and errors out in the wild. This has allowed us to fix the most important bugs first, iterate quickly on popular feature requests, and understand how our users are using the CLI.

Posthog has a first party library in Go that supports this exact functionality. First, we define a Posthog client:

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
}
登入後複製

Then, after initializing a new client, we can use it through the various struct methods we’ve defined. For example, when logging into the OpenSauced platform, we capture specific information on a successful login:

// 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
}
登入後複製

During command execution, the various “capture” functions get called to capture error paths, happy paths, etc.

For the anonymized IDs, we use Google’s excellent UUID Go library:

newUUID := uuid.New().String()
登入後複製

These UUIDs get stored locally on end users machines as JSON under their home directory: ~/.pizza-cli/telemtry.json. This gives the end user complete authority and autonomy to delete this telemetry data if they want (or disable telemetry altogether through configuration options!) to ensure they’re staying anonymous when using the CLI.

Iterative Development and Testing

Our lean engineering team follows an iterative development process, focusing on delivering small, testable features rapidly. Typically, we do this through GitHub issues, pull requests, milestones, and projects. We use Go's built-in testing framework extensively, writing unit tests for individual functions and integration tests for entire commands.

Unfortunately, Go’s standard testing library doesn’t have great assertion functionality out of the box. It’s easy enough to use “==” or other operands, but most of the time, when going back and reading through tests, it’s nice to be able to eyeball what’s going on with assertions like “assert.Equal” or “assert.Nil”.

We’ve integrated the excellent testify library with its “assert” functionality to allow for smoother test implementation:

config, _, err := LoadConfig(nonExistentPath)
require.Error(t, err)
assert.Nil(t, config)
登入後複製

Using Just

We heavily use Just at OpenSauced, a command runner utility, much like GNU’s “make”, for easily executing small scripts. This has enabled us to quickly onramp new team members or community members to our Go ecosystem since building and testing is as simple as “just build” or “just test”!

For example, to create a simple build utility in Just, within a justfile, we can have:

build:
  go build main.go -o build/pizza
登入後複製

Which will build a Go binary into the build/ directory. Now, building locally is as simple as executing a “just” command.

But we’ve been able to integrate more functionality into using Just and have made it a cornerstone of how our entire build, test, and development framework is executed. For example, to build a binary for the local architecture with injected build time variables (like the sha the binary was built against, the version, the date time, etc.), we can use the local environment and run extra steps in the script before executing the “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
登入後複製

We’ve even extended this to enable cross architecture and OS build: Go uses the GOARCH and GOOS env vars to know which CPU architecture and operating system to build against. To build other variants, we can create specific Just commands for that:

# 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}
登入後複製

Conclusion

Building the Pizza CLI using Go and Cobra has been an exciting journey and we’re thrilled to share it with you. The combination of Go's performance and simplicity with Cobra's powerful command structuring has allowed us to create a tool that's not only robust and powerful, but also user-friendly and maintainable.

We invite you to explore the Pizza CLI GitHub repository, try out the tool, and let us know your thoughts. Your feedback and contributions are invaluable as we work to make code ownership management easier for development teams everywhere!

以上是技術深入探討:我們如何使用 Go 和 Cobra 來建立 Pizza CLI的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1672
14
CakePHP 教程
1428
52
Laravel 教程
1332
25
PHP教程
1276
29
C# 教程
1256
24
Golang vs. Python:性能和可伸縮性 Golang vs. Python:性能和可伸縮性 Apr 19, 2025 am 12:18 AM

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

Golang和C:並發與原始速度 Golang和C:並發與原始速度 Apr 21, 2025 am 12:16 AM

Golang在並發性上優於C ,而C 在原始速度上優於Golang。 1)Golang通過goroutine和channel實現高效並發,適合處理大量並發任務。 2)C 通過編譯器優化和標準庫,提供接近硬件的高性能,適合需要極致優化的應用。

開始GO:初學者指南 開始GO:初學者指南 Apr 26, 2025 am 12:21 AM

goisidealforbeginnersandsubableforforcloudnetworkservicesduetoitssimplicity,效率和concurrencyFeatures.1)installgromtheofficialwebsitealwebsiteandverifywith'.2)

Golang vs.C:性能和速度比較 Golang vs.C:性能和速度比較 Apr 21, 2025 am 12:13 AM

Golang適合快速開發和並發場景,C 適用於需要極致性能和低級控制的場景。 1)Golang通過垃圾回收和並發機制提升性能,適合高並發Web服務開發。 2)C 通過手動內存管理和編譯器優化達到極致性能,適用於嵌入式系統開發。

Golang vs. Python:主要差異和相似之處 Golang vs. Python:主要差異和相似之處 Apr 17, 2025 am 12:15 AM

Golang和Python各有优势:Golang适合高性能和并发编程,Python适用于数据科学和Web开发。Golang以其并发模型和高效性能著称,Python则以简洁语法和丰富库生态系统著称。

Golang和C:性能的權衡 Golang和C:性能的權衡 Apr 17, 2025 am 12:18 AM

Golang和C 在性能上的差異主要體現在內存管理、編譯優化和運行時效率等方面。 1)Golang的垃圾回收機制方便但可能影響性能,2)C 的手動內存管理和編譯器優化在遞歸計算中表現更為高效。

表演競賽:Golang vs.C 表演競賽:Golang vs.C Apr 16, 2025 am 12:07 AM

Golang和C 在性能競賽中的表現各有優勢:1)Golang適合高並發和快速開發,2)C 提供更高性能和細粒度控制。選擇應基於項目需求和團隊技術棧。

Golang vs. Python:利弊 Golang vs. Python:利弊 Apr 21, 2025 am 12:17 AM

Golangisidealforbuildingscalablesystemsduetoitsefficiencyandconcurrency,whilePythonexcelsinquickscriptinganddataanalysisduetoitssimplicityandvastecosystem.Golang'sdesignencouragesclean,readablecodeanditsgoroutinesenableefficientconcurrentoperations,t

See all articles