"إذا أراد العامل أن يؤدي عمله بشكل جيد، فعليه أولاً أن يشحذ أدواته." - كونفوشيوس، "مختارات كونفوشيوس. لو لينجونج"
الصفحة الأمامية > برمجة > الغوص التقني العميق: كيف قمنا ببناء بيتزا CLI باستخدام Go وCobra

الغوص التقني العميق: كيف قمنا ببناء بيتزا CLI باستخدام Go وCobra

تم النشر بتاريخ 2024-11-01
تصفح:359

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

في الأسبوع الماضي، أصدر الفريق الهندسي لـ OpenSauced أداة Pizza CLI، وهي أداة سطر أوامر قوية وقابلة للتركيب لإنشاء ملفات CODEOWNER والتكامل مع منصة OpenSauced. قد يبدو بناء أدوات سطر أوامر قوية أمرًا سهلاً، ولكن بدون تخطيط دقيق ونماذج مدروسة، يمكن أن تصبح واجهات سطر الأوامر (CLIs) سريعًا عبارة عن فوضى متشابكة من التعليمات البرمجية التي يصعب صيانتها ومليئة بالأخطاء. في منشور المدونة هذا، سنلقي نظرة عميقة على كيفية إنشاء واجهة سطر الأوامر (CLI) هذه باستخدام Go، وكيف ننظم أوامرنا باستخدام Cobra، وكيف يقوم فريقنا الهندسي البسيط بالتكرار بسرعة لبناء وظائف قوية.

باستخدام الذهاب وكوبرا

إن Pizza CLI هي أداة سطر أوامر Go تعمل على الاستفادة من العديد من المكتبات القياسية. إن بساطة Go وسرعته وتركيزه على برمجة الأنظمة يجعله خيارًا مثاليًا لبناء CLIs. في جوهره، يستخدم Pizza-CLI spf13/cobra، وهي مكتبة التمهيد لـ CLI في Go، لتنظيم وإدارة شجرة الأوامر بأكملها.

يمكنك التفكير في كوبرا باعتبارها السقالة التي تجعل واجهة سطر الأوامر نفسها تعمل، وتمكن جميع العلامات من العمل بشكل متسق، وتتعامل مع التواصل مع المستخدمين عبر رسائل المساعدة والوثائق الآلية.

هيكلة قاعدة التعليمات البرمجية

أحد التحديات الأولى (والأكبر) عند إنشاء Go CLI المستندة إلى Cobra هو كيفية تنظيم جميع التعليمات البرمجية والملفات الخاصة بك. خلافًا للاعتقاد السائد، لا توجد طريقة موصوفة للقيام بذلك في Go. لن يشتكي أمر go build ولا الأداة المساعدة gofmt من كيفية تسمية حزمك أو تنظيم أدلةك. يعد هذا أحد أفضل أجزاء Go: حيث أن بساطته وقوته تجعل من السهل تحديد الهياكل التي تناسبك أنت وفريقك الهندسي! في النهاية، في رأيي، من الأفضل التفكير في قاعدة بيانات Go المستندة إلى Cobra وهيكلتها كشجرة أوامر:

├── الأمر الجذر │ ├── أمر الطفل │ ├── أمر الطفل │ │ └── أمر الحفيد


يوجد في قاعدة الشجرة الأمر الجذري: هذا هو الأساس لتطبيق CLI بأكمله وسيحصل على اسم CLI الخاص بك. مرفقة كأوامر فرعية، سيكون لديك شجرة من المنطق المتفرع التي تُعلم بنية كيفية عمل تدفق واجهة سطر الأوامر (CLI) بالكامل.

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

على سبيل المثال، في Kubectl، ستشاهد هذا النموذج في كل مكان: "kubectl get pods"، "kubectl Apply ..."، أو "kubectl label pods..." وهذا يضمن تدفقًا حساسًا لكيفية تفاعل المستخدمين مع سطر الأوامر الخاص بك التطبيق ويساعد كثيرًا عند التحدث عن الأوامر مع أشخاص آخرين.

في النهاية، يمكن أن يوضح هذا الهيكل والاقتراح كيفية تنظيم ملفاتك وأدلتك، ولكن مرة أخرى، الأمر متروك لك في النهاية لتحديد كيفية هيكلة سطر الأوامر الخاص بك وتقديم التدفق للمستخدمين النهائيين.

