ليلة خميس لا تُنسى: حين كان الإصدار يدوياً
أذكرها وكأنها البارحة، ليلة خميس وكنا على وشك إنهاء أسبوع عمل طويل. الفريق كله كان يجهز نفسه للعودة إلى البيت، لكن كان هناك تحديث عاجل لا بد من إطلاقه. “شغلة بسيطة يا أبو عمر، مجرد إصلاح لـ bug صغير”، قال لي أحد المطورين الشباب في الفريق.
بكل ثقة، قمنا بدمج التغيير في الفرع الرئيسي. جاءت اللحظة الحاسمة: تحديث رقم الإصدار. كان الإصدار الحالي v1.4.2. بما أنه مجرد إصلاح خطأ (bug fix)، كان يجب أن يصبح v1.4.3. لكن في عجلة الأمر، وبسبب الإرهاق، قام زميلي بكتابة v1.5.0. خطأ بسيط، أليس كذلك؟
في الدقائق التالية، بدأت هواتفنا بالرنين. العملاء يشتكون من أن التحديث لم يصلهم، وأنظمة الكاش (Caching) عند المستخدمين لم تتعرف على التغيير الطفيف لأن القفزة في الرقم أوهمتها بوجود تغيير ضخم. والأدهى من ذلك، أننا نسينا توثيق هذا التغيير البسيط في ملف CHANGELOG.md (سجل التغييرات). عشنا ساعتين من الجحيم في محاولة لتصحيح الخطأ، وسحب الإصدار، وإعادة إصداره بالرقم الصحيح، وتحديث السجل يدوياً. تلك الليلة، وأنا أحتسي كوب الميرمية، قلت لنفسي: “خلص، بكفي! لازم نلاقي حل لهالـ ‘شغلانة’ اليدوية المقرفة”.
ما هو الجحيم الذي كنا نعيش فيه؟
قصتي ليست فريدة من نوعها. العديد من فرق التطوير، خصوصاً في بداياتها، تعاني من نفس المشاكل عند إدارة إصدارات برمجياتها يدوياً. كانت فوضى حقيقية، ويمكن تلخيصها في عدة نقاط:
- الترقيم العشوائي: كل مطور لديه “رأيه” في رقم الإصدار القادم. هل هو
1.2.10أم1.3.0؟ النقاش بحد ذاته كان يستهلك وقتاً ثميناً. - سجلات التغيير المنسية: كتابة الـ
CHANGELOGكانت مهمة مؤجلة دائماً، وفي معظم الأحيان تُنسى تماماً. وعندما نتذكرها، نحاول جاهدين تصفح تاريخ الـ Git لنفهم ما الذي تغير بالفعل. - الأخطاء البشرية القاتلة: كما حدث في قصتي، خطأ بسيط في كتابة رقم يمكن أن يسبب مشاكل متتالية. الإنسان يخطئ، خصوصاً تحت الضغط.
- الوقت المهدر: عملية الإصدار اليدوية كانت تستغرق من نصف ساعة إلى ساعة كاملة من التركيز الشديد، وهي عملية مملة ومتكررة كان يمكن استغلال وقتها في تطوير ميزات جديدة.
الأساس: فهم الترقيم الدلالي (Semantic Versioning)
قبل أن نتحدث عن الأداة السحرية التي أنقذتنا، يجب أن نفهم المبدأ الذي تقوم عليه. هذا المبدأ هو الترقيم الدلالي (Semantic Versioning) أو اختصاراً SemVer. إنه ليس مجرد أرقام عشوائية، بل هو عقد واتفاق بينك وبين مستخدمي برنامجك.
ما هو الترقيم الدلالي (SemVer)؟
ببساطة، هو معيار لترقيم الإصدارات يتكون من ثلاثة أجزاء: MAJOR.MINOR.PATCH.
- PATCH (تصحيح): يتم زيادة هذا الرقم (مثلاً من
1.2.0إلى1.2.1) عندما تقوم بإصلاح أخطاء (bug fixes) لا تؤثر على طريقة عمل البرنامج الأساسية (backward-compatible). - MINOR (ثانوي): يتم زيادة هذا الرقم (مثلاً من
1.2.1إلى1.3.0) عندما تضيف ميزات جديدة، لكنها لا تكسر التوافق مع الإصدارات السابقة (backward-compatible). - MAJOR (رئيسي): يتم زيادة هذا الرقم (مثلاً من
1.3.0إلى2.0.0) عندما تقوم بتغيير جذري يكسر التوافق مع الإصدارات السابقة (breaking change). هذا يعني أن من يستخدم الإصدار القديم قد يحتاج لتغيير شيء في كوده ليعمل مع الإصدار الجديد.
نصيحة من أبو عمر: فهم وتطبيق SemVer يدوياً هو الخطوة الأولى نحو النضج في عملية التطوير. حتى لو لم تستخدم الأتمتة بعد، ابدأ اليوم بتطبيق هذا المعيار في مشاريعك. إنه يضيف احترافية وثقة لعملك.
المنقذ: ‘الإصدار الدلالي’ (Semantic Release) يدخل المشهد
الترقيم الدلالي رائع، لكن تطبيقه يدوياً لا يزال عرضة للخطأ البشري. هنا يأتي دور semantic-release. إنها ليست مجرد أداة، بل هي منظومة عمل كاملة تندمج مع مسار التكامل والنشر المستمر (CI/CD) الخاص بك لأتمتة عملية الإصدار بأكملها.
ماذا تفعل semantic-release بالزبط؟
- تقرأ رسائل الـ commits في الـ Git الخاص بك.
- بناءً على صيغة الرسائل، تقرر تلقائياً أي جزء من رقم الإصدار يجب زيادته (MAJOR, MINOR, or PATCH).
- تنشئ سجل تغييرات (CHANGELOG.md) محدث وجميل تلقائياً.
- تنشئ Git Tag جديد بالإصدار الجديد.
- تنشر الحزمة البرمجية (Package) إلى مستودع مثل npm أو Docker Hub.
- وأخيراً، تقوم بعمل commit جديد يحتوي على ملف
package.jsonالمحدث وسجل التغييرات.
كل هذا يحدث بضغطة زر (أو بالأحرى، بعد كل عملية git push على الفرع الرئيسي) وبدون أي تدخل بشري.
كيف يعمل هذا السحر؟ رحلة الـ Commit الآلي
قد يبدو الأمر سحرياً، لكنه يعتمد على قاعدة بسيطة جداً: رسائل الـ Commit المنظمة (Structured Commit Messages). تعتمد semantic-release على معيار يسمى Conventional Commits.
1. رسائل الـ Commit المنظمة
بدلاً من كتابة رسائل عشوائية مثل “update code” أو “fixed bug”، يجب أن تتبع صيغة محددة. أهمها:
fix:تُستخدم عندما تصلح خطأ. هذا سيؤدي إلى زيادة رقم الـ PATCH.feat:تُستخدم عندما تضيف ميزة جديدة. هذا سيؤدي إلى زيادة رقم الـ MINOR.
وإذا كان تغييرك يكسر التوافق (Breaking Change)، يمكنك الإشارة إلى ذلك في الـ footer الخاص بالرسالة:
feat: allow provided config object to extend other configs
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
وجود BREAKING CHANGE: في الرسالة سيؤدي إلى زيادة رقم الـ MAJOR.
هناك أنواع أخرى مثل docs:, style:, refactor:, test:, chore: التي لا تؤثر على رقم الإصدار ولكنها تظهر في سجل التغييرات لتنظيمه.
2. التحليل والنشر عبر CI/CD
عندما تقوم بالدفع (push) إلى فرعك الرئيسي (مثل main أو master)، يبدأ مسار الـ CI/CD بالعمل. يقوم بتشغيل أمر semantic-release، الذي يقوم بالخطوات التي ذكرناها سابقاً: يحلل الـ commits منذ آخر إصدار، يحدد الرقم الجديد، يولد سجل التغييرات، وينشر الإصدار.
يلا نطبق: دليل عملي لإعداد Semantic Release
لنقم بإعداد بسيط للمشروع باستخدام GitHub Actions. هذا مثال لمشروع Node.js.
المتطلبات الأساسية
- مشروع Node.js مع ملف
package.json. - مستودع Git على GitHub.
- حساب في npm (إذا كنت ستنشر الحزمة).
الخطوات
1. تثبيت الحزم اللازمة:
npm install --save-dev semantic-release @semantic-release/git @semantic-release/changelog
2. إضافة إعدادات semantic-release:
أنشئ ملفاً باسم .releaserc.json في جذر مشروعك، أو أضف قسماً جديداً في ملف package.json:
// في ملف .releaserc.json
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
["@semantic-release/npm", {
"npmPublish": true
}],
["@semantic-release/git", {
"assets": ["package.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]nn${nextRelease.notes}"
}]
]
}
هذا الإعداد يخبر الأداة بمراقبة فرع main، واستخدام الإضافات (plugins) لتحليل الـ commits، إنشاء سجل التغييرات، نشر الحزمة على npm، وتحديث ملفات package.json و CHANGELOG.md.
3. إعداد مسار العمل (Workflow) في GitHub Actions:
أنشئ ملفاً جديداً في مشروعك بالمسار التالي: .github/workflows/release.yml
name: Release
on:
push:
branches:
- main
jobs:
release:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
ملاحظة هامة: ستحتاج إلى إضافة
NPM_TOKENالخاص بك كـ “secret” في إعدادات مستودعك على GitHub ليتمكن من النشر على npm.
وهكذا، في كل مرة تقوم فيها بدمج تغييراتك في فرع main، سيتم تشغيل هذا المسار، وإذا كانت هناك commits جديدة بصيغة صحيحة، سيتم إنشاء إصدار جديد تلقائياً. وداعاً للكوابيس!
نصائح من أبو عمر (من الكيس 🤓)
- ابدأ بالتدريج: لا تخف من الأتمتة. ابدأ بمشروع جانبي صغير لتفهم الآلية قبل تطبيقها على مشاريعك الكبيرة.
- ثقّف فريقك: أهم جزء في هذه المنظومة هو التزام الفريق بكتابة رسائل commit صحيحة. عقد ورشة عمل صغيرة، اشرح لهم أهمية
featوfix، ووفر لهم دليلاً مرجعياً. - استخدم أدوات مساعدة: أدوات مثل
commitizenتجبر المطورين على كتابة رسائل commit بالصيغة الصحيحة عبر واجهة تفاعلية بسيطة. - احمِ فرعك الرئيسي: استخدم قواعد حماية الفروع (Branch Protection Rules) في GitHub لمنع الدفع المباشر إلى
mainوفرض مراجعة الكود (Pull Requests). هذا يضمن أن الكود الذي يصل إلى الفرع الرئيسي عالي الجودة وجاهز للإصدار.
الخلاصة: من الفوضى إلى النظام بضغطة زر 🚀
التحول من الإصدارات اليدوية إلى نظام مؤتمت باستخدام semantic-release كان واحداً من أفضل القرارات التقنية التي اتخذناها. لقد حررنا من مهمة مملة وعرضة للخطأ، وأعطانا الثقة في كل إصدار نطلقه. لم نعد نقلق بشأن “هل نسيت تحديث سجل التغييرات؟” أو “ما هو رقم الإصدار الصحيح؟”.
نصيحتي الأخيرة لك: لا تدع عملية الإصدار تكون “شغلانة” تثقل كاهل فريقك. استثمر قليلاً من الوقت في إعداد الأتمتة، وركز على ما يهم حقاً: كتابة كود رائع وحل مشاكل حقيقية. صدقني، ستشكرني لاحقاً.