الجزء 11: التكامل والتسليم المستمر
البدء مع GitHub Actions
قبل أن نبدأ اللعب مع GitHub Actions، لنلقِ نظرة على ماهيتها وكيف تعمل.
تعمل GitHub Actions وفق أسيار العمل (workflows). سير العمل هو سلسلة من المهام (jobs) التي تُنفَّذ عندما يحفّزها حدث معيّن. تحتوي كل مهمة على مجموعتها الخاصة من التعليمات التي تخبر GitHub Actions بما ينبغي فعله.
يبدو التنفيذ النموذجي لسير العمل هكذا:
- يقع الحدث المُحفِّز (على سبيل المثال، دفع (push) إلى الفرع main).
- يُنفَّذ سير العمل المرتبط بذلك المُحفِّز.
- التنظيف
الاحتياجات الأساسية
بشكل عام، لكي تعمل CI على مستودع، نحتاج إلى بضعة أمور:
- مستودع (بديهي)
- تعريف لما تحتاج CI إلى فعله: يمكن أن يكون ذلك في صورة ملف معيّن داخل المستودع أو يمكن تعريفه في نظام CI نفسه
- أن تكون CI على علم بوجود المستودع (وملف الإعدادات داخله)
- أن تكون CI قادرة على الوصول إلى المستودع
- أن تملك CI الأذونات اللازمة لتنفيذ الإجراءات التي يُفترض أن تقوم بها: على سبيل المثال، إذا احتاجت CI إلى النشر في بيئة إنتاج، فإنها تحتاج إلى بيانات اعتماد لتلك البيئة.
هذا هو النموذج التقليدي على الأقل، وسنرى بعد قليل كيف تختصر GitHub Actions بعض هذه الخطوات، أو بالأحرى تجعلها بحيث لا داعي للقلق بشأنها!
لـGitHub Actions ميزة كبيرة على الحلول المستضافة ذاتياً: فالمستودع مستضاف لدى مزوّد CI. بعبارة أخرى، يوفّر GitHub كلاً من المستودع ومنصة CI. وهذا يعني أننا إذا فعّلنا Actions لمستودع، فإن GitHub يعلم مسبقاً بأن لدينا أسيار عمل معرّفة وكيف تبدو تلك التعريفات.
2. المشروع المثال
البدء مع سير العمل
المكوّن الجوهري لإنشاء خطوط أنابيب CI/CD باستخدام GitHub Actions هو ما يُسمى سير العمل (Workflow). أسيار العمل هي تدفقات عملية يمكنك إعدادها في مستودعك لتشغيل مهام آلية مثل البناء والاختبار وفحص الشيفرة وإصدار الإصدارات والنشر، على سبيل المثال لا الحصر! يبدو التسلسل الهرمي لسير العمل كما يلي:
سير العمل
- مهمة
- مهمة
يجب أن يحدد كل سير عمل مهمة (Job) واحدة على الأقل، تحتوي على مجموعة من الخطوات (Steps) لتنفيذ مهام فردية. تُشغَّل المهام بالتوازي، أما الخطوات في كل مهمة فتُنفَّذ بالتتابع.
تتنوع الخطوات بين تشغيل أمر مخصص واستخدام إجراءات معرّفة مسبقاً، ومن هنا جاء اسم GitHub Actions. يمكنك إنشاء إجراءات مخصصة أو استخدام أي إجراءات ينشرها المجتمع، وهي كثيرة، لكن لنعد إلى ذلك لاحقاً!
لكي يتعرف GitHub على أسيار عملك، يجب تحديدها في مجلد .github/workflows داخل مستودعك. كل سير عمل هو ملف منفصل بذاته، ويجب إعداده باستخدام لغة تسلسل البيانات YAML.
YAML اختصار تكراري لعبارة "YAML Ain't Markup Language". وكما يوحي الاسم، هدفها أن تكون مقروءة للبشر، وهي تُستخدم عادةً في ملفات الإعدادات.
لاحظ أن المسافات البادئة مهمة في YAML. يمكنك تعلم المزيد عن الصياغة هنا.
يحتوي سير العمل الأساسي على ثلاثة عناصر في مستند YAML. هذه العناصر الثلاثة هي:
- name: أجل، لقد خمّنتها، اسم سير العمل
- (on) triggers: الأحداث التي تحفّز تنفيذ سير العمل
- jobs: المهام المنفصلة التي سينفذها سير العمل (قد يحتوي سير العمل الأساسي على مهمة واحدة فقط).
يبدو تعريف سير العمل البسيط هكذا:
name: Hello World!
on:
push:
branches:
- main
jobs:
hello_world_job:
runs-on: ubuntu-latest
steps:
- name: Say hello
run: |
echo "Hello World!"
توجد مهمة واحدة باسم hello_world_job، وستُشغَّل في بيئة افتراضية بنظام Ubuntu 24.04. للمهمة خطوة واحدة فقط باسم "Say hello"، وستنفّذ الأمر echo "Hello World!" في صدفة النظام.
قد تتساءل إذن: متى يحفّز GitHub بدء سير العمل؟ هناك الكثير من الخيارات للاختيار منها، لكن بشكل عام يمكنك إعداد سير العمل ليبدأ عند:
- وقوع حدث على GitHub مثل دفع (push) commit إلى مستودع أو إنشاء issue أو pull request
- وقوع حدث مجدول، يُحدد باستخدام صياغة cron
- وقوع حدث خارجي، على سبيل المثال تنفيذ أمر في تطبيق خارجي مثل تطبيق المراسلة Slack أو Discord
لمعرفة المزيد عن الأحداث التي يمكن استخدامها لتحفيز سير العمل، يُرجى الرجوع إلى توثيق GitHub Actions.
3. مرحباً بالعالم!
4. التاريخ ومحتويات المجلد
إعداد خطوات الفحص والاختبار والبناء
بعد إكمال التمارين الأولى، سيكون لديك سير عمل بسيط لكنه عديم الفائدة تقريباً. لنجعل سير العمل يقوم بشيء مفيد.
لنُنفّذ إجراءً (action) في GitHub Actions يقوم بفحص الشيفرة. إذا لم تنجح الفحوصات، سيعرض GitHub Actions حالة حمراء.
في البداية، يبدو سير العمل الذي سنحفظه في الملف pipeline.yml هكذا:
name: Deployment pipeline
on:
push:
branches:
- main
jobs:
قبل أن نتمكن من تشغيل أمر لفحص الشيفرة، علينا تنفيذ بضعة إجراءات لتهيئة بيئة المهمة.
تهيئة البيئة
تُعدّ تهيئة البيئة خطوة مهمة عند إعداد خط أنابيب. سنستخدم بيئة افتراضية ubuntu-latest لأنها مطابقة لإصدار Ubuntu (وهو 24.04) الذي سنشغّله في الإنتاج.
من المهم محاكاة البيئة نفسها في CI كما في الإنتاج بأقصى قدر ممكن من الدقة، لتجنّب حالات تعمل فيها الشيفرة نفسها بشكل مختلف في CI وفي الإنتاج، وهو ما يُبطل الغرض من استخدام CI فعلياً.
بعد ذلك، نُدرج الخطوات في مهمة "build" التي ستحتاج CI إلى تنفيذها. كما لاحظنا في التمرين الأخير، لا تحتوي البيئة الافتراضية افتراضياً على أي شيفرة، لذا نحتاج إلى سحب الشيفرة من المستودع.
هذه خطوة سهلة:
name: Deployment pipeline
on:
push:
branches:
- main
jobs:
simple_deployment_pipeline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
تُخبر الكلمة المفتاحية uses سير العمل بتشغيل إجراء معيّن. والإجراء قطعة شيفرة قابلة لإعادة الاستخدام، مثل دالة. يمكن تعريف الإجراءات في مستودعك في ملف منفصل، أو يمكنك استخدام الإجراءات المتاحة في المستودعات العامة.
هنا نستخدم إجراءً عاماً هو actions/checkout، ونحدد إصداراً (@v6) لتجنّب التغييرات الكاسرة المحتملة إذا حُدِّث الإجراء. يفعل إجراء checkout ما يوحي به الاسم: فهو يسحب الشيفرة المصدرية للمشروع من Git.
ثانياً، بما أن التطبيق مكتوب بـJavaScript، يجب تهيئة Node.js لتتمكن من استخدام الأوامر المحددة في package.json. لتهيئة Node.js يمكن استخدام إجراء actions/setup-node. اختير الإصدار 24 من Node لأنه الإصدار الذي يستخدمه التطبيق في بيئة الإنتاج.
# لم يعد يُعرض الاسم والمُحفِّز...
jobs:
simple_deployment_pipeline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
كما نرى، تُستخدم الكلمة المفتاحية with لإعطاء "معامل" للإجراء. وهنا يحدد المعامل إصدار Node.js الذي نريد استخدامه.
أخيراً، يجب تثبيت اعتماديات التطبيق. وكما على جهازك الخاص، ننفّذ npm install. ينبغي أن تبدو الخطوات في المهمة الآن هكذا:
jobs:
simple_deployment_pipeline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
- name: Install dependencies
run: npm install
الآن ينبغي أن تكون البيئة جاهزة بالكامل كي تنفّذ المهمة مهامها المهمة.
فحص الشيفرة
بعد تهيئة البيئة، يمكننا تشغيل جميع السكربتات من package.json كما نفعل على جهازنا الخاص. لفحص الشيفرة، كل ما عليك فعله هو إضافة خطوة لتشغيل الأمر npm run eslint.
jobs:
simple_deployment_pipeline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: '24'
- name: Install dependencies
run: npm install
- name: Check style
run: npm run eslint
لاحظ أن name الخطوة اختياري؛ فإذا عرّفت خطوة كما يلي
- run: npm run eslint
يُستخدم الأمر الذي يُشغَّل كاسم افتراضي.
5. سير عمل فحص الشيفرة
6. أصلح الشيفرة
7. البناء والاختبار
8. العودة إلى اللون الأخضر
9. اختبارات من طرف إلى طرف بسيطة