الترقيم التلقائي لبناءات Go: حل مشكلة إدارة الإصدارات مرة واحدة وإلى الأبد

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا إدارة الإصدارات يدويًا هي كابوس لكل مطور Go

\n

هل سبق لك أن أمسكت بنفسك تفكر: "أي إصدار يعمل حاليًا على الإنتاج؟" أو أضعت ساعة لتفهم أي commit تسبب في خطأ في ملف ثنائي تم بناؤه قبل شهر؟ إذا كان الأمر كذلك - فقد وصلت إلى المكان الصحيح. سؤال "Application auto build versioning" هو واحد من أكثر الأسئلة شيوعًا على Stack Overflow (آلاف المشاهدات وعشرات الحلول). وليس من دون سبب: في التطوير الحديث، حيث تقوم خطوط CI/CD بتشغيل البناءات كل 10 دقائق، فإن وضع الإصدارات يدويًا هو طريق نحو الفوضى.

\n

في هذه المقالة، سنشرح الإصدار التلقائي للبناءات في Go من الألف إلى الياء. ستتعلم كيفية تنفيذ نظام يقوم تلقائيًا بتعيين رقم الإصدار، وhash الـ commit، وتاريخ البناء، وحتى علامة المستودع "المتسخ". سيعمل الكود في كل من التطوير المحلي وفي GitHub Actions أو GitLab CI أو Jenkins.

\n\n

المشكلة: ما الخطأ في ملف version.go اليدوي؟

\n

كثير من المطورين المبتدئين ينشئون ملف version.go ويكتبون:

\n
package main\n\nvar Version = "1.0.0"
\n

وهذا يعمل تمامًا حتى أول commit. بعد ذلك يبدأ:

\n
    \n
  • "نسيت تحديث الإصدار قبل البناء"
  • \n
  • "على الإنتاج إصدار، وفي الكود إصدار آخر"
  • \n
  • "أي commit يتوافق مع هذا البناء؟"
  • \n
\n

المشكلة هي أن الإصدار هو بيانات وصفية للبناء، وليس جزءًا من الكود المصدري. يجب أن يتم توليده تلقائيًا في مرحلة التجميع.

\n\n

الحل: ldflags - السلاح السري لـ Go

\n

مترجم Go يسمح "بإدراج" قيم المتغيرات أثناء البناء من خلال العلم -ldflags. هذا هو auto build versioning في شكله الكلاسيكي. نحن لا نخزن الإصدار في الكود - بل نمرره إلى المترجم.

\n\n

الخطوة 1: تحديد هيكل الإصدار

\n

لننشئ حزمة version بمتغيرات سيتم ملؤها في مرحلة البناء:

\n
// version/version.go\npackage version\n\nvar (\n    // Version — الإصدار الدلالي (يُضبط يدويًا أو عبر git tag)\n    Version = "dev"\n\n    // Commit — hash قصير للـ commit\n    Commit = "none"\n\n    // BuildTime — وقت البناء بتنسيق ISO 8601\n    BuildTime = "unknown"\n\n    // IsDirty — علامة أن المستودع يحتوي على تغييرات غير ملتزمة\n    IsDirty = "false"\n)\n\nfunc String() string {\n    return "v" + Version + " (commit: " + Commit + ", built: " + BuildTime + ")"\n}
\n\n

الخطوة 2: البناء باستخدام ldflags

\n

الآن الجزء الممتع. بدلاً من go build، سنستخدم سكريبت أو Makefile يقوم بحساب القيم المطلوبة وتمريرها إلى المترجم:

\n
#!/bin/bash\n# build.sh\n\nVERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "dev")\nCOMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "none")\nBUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")\nIS_DIRTY=$(git diff --quiet || echo "true")\n\ngo build -ldflags="\\\n    -X 'main/version.Version=$VERSION' \\\n    -X 'main/version.Commit=$COMMIT' \\\n    -X 'main/version.BuildTime=$BUILD_TIME' \\\n    -X 'main/version.IsDirty=$IS_DIRTY'" \\\n    -o myapp .
\n

هام: المسار main/version.Version يعتمد على هيكل مشروعك. إذا كانت الحزمة version موجودة في الجذر - اكتب main/version.Version. إذا كانت في دليل منفصل - github.com/your-username/project/version.Version.

\n\n

الخطوة 3: الاستخدام في التطبيق

\n

الآن في أي مكان في البرنامج يمكنك عرض الإصدار:

\n
// main.go\npackage main\n\nimport (\n    "fmt"\n    "main/version"\n)\n\nfunc main() {\n    fmt.Println("App version:", version.String())\n    fmt.Println("Is dirty repo:", version.IsDirty)\n}
\n

قم بالبناء والتشغيل:

\n
$ ./build.sh\n$ ./myapp\nApp version: v1.2.3 (commit: a1b2c3d, built: 2024-03-15T10:30:00Z)\nIs dirty repo: false
\n\n

المستوى المتقدم: الإصدار الدلالي

المدونات

توصيات الكتب