الدرس 1 من 10 · المبادئ وGitHub Actions
التكامل والنشر المستمران: المبادئ، خط المعالجة، البيئات
الهدف
فهم لماذا تُؤتمت الفرق اختباراتها وعمليات وضع التطبيقات في الإنتاج، وما الذي تعنيه بالضبط الاختصارات CI وCD، وكيف يُقسَّم خط المعالجة (pipeline)، وما فائدة البيئات (environments). ستكتب بعد ذلك أول pipeline لك… بلغة shell بسيطة، لترى بوضوح ما تُؤتمته أداة مثل GitHub Actions أو GitLab CI.
أهم النقاط
- التكامل المستمر (CI): كل تعديل يُدفع (push) يُبنى ويُختبر تلقائيًا، عدة مرات في اليوم، لاكتشاف المشاكل فورًا.
- التسليم المستمر (continuous delivery): كل نسخة تجتاز الـ CI تكون جاهزة للانتقال إلى الإنتاج؛ ويبقى الوضع في الإنتاج نقرة بشرية.
- النشر المستمر (continuous deployment): كل نسخة تجتاز جميع الفحوص تنتقل تلقائيًا إلى الإنتاج.
- يسلسل الـ pipeline مجموعة من المراحل (lint، الاختبارات، build، النشر)؛ وأول مرحلة تفشل توقف كل شيء.
- تسمح البيئات (التطوير، القبول/staging، الإنتاج) بالتحقق من نسخة قبل أن تصل إلى الزبائن.
- المبدأ المحوري: نبني مرة واحدة فقط وننشر الأثر نفسه (artifact) في كل مكان.
1. المشكلة: « إنه يعمل على جهازي »
في وكالة Atlas Web (الدار البيضاء)، قبل الأتمتة، كان الوضع في الإنتاج يبدو هكذا:
- مساء الجمعة، يدمج يوسف ثلاثة أسابيع من عمل أربعة أشخاص؛
- لم يشغّل أحد جميع الاختبارات: بعضها لم يعد ينجح منذ عشرة أيام؛
- يبني الموقع على جهازه هو (بنسخة Node.js الخاصة به)، ثم ينسخ الملفات عبر FTP إلى الخادم؛
- يتعطّل موقع أحد الزبائن يوم السبت؛ ولا أحد يعرف أي تعديل هو السبب.
يحلّ التكامل والنشر المستمران هذه المشاكل بثلاث أفكار:
| الفكرة | عمليًا |
|---|---|
| الدمج كثيرًا | فروع قصيرة تُدمج كل يوم: تبقى التعارضات صغيرة |
| التحقق من كل شيء تلقائيًا | آلة محايدة تشغّل lint والاختبارات والـ build عند كل push |
| النشر كما نختبر | النشر سكربت مُدار بالنسخ، مطابق في كل مرة، وقابل للتراجع |
2. CI، التسليم المستمر، النشر المستمر
Intégration continue (CI)
┌──────────────────────────────────────────────┐
push → │ récupérer le code → lint → tests → build │ → artefact
└──────────────────────────────────────────────┘
│
Livraison continue : déploiement en recette automatique, │
en production sur décision humaine (bouton) ────────────┤
│
Déploiement continu : tout est automatique jusqu'à la │
production, si tous les contrôles sont verts ───────────┘
| التكامل المستمر | التسليم المستمر | النشر المستمر | |
|---|---|---|---|
| build واختبارات عند كل push | نعم | نعم | نعم |
| أثر (artifact) جاهز للنشر | أحيانًا | دائمًا | دائمًا |
| الوضع في الإنتاج | يدوي، خارج الـ pipeline | نقرة واحدة داخل الـ pipeline | تلقائي |
| المتطلبات | اختبارات مؤتمتة | + عمليات نشر مكتوبة بسكربتات | + ثقة كبيرة في الاختبارات والمراقبة |
عمليًا، كثيرًا ما نقول « CI/CD » للدلالة على المجموع. وكالة مثل Atlas Web تمارس عادةً التسليم المستمر: كل دمج في main ينتقل تلقائيًا إلى بيئة القبول، ويُطلق رئيس المشروع الإنتاج بعد موافقة الزبون.
3. تشريح الـ pipeline
يُقسَّم الـ pipeline إلى مراحل (stages) تحتوي على مهام (jobs). يمكن لمهام المرحلة نفسها أن تعمل بالتوازي؛ أما المراحل فتتوالى بالترتيب.
étape « vérifier » étape « tester » étape « construire » étape « déployer »
┌──────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ lint │ │ tests Node 20│ │ image Docker │ │ recette │
│ format │ ───→ │ tests Node 22│ ───→ │ archive │ ───→ │ production │
└──────────┘ └──────────────┘ └──────────────┘ │ (manuel) │
└──────────────┘
| المرحلة | الدور | أمثلة |
|---|---|---|
| Lint / التنسيق | التحقق من الأسلوب والأخطاء الواضحة، في بضع ثوانٍ | ESLint، Prettier، shellcheck، php -l |
| الاختبارات | إثبات أن الشيفرة تفعل ما يجب | اختبارات وحدوية، اختبارات تكامل، اختبارات شاملة (end-to-end) |
| الأمن | كشف التبعيات الضعيفة والأسرار | npm audit، تحليل الشيفرة، كشف الأسرار |
| Build | إنتاج الأثر المراد نشره | أرشيف، صورة Docker، ملف تنفيذي، تطبيق محمول |
| النشر | تثبيت الأثر على بيئة | SSH نحو خادم VPS، Kubernetes، مزوّد استضافة |
يعمل الـ pipeline على runner (أو المنفّذ): آلة، غالبًا ما تكون آلة افتراضية أو حاوية جديدة، يوفّرها GitHub أو GitLab، أو مثبّتة على خوادمك الخاصة. وبما أنها تنطلق من الصفر في كل مرة، تختفي عبارة « إنه يعمل على جهازي ».
الخصائص الجيدة للـ pipeline
- سريع: أقل من 10 دقائق للنتيجة الرئيسية؛ وبعد ذلك يتوقف المطوّرون عن انتظار النتيجة.
- موثوق: الاختبار الذي يفشل عشوائيًا (flaky) يدمّر الثقة؛ إما أن نصلحه أو نزيله.
- مُدار بالنسخ: الـ pipeline ملف داخل المستودع (
.github/workflows/*.yml،.gitlab-ci.yml)، يُراجَع في pull request مثل الشيفرة. - الأحمر = أولوية: عندما يكون
mainمعطّلًا، يصلحه الفريق قبل المواصلة.
4. البيئات
| البيئة | الدور | من يستعملها | البيانات |
|---|---|---|---|
| التطوير (محلي) | الكتابة والتجربة | المطوّر | وهمية |
| المراجعة (review app) | رؤية pull request على الإنترنت | المراجعون، المصمّمون | وهمية |
| القبول / staging | التحقق من نسخة في ظروف قريبة من الإنتاج | رئيس المشروع، الزبون | نسخة مجهولة الهوية أو مجموعة بيانات اختبار |
| الإنتاج | خدمة الزبائن الحقيقيين | الجميع | حقيقية |
قواعد تجنّب الكوارث:
- الأثر نفسه ينتقل من القبول إلى الإنتاج: لا نعيد البناء بينهما، وإلا فإن ما تحقّقنا منه ليس هو ما ننشره.
- وحده الإعداد (configuration) يتغيّر من بيئة إلى أخرى (عنوان قاعدة البيانات، مفاتيح API)، عبر متغيّرات وأسرار خاصة بكل بيئة.
- الإنتاج محمي: النشر محصور في فروع معيّنة، والموافقة إلزامية، والأسرار غير متاحة للمهام الأخرى.
- نعرف دائمًا كيف نتراجع (rollback) إلى النسخة السابقة (الدرس 3.2).
5. مشهد الأدوات
| الأداة | أين يوجد الـ pipeline | الخصوصية |
|---|---|---|
| GitHub Actions | .github/workflows/*.yml |
مدمجة في GitHub، مع فهرس ضخم من الإجراءات القابلة لإعادة الاستعمال |
| GitLab CI/CD | .gitlab-ci.yml |
مدمجة في GitLab، runners قابلة للتثبيت في كل مكان، واسعة الاستعمال في المؤسسات |
| Jenkins | Jenkinsfile |
تاريخية، ذاتية الاستضافة، قابلة للتوسيع بالإضافات (plugins) |
| أخرى | Azure Pipelines، Bitbucket Pipelines، CircleCI، Drone/Woodpecker… | المنطق نفسه: ملف YAML، مراحل، runners |
يغطي المستويان 1 و2 من هذا التكوين GitHub Actions ثم GitLab CI. بعد فهم المبادئ، يصبح الانتقال من أداة إلى أخرى مسألة مفردات في الغالب.
6. pipeline يدوي، للفهم
قبل أن نعهد بالعمل إلى منصة، لنكتب التسلسل نفسه بلغة shell. هذا بالضبط ما سيفعله الـ runner: أوامر، بالترتيب، مع التوقف عند أول خطأ.
#!/bin/sh
# scripts/pipeline.sh — pipeline محلي لواجهة Atlas
set -e # التوقف عند أول أمر يفشل
echo "== lint"
npm run lint
echo "== tests"
npm test
echo "== build"
mkdir -p dist
tar -czf dist/vitrine.tar.gz package.json src
echo "Artefact prêt : dist/vitrine.tar.gz"
chmod +x scripts/pipeline.sh
./scripts/pipeline.sh
echo "code : $?" # 0 = pipeline أخضر؛ أي قيمة أخرى = أحمر
set -e هو المفتاح: من دونه، سيواصل السكربت بعد اختبار فاشل وينتج أثرًا معطوبًا. وتطبّق منصات الـ CI القاعدة نفسها: الأمر الذي يُرجع رمزًا غير صفري يُفشل المهمة.
يمكنك حتى ربط هذا الـ pipeline بـ Git بواسطة hook من نوع pre-push (تكوين Git، الدرس 3.2): يُرفض الـ push إذا فشل الـ pipeline. هذا مفيد، لكنه يبقى على جهازك ويمكن تجاوزه بـ --no-verify. أما الـ CI الحقيقي فيعمل على الخادم، للجميع، عند كل push.
7. القياس: المؤشرات الأربعة
لمعرفة ما إذا كانت سلسلتك تتحسّن، كثيرًا ما تتابع الفرق أربعة مؤشرات (تُسمّى « DORA »):
| المؤشر | السؤال |
|---|---|
| تواتر النشر | كم عملية وضع في الإنتاج في الأسبوع؟ |
| مهلة الوضع في الإنتاج | كم من الوقت بين commit ونشره على الإنترنت؟ |
| نسبة فشل التغييرات | ما نسبة عمليات النشر التي تتسبّب في حادث؟ |
| زمن الاسترجاع | كم من الوقت للعودة إلى خدمة عادية بعد حادث؟ |
الـ CI/CD الجيد يرفع المؤشرين الأولين ويخفض المؤشرين الأخيرين في الوقت نفسه: نُسلّم أكثر وبأمان أكبر، لأن كل تسليم صغير.
8. من أين نبدأ في فريق قائم
لا داعي لأتمتة كل شيء دفعة واحدة. هذه هي الخطة التي اتبعتها Atlas Web، على مدى شهر تقريبًا:
| الأسبوع | الهدف | النتيجة المرئية |
|---|---|---|
| 1 | تشغيل lint والاختبارات الموجودة عند كل pull request | علامة خضراء أو صليب أحمر على كل PR |
| 2 | حماية main: CI أخضر ومراجعة إلزاميان |
لا مزيد من الـ push المباشر، ولا مزيد من « كان يعمل عندي » |
| 3 | بناء الأثر بواسطة الـ CI، ونشر تلقائي في بيئة القبول | يرى الزبون كل دمج على الإنترنت في اليوم نفسه |
| 4 | نشر في الإنتاج بنقرة واحدة، مع تراجع موثّق | انتهى زمن النسخ عبر FTP مساء الجمعة |
بعد ذلك فقط يأتي التحسين (التخزين المؤقت cache، التوازي)، والأمن المتقدّم (OIDC، التوقيعات، المستوى 4)، وإذا رغب الفريق، النشر المستمر الكامل.
نصيحتان لإنجاح هذا الانتقال:
- قِس قبل وبعد: متوسط مدة الوضع في الإنتاج، وعدد الحوادث في الشهر. هذه الأرقام تُقنع أكثر من أي خطاب.
- حافظ على pipeline بسيط ومقروء: ملف YAML يفهمه الفريق كله أفضل من آلة معقّدة لا يعرف إصلاحها إلا شخص واحد.
أخطاء متكررة
- أتمتة الفوضى: دون اختبارات مؤتمتة، لا يفعل الـ pipeline سوى نشر الأخطاء بسرعة أكبر. ابدأ ببعض الاختبارات على الدوال الحرجة (حساب الأسعار، الدفع).
- إعادة البناء للإنتاج لما تمّ التحقق منه في بيئة القبول: لم يعد الأثر نفسه.
- pipeline مدّته 40 دقيقة: يقلّ دفع المطوّرين للشيفرة ويتجاوزون الـ CI. وزّع العمل بالتوازي، واستعمل التخزين المؤقت (الدرس 1.3).
- تجاهل pipeline أحمر « لأن هذا الاختبار يفشل دائمًا »: أصلحه أو احذفه، لكن لا تعتد على اللون الأحمر.
التطبيق العملي (Lab)
في مشروع Node.js /root/vitrine (الموجود مسبقًا في Git، مع اختبار وlint)، تبني pipeline محليًا:
scripts/pipeline.sh: lint ثم الاختبارات، مع التوقف عند أول خطأ؛- مرحلة build تُنتج الأثر
dist/vitrine.tar.gz، فقط إذا كان كل شيء أخضر؛ - commit للسكربت، دون الأثر؛
- hook من نوع
pre-pushيشغّل الـ pipeline قبل كل push.
تُعطّل عمليات التحقق اختبارًا عمدًا في نسخة من المشروع للتأكد من أن الـ pipeline الخاص بك يصبح أحمر فعلًا.
اختبار قصير
- ما الفرق بين التسليم المستمر والنشر المستمر؟
- لماذا يتوقف الـ pipeline عند أول مرحلة فاشلة؟
- ما هو الـ runner، ولماذا ينطلق من آلة جديدة؟
- لماذا يجب أن ننشر في الإنتاج الأثر نفسه الذي تمّ التحقق منه في بيئة القبول؟
- ماذا يفعل
set -eفي سكربت pipeline؟
الأجوبة
- في التسليم المستمر، تكون كل نسخة متحقَّق منها جاهزة، ويُطلق الوضع في الإنتاج بقرار بشري؛ أما في النشر المستمر، فتنتقل النسخة تلقائيًا إلى الإنتاج إذا نجحت جميع الفحوص.
- حتى لا نبني أبدًا أو ننشر شيفرة نعرف أنها معيبة، ولإعطاء نتيجة سريعة عن سبب الفشل.
- هو الآلة (أو الحاوية) التي تنفّذ مهام الـ pipeline. تنطلق من حالة نظيفة حتى لا تتعلّق النتيجة بما قد يتراكم على جهاز معيّن.
- لأن إعادة البناء قد تُنتج نتيجة مختلفة (تبعيات محدّثة، بيئة أخرى): فما سننشره لن يكون هو ما اختبرناه.
- يوقف السكربت بمجرد أن يُرجع أمر ما رمزًا غير صفري، وينتهي السكربت نفسه بالفشل.
المراجع: GitHub Docs (CC BY 4.0) وGitLab Docs (CC BY-SA 4.0). نص أصلي من MasterLinux.