في Pizza CLI، لدينا هيكل محدد جيدًا حيث تعيش أوامر الأطفال (والأحفاد اللاحقون لأوامر الأطفال تلك). ضمن دليل cmd في الحزم الخاصة به، يحصل كل أمر على التنفيذ الخاص به. توجد سقالات أمر الجذر في دليل pkg/utils نظرًا لأنه من المفيد التفكير في أمر الجذر كأداة مساعدة ذات مستوى أعلى يستخدمها main.go، بدلاً من أمر قد يحتاج إلى الكثير من الصيانة. عادةً، في تنفيذ الأمر الجذري Go، سيكون لديك الكثير من الأمور النمطية لإعداد الأشياء التي لن تتطرق إليها كثيرًا، لذا من الجيد التخلص من هذه الأشياء.

إليك عرض مبسط لبنية الدليل الخاص بنا:

├── main.go ├── بكج/ │ ├── المرافق/ │ │ └── root.go ├── كمد/ │ ├── أمر الطفل دير │ ├── أمر الطفل دير │ │ └── أمر الحفيد دير


يسمح هذا الهيكل بالفصل الواضح بين الاهتمامات ويجعل من السهل صيانة وتوسيع واجهة سطر الأوامر (CLI) أثناء نموها وكلما أضفنا المزيد من الأوامر.

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

إحدى المكتبات الرئيسية التي نستخدمها في Pizza-CLI هي مكتبة go-git، وهي عبارة عن تطبيق git خالص في Go وقابل للتوسيع بدرجة كبيرة. أثناء إنشاء CODEOWNERS، تمكننا هذه المكتبة من تكرار سجل git ref، والنظر في اختلافات التعليمات البرمجية، وتحديد مؤلفي git المرتبطين بالسمات التي تم تكوينها والتي يحددها المستخدم.

يعد تكرار سجل git ref الخاص بمستودع git repo المحلي أمرًا بسيطًا جدًا:

// 1. افتح مستودع git المحلي الريبو، يخطئ := git.PlainOpen("/path/to/your/repo") إذا أخطأت!= لا شيء { الذعر ("تعذر فتح مستودع git") } // 2. احصل على مرجع الرأس لمستودع git المحلي الرأس يخطئ := الريبو.Head() إذا أخطأت!= لا شيء { الذعر ("تعذر الحصول على رأس الريبو") } // 3. قم بإنشاء مكرر سجل git ref بناءً على بعض الخيارات CommitIter، يخطئ := repo.Log(&git.LogOptions{ من: head.Hash()، }) إذا أخطأت!= لا شيء { الذعر ("تعذر الحصول على مكرر سجل الريبو") } تأجيل الالتزام Iter.Close() // 4. كرر خلال سجل الالتزام يخطئ = CommitIter.ForEach(func(commit *object.Commit) خطأ { // قم بمعالجة كل التزام بينما يكرره المكرر العودة لا شيء }) إذا أخطأت!= لا شيء { الذعر ("تعذرت معالجة مكرر الالتزام") }


إذا كنت تقوم بإنشاء تطبيق يستند إلى Git، فإنني أوصي بالتأكيد باستخدام go-git: فهو سريع، ويتكامل بشكل جيد مع نظام Go البيئي، ويمكن استخدامه للقيام بجميع أنواع الأشياء!

// 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")
}

يستثمر فريقنا الهندسي وفريق الإنتاج بشكل كبير في تقديم أفضل تجربة ممكنة لسطر الأوامر لمستخدمينا النهائيين: وهذا يعني أننا اتخذنا خطوات لدمج القياس عن بعد مجهول المصدر الذي يمكنه إبلاغ Posthog عن الاستخدام والأخطاء في البرية. وقد أتاح لنا ذلك إصلاح الأخطاء الأكثر أهمية أولاً، والتكرار بسرعة في طلبات الميزات الشائعة، وفهم كيفية استخدام مستخدمينا لواجهة سطر الأوامر.

لدى Posthog مكتبة الطرف الأول في Go التي تدعم هذه الوظيفة بالضبط. أولاً، نحدد عميل Posthog:

استيراد "github.com/posthog/posthog-go" // PosthogCliClient عبارة عن غلاف حول عميل posthog-go ويستخدم كـ a // نقطة دخول API لإرسال بيانات القياس عن بعد OpenSauced لأوامر CLI اكتب بنية PosthogCliClient { // العميل هو عميل Posthog Go العميل posthog.Client // يشير التنشيط إلى ما إذا كان المستخدم قد قام بتمكين القياس عن بعد أو تعطيله منطقي المنشط // UniqueID هو المعرف الفريد والمجهول للمستخدم سلسلة معرف فريد }


