المشروع 14 — CI/CD مع GitHub Actions · المستوى مبتدئ → متوسط · المدة المقدَّرة: ساعة ونصف إلى ساعتين
تتلقى موقعاً ثابتاً صغيراً موصولاً مسبقاً بسبرتَي عمل GitHub Actions تعملان. تستنسخ، تدفع، موقعك على الإنترنت. تغيّر لوناً في
config.json، تعيد الدفع، تتحدّث الصفحة العامة وحدها. تنهي بإصلاح ثلاث سير عمل معيبة توضّح الأخطاء الكلاسيكية في CI/CD.
وُظِّفت للتو مبتدئاً في وكالة ويب. أول عملك: موقع «محفظة» صغير يُستضاف مجاناً. تعليمات الرئيس:
« أريد أن أستطيع تغيير لون الموقع بتعديل ملف واحد، إجراء
git push، وأن يتحدّث وحده. لا FTP، لا خادم أديره. »
كتب كبير الفريق مسبقاً سبرتَي عمل GitHub Actions ومخطوط البناء. عملك:
git clone + دفع + تفعيل Pages).config.json بمعلوماتك وألوانك.push → Pages تعمل بتغيير اللون مرتين على الأقل.هذا المشروع ليس تدريباً تكتب فيه YAML من الصفر. هو تدريب تستلم فيه سلسلة CI/CD قائمة، كما في فريق حقيقي: لا تعيد اختراع سير العمل، أنت تفهمها وتخصّصها وتصلح أعطالها.
هذا المستند مكتفٍ بذاته. لا دورة خارجية تُفتح لإنهائه.
GitHub Actions محرك تنفيذ مدمج في GitHub. عند كل حدث (push، pull_request، مؤقت، زر يدوي)، يشغّل سير عمل موصوفة بـ YAML، على عدّائين (آلات افتراضية Ubuntu / Windows / macOS مجانية للمستودعات العامة).
name: Mon premier workflow # يظهر في الواجهة
on: # متى يُنفَّذ
push:
branches: [main]
jobs: # ماذا يُفعل (>= مهمة واحدة)
construire: # اسم حر للمهمة
runs-on: ubuntu-latest # أين يُنفَّذ (صورة الآلة)
steps: # خطوات المهمة
- uses: actions/checkout@v4 # خطوة "إجراء قابل لإعادة الاستخدام"
- run: echo "Bonjour" # خطوة "أمر صدفة"نوعان من الخطوات:
uses: يستدعي إجراء منشوراً (actions/checkout@v4، actions/setup-python@v5، إلخ).run: ينفّذ أمر صدفة على العدّاء.on: | يُحفَّز عندما… |
|---|---|
push: { branches: [main] } | ندفع commit على main |
pull_request: { branches: [main] } | يفتح أحدهم / يحدّث PR نحو main |
schedule: [{ cron: "0 6 * * *" }] | كل يوم الساعة 06:00 UTC |
workflow_dispatch: | زر يدوي في تبويب Actions |
يمكن لسيرة العمل نفسها أن تملك عدة محفّزات معاً — هذه حالة deployer-pages.yml (دفع + يدوي).
GitHub Pages استضافة ثابتة تُقدَّم مع كل مستودع عام. ننشر فيها HTML/CSS/JS بإحدى الطرق التالية:
gh-pages (الطريقة القديمة)./docs في main (طريقة أخرى).العنوان العام هو https://<utilisateur>.github.io/<nom-du-depot>/ — قابل للوصول من أي متصفح.
للنشر من Actions، تتسلسل deployer-pages.yml ثلاثة إجراءات رسمية:
قيد جوهري: المهمة التي تنشر تحتاج إلى صلاحيات خاصة، وإلا نحصل على Resource not accessible by integration. تصرّح سيرة العمل المقدَّمة بها مسبقاً:
permissions:
contents: read
pages: write
id-token: writeهذا أيضاً عطل المهمة 3 رقم 1 — احفظه.
في كل تنفيذ، يعرض GitHub Actions متغيرات بيئة مفيدة:
| المتغير | المحتوى |
|---|---|
GITHUB_SHA | SHA للـ commit الذي حفّز التشغيل |
GITHUB_REF_NAME | اسم الفرع (main، feature-x، إلخ) |
GITHUB_RUN_NUMBER | عدّاد يزيد في كل تشغيل |
في هذا المشروع، يقرأ outils/build.py هذه المتغيرات الثلاثة لعرضها على الصفحة المنشورة. دليل مرئي أن النشر يأتي فعلاً من Actions وليس من python build.py محلياً.
الحلقة النهائية المتوقعة — يجب أن تعيد إنتاجها مرتين على الأقل:
site/config.json في VS Code.couleur_fond (مثلاً #0f172a ← #7c3aed).git add site/config.json && git commit -m "changement de fond" && git push.https://<vous>.github.io/<depot>/ ← الخلفية بنفسجية.projet14-github-actions-pages-tp/
├── 00-ENONCE.md <- هذا المستند
├── 02-CORRECTION.md <- حلول مفصَّلة (تُقرأ بعد المحاولة)
├── README.md
│
├── site/ <- الموقع (المصدر)
│ ├── config.json <- تعدّله أنت: ألوان، عنوان، مؤلّف
│ └── src/
│ ├── index.html.template <- قالب HTML (علامات {{...}})
│ └── css/
│ └── style.css.template <- قالب CSS (علامات {{...}})
│
├── outils/
│ └── build.py <- مقدَّم — لا يُعدَّل
│
├── .github/ <- سير عمل تعمل مسبقاً
│ └── workflows/
│ ├── deployer-pages.yml <- بناء + نشر على Pages
│ └── verifier-config.yml <- يتحقق من config.json على PR
│
├── casses/ <- 3 سير عمل معيبة (المهمة 3)
│ ├── casse-1-permissions-manquantes.yml
│ ├── casse-2-declencheur-errone.yml
│ └── casse-3-chemin-artefact.yml
│
└── .gitignore <- يتجاهل dist/نقطة مهمة: السيرتان موجودتان مسبقاً في .github/workflows/. لا شيء تكتبه لجزء النشر — فقط تفهم ما يحدث وتخصّص config.json.
outils/build.py — هو العقد بين config.json و HTML/CSS النهائي.dist/: موجود في .gitignore ويُعاد بناؤه في كل تشغيل.git --version).python --version — لاختبار build.py محلياً).projet14-github-actions-pages-tp/ على قرصك.projet14-actions-pages..gitignore (مقدَّمان مسبقاً).من جذر المشروع:
python .\outils\build.pyيجب أن ترى:
OK dist/index.html genere
OK dist/css/style.css genere
--- Substitutions appliquees ---
{{TITRE}} -> Mon premier site pilote par GitHub Actions
{{COULEUR_FOND}} -> #0f172a
...افتح dist/index.html في متصفح: ترى الموقع بخلفية داكنة افتراضية. إن عمل محلياً، سيعمل على Actions.
git init
git branch -M main
git add .
git commit -m "point de depart projet14"
git remote add origin https://github.com/<votre-utilisateur>/projet14-actions-pages.git
git push -u origin mainتبويب Actions لمستودعك ← يظهر تشغيل بعنوان Deployer sur GitHub Pages. انتظر حتى يصير أخضر (~40 ثانية بعد الدفع).
في نهاية المهمة deployer، تعلن رسالة:
Your site is live at https://<vous>.github.io/projet14-actions-pages/انقر ← ترى موقعك منشوراً.
config.json (20 نقطة)افتح site/config.json وعدّل على الأقل:
titre — ضع اسمك أو اسم مشروع وهمي.auteur — اسمك.couleur_fond — بالصيغة #RRGGBB (مثلاً #7c3aed، #dc2626، #0891b2).تحقّق بـ python .\outils\build.py أن البناء ما زال ينجح. صيغة سيئة (rouge، #ff، RGB(255,0,0)) يكتشفها build.py وستحجب سيرة العمل.
قيد: لا تكسر JSON. فاصلة زائدة، قوس مفقود ← سيرة عمل حمراء على GitHub.
هذا قلب المشروع. يجب أن تثبت أن دورة CI/CD تعمل، بتحفيزها مرتين على الأقل:
site/config.json (غيّر couleur_fond مثلاً إلى #dc2626 — أحمر).git add site/config.json && git commit -m "fond en rouge" && git push.https://<vous>.github.io/<votre-depot>/ ← الخلفية حمراء.#16a34a، أزرق #0891b2، بنفسجي #7c3aed، حسب اختيارك).الدليل المطلوب في التقرير:
git log --oneline).النقطة الأساس: على الصفحة العامة، مربّع يعرض Commit SHA : <7 caractères>. يطابق هذا SHA آخر commit على main. هذا الدليل المرئي أن الصفحة تأتي فعلاً من Actions.
يحتوي المجلد casses/ على ثلاث سير عمل معيبة، كل منها توضّح خطأ كلاسيكياً. لكل واحدة:
.github/workflows/ (إلى جانب السير الرسمية)..github/workflows/ (ليس في casses/ — يبقى الأصل معيباً).| الملف | العَرَض المتوقع |
|---|---|
casse-1-permissions-manquantes.yml | تفشل المهمة deployer بـ Resource not accessible by integration |
casse-2-declencheur-errone.yml | لا يُحفَّز أي تشغيل: تُتجاهَل سيرة العمل بصمت |
casse-3-chemin-artefact.yml | تفشل المهمة construire بـ Error: Path does not exist: ./public |
تلميح مهم: يمكنك تشغيل كل سيرة عمل يدوياً عبر Run workflow في تبويب Actions، بلا commit حقيقي في كل اختبار. للسير المعيبة كلها workflow_dispatch: أو يمكنك إضافته مؤقتاً.
الدليل المطلوب لكل عطل: لقطة للتشغيل الأحمر قبل، فرق التصحيح، لقطة للتشغيل الأخضر بعد.
حسب اختيارك، واحد فقط يكفي:
أ) أضف شارة حالة في README.md في جذر مستودعك على GitHub (ليس ذاك الخاص بالتدريب):
تعرض passing (أخضر) أو failing (أحمر) مباشرة.
ب) أنشئ PR اختبار بـ config.json باطل عمداً (مثلاً "couleur_fond": "rouge"). يجب أن تفشل سيرة العمل Verifier la configuration وتحجب الدمج. قدّم لقطة الحجب.
ج) أنشئ سيرة عمل يدوية ثالثة (workflow_dispatch:) ترسل إشعاراً curl إلى webhook (عنوان يُوفَّر عبر Settings → Secrets، أبداً ظاهراً في YAML).
ملف RAPPORT.md في جذر مستودعك، يحتوي:
https://<vous>.github.io/<depot>/).on: push بلا تحديد branches: — متى تُحفَّز؟ هل هذه مشكلة؟id-token: write للنشر على GitHub Pages؟ ما فائدة رمز OIDC؟git push على main في الوقت نفسه؟ ماذا تغيّر كتلة concurrency في السلوك؟API_KEY في Settings → Secrets. يمكن لشفرة سيرة العمل قراءته عبر ${{ secrets.API_KEY }}. هل يمكن طباعته في السجلات؟ لماذا يخفي GitHub بعض القيم؟deployer-pages.yml و verifier-config.yml. هل الثانية بلا فائدة لأن الأولى تقوم بالبناء نفسه؟ برّر لماذا هما متكاملتان.build.py ملفاً historique.log؟ أين ينبغي حفظه؟| العنصر | النقاط |
|---|---|
المهمة 1 — config.json مخصَّص وبناء محلي سليم | 20 |
| المهمة 2 — دليل دورة الدفع → Pages (لونان + لقطات + SHA) | 30 |
| المهمة 3 — 3 أعطال شُخِّصت وأُصلحت | 30 |
| جودة التقرير (هيكل، لقطات، شروح) | 20 |
| مكافأة — المهمة 4 | +10 |
| المجموع | 100 (+10) |
عقوبات:
.github/workflows/ (إذن يتجاهلها GitHub).dist/ مودَع (يجب أن يبقى في .gitignore).outils/build.py.# هيكل مرجعي لسيرة عمل
name: Description humaine
on: # متى
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch: # زر يدوي
permissions: # فقط ما يلزم
contents: read
pages: write
concurrency: # عدم تكديس التشغيلات
group: pages
cancel-in-progress: false
jobs:
mon-job:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Une commande
run: python outils/build.pyالإيماءات الخمس التي تنقذ عند التشخيص:
Re-run failed jobs: يعيد تشغيل الخطوات الفاشلة فقط، أسرع.workflow_dispatch: أضفه إلى سير عملك لإعادة تشغيلها يدوياً بلا commit فارغ.echo "::debug::mon message": يطبع رسالة تصحيح في السجلات.site/config.jsonهذا الملف الوحيد الذي يجب تعديله في الحالة العادية.
{
"titre": "Mon premier site pilote par GitHub Actions",
"sous_titre": "Change une couleur, fais un push, et regarde GitHub Pages se mettre a jour tout seul.",
"couleur_fond": "#0f172a",
"couleur_texte": "#f1f5f9",
"couleur_accent": "#38bdf8",
"auteur": "Etudiant du cours AOA-DEVOPS-101",
"version": "1.0.0"
}قيود الملف:
couleur_fond، couleur_texte، couleur_accent) يجب أن تحترم الصيغة #RRGGBB — وإلا يرفض build.py.site/src/index.html.templateقالب HTML يستبدل build.py كل {{MARQUEUR}} فيه. ستعرض الصفحة:
config.json).config.json).GITHUB_* للعدّاء).الملف الكامل مقدَّم في site/src/index.html.template.
site/src/css/style.css.templateقالب CSS يستخدم {{COULEUR_FOND}} و {{COULEUR_TEXTE}} و {{COULEUR_ACCENT}} في كتلة :root.
مقتطف:
:root {
--fond: {{COULEUR_FOND}};
--texte: {{COULEUR_TEXTE}};
--accent: {{COULEUR_ACCENT}};
}
body {
background: var(--fond);
color: var(--texte);
}.github/workflows/deployer-pages.ymlname: Deployer sur GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
construire:
name: Construire le site
runs-on: ubuntu-latest
steps:
- name: Recuperer le code
uses: actions/checkout@v4
- name: Installer Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Generer dist/ a partir de config.json
run: python outils/build.py
- name: Preparer le dossier a publier
uses: actions/upload-pages-artifact@v3
with:
path: dist
deployer:
name: Publier sur GitHub Pages
needs: construire
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.publication.outputs.page_url }}
steps:
- name: Publier l'artefact
id: publication
uses: actions/deploy-pages@v4قراءة سطراً سطراً: انظر 02-CORRECTION.md ← المهمة 2.
.github/workflows/verifier-config.ymlname: Verifier la configuration
on:
pull_request:
branches: [main]
push:
branches-ignore: [main]
jobs:
linter:
name: Valider config.json
runs-on: ubuntu-latest
steps:
- name: Recuperer le code
uses: actions/checkout@v4
- name: Installer Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Construire en dry-run
run: python outils/build.py
- name: Verifier que dist/ est bien genere
run: |
test -f dist/index.html
test -f dist/css/style.css
echo "OK - site construit sans erreur"يفعل outils/build.py أربعة أشياء:
site/config.json.#RRGGBB (تعبير نمطي).{{MARQUEUR}} في القالبين ويكتب النتيجة في dist/.على GitHub Actions، المتغيرات GITHUB_SHA و GITHUB_REF_NAME و GITHUB_RUN_NUMBER متاحة تلقائياً. محلياً لا توجد ← يعرض المخطوط local-* بدلها. هكذا نميّز صفحة نُشرت فعلاً بواسطة Actions عن صفحة بُنيت محلياً.
casses/casse-1-permissions-manquantes.ymlتعمل سيرة العمل لكن تفشل المهمة deployer بـ:
Error: Resource not accessible by integrationسؤال تطرحه على نفسك: أي قسم كامل ناقص، في أعلى الملف، إلى جانب on:؟
casses/casse-2-declencheur-errone.ymlدفع على main لا يُحفّز أبداً هذه سيرة العمل. لا يظهر أي تشغيل. لا خطأ.
سؤال تطرحه على نفسك: اقرأ المفتاح تحت on:. ينتظر GitHub push، لكن ماذا نرى مكتوباً؟
casses/casse-3-chemin-artefact.ymlينجح build.py ويعرض OK dist/index.html genere. لكن يفشل upload-pages-artifact بـ:
Error: Path does not exist: ./publicسؤال تطرحه على نفسك: أين يكتب build.py الموقع (انظر شفرة Python)؟ أي path: يلزم إذن في سيرة العمل؟
دورة من إعداد د. هيثم رحومة — تطوير ونشر حلول البيانات