بعد ذلك، بعد تهيئة عميل جديد، يمكننا استخدامه من خلال طرق البنية المختلفة التي حددناها. على سبيل المثال، عند تسجيل الدخول إلى منصة OpenSauced، نحصل على معلومات محددة حول تسجيل دخول ناجح:

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
}
// يقوم CaptureLogin بجمع بيانات القياس عن بعد للمستخدمين الذين يقومون بتسجيل الدخول إلى OpenSauced عبر واجهة سطر الأوامر (CLI) func (p *PosthogCliClient) خطأ CaptureLogin (سلسلة اسم المستخدم) { إذا تم تنشيطه { إرجاع p.client.Enqueue(posthog.Capture{ المعرف المميز: اسم المستخدم، الحدث: "pizza_cli_user_logged_in"، }) } العودة لا شيء }


أثناء تنفيذ الأمر، يتم استدعاء وظائف "الالتقاط" المختلفة لالتقاط مسارات الخطأ، والمسارات السعيدة، وما إلى ذلك.

// 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
}

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


يتم تخزين معرفات UUID هذه محليًا على أجهزة المستخدمين النهائيين بتنسيق JSON ضمن الدليل الرئيسي الخاص بهم: ~/.pizza-cli/telemtry.json. يمنح هذا المستخدم النهائي السلطة والاستقلالية الكاملة لحذف بيانات القياس عن بعد هذه إذا أراد ذلك (أو تعطيل القياس عن بعد تمامًا من خلال خيارات التكوين!) لضمان عدم الكشف عن هويته عند استخدام واجهة سطر الأوامر.

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

يتبع فريقنا الهندسي البسيط عملية تطوير متكررة، مع التركيز على تقديم ميزات صغيرة قابلة للاختبار بسرعة. عادةً ما نقوم بذلك من خلال مشكلات GitHub وطلبات السحب والمعالم والمشاريع. نحن نستخدم إطار عمل الاختبار المدمج في Go على نطاق واسع، ونكتب اختبارات الوحدات للوظائف الفردية واختبارات التكامل للأوامر بأكملها.

لسوء الحظ، لا تحتوي مكتبة الاختبار القياسية لـ Go على وظيفة تأكيد رائعة خارج الصندوق. من السهل استخدام "==" أو معاملات أخرى، ولكن في معظم الأحيان، عند الرجوع إلى الاختبارات وقراءتها، من الجيد أن تكون قادرًا على مراقبة ما يحدث مع التأكيدات مثل "assert.Equal" أو "assert.Nil" ".

لقد قمنا بدمج مكتبة الشهادات الممتازة مع وظيفة "التأكيد" الخاصة بها للسماح بتنفيذ الاختبار بشكل أكثر سلاسة:

config, _, err := LoadConfig(nonExistentPath) يتطلب خطأ (ر، خطأ) تأكيد. لا شيء (ر، التكوين)


باستخدام فقط

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

على سبيل المثال، لإنشاء أداة بناء بسيطة في Just، ضمن ملف justfile، يمكن أن يكون لدينا:

يبني: اذهب لبناء main.go -o build/pizza


والذي سيؤدي إلى إنشاء ملف Go ثنائي في دليل البناء/. الآن، أصبح البناء محليًا أمرًا بسيطًا مثل تنفيذ أمر "فقط".

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

يبني: #!/usr/bin/env sh صدى "بناء القوس المحلي" تصدير الإصدار = "${RELEASE_TAG_VERSION:-dev}" تصدير DATETIME=$(date -u "%Y-%m-%d-%H:%M:%S") تصدير SHA=$(git rev-parse HEAD) اذهب للبناء \ -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}'" \ -س بناء/البيتزا


لقد قمنا بتوسيع هذا لتمكين البنية المتقاطعة وبناء نظام التشغيل: يستخدم Go متغيرات البيئة GOARCH وGOOS لمعرفة بنية وحدة المعالجة المركزية ونظام التشغيل الذي سيتم البناء عليه. لإنشاء متغيرات أخرى، يمكننا إنشاء أوامر Just محددة لذلك:

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
}
# تصميمات لـ Darwin linux (أي MacOS) على بنية Arm64 (أي Apple silicon) بناء-داروين-arm64: #!/usr/bin/env sh صدى "بناء داروين Arm64" تصدير الإصدار = "${RELEASE_TAG_VERSION:-dev}" تصدير DATETIME=$(date -u "%Y-%m-%d-%H:%M:%S") تصدير SHA=$(git rev-parse HEAD) تصدير CGO_ENABLED=0 تصدير GOOS = "داروين" تصدير GOARCH = "arm64" اذهب للبناء \ -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 بناء/بيتزا-${GOOS}-${GOARCH}


خاتمة

# 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 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