مهمة GitHub Actions: نشر موقع على GitHub Pages بضغطة واحدة

12 دقيقة

المشروع 14 — CI/CD مع GitHub Actions · المستوى مبتدئ → متوسط · المدة المقدَّرة: ساعة ونصف إلى ساعتين

تتلقى موقعاً ثابتاً صغيراً موصولاً مسبقاً بسبرتَي عمل GitHub Actions تعملان. تستنسخ، تدفع، موقعك على الإنترنت. تغيّر لوناً في config.json، تعيد الدفع، تتحدّث الصفحة العامة وحدها. تنهي بإصلاح ثلاث سير عمل معيبة توضّح الأخطاء الكلاسيكية في CI/CD.


جدول المحتويات


السياق

وُظِّفت للتو مبتدئاً في وكالة ويب. أول عملك: موقع «محفظة» صغير يُستضاف مجاناً. تعليمات الرئيس:

« أريد أن أستطيع تغيير لون الموقع بتعديل ملف واحد، إجراء git push، وأن يتحدّث وحده. لا FTP، لا خادم أديره. »

كتب كبير الفريق مسبقاً سبرتَي عمل GitHub Actions ومخطوط البناء. عملك:

  1. استلام المشروع المسلَّم (git clone + دفع + تفعيل Pages).
  2. تخصيص config.json بمعلوماتك وألوانك.
  3. إثبات أن دورة push → Pages تعمل بتغيير اللون مرتين على الأقل.
  4. إصلاح ثلاث سير عمل معيبة لا تبدأ من الصفر لكنها توضّح أكثر 3 أخطاء شيوعاً في CI/CD.

هذا المشروع ليس تدريباً تكتب فيه YAML من الصفر. هو تدريب تستلم فيه سلسلة CI/CD قائمة، كما في فريق حقيقي: لا تعيد اختراع سير العمل، أنت تفهمها وتخصّصها وتصلح أعطالها.


مفاهيم أساسية قبل البدء

هذا المستند مكتفٍ بذاته. لا دورة خارجية تُفتح لإنهائه.

1. ما هو GitHub Actions؟

GitHub Actions محرك تنفيذ مدمج في GitHub. عند كل حدث (push، pull_request، مؤقت، زر يدوي)، يشغّل سير عمل موصوفة بـ YAML، على عدّائين (آلات افتراضية Ubuntu / Windows / macOS مجانية للمستودعات العامة).

2. تشريح سيرة عمل YAML

yaml
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: ينفّذ أمر صدفة على العدّاء.

3. المحفّزات الأربعة التي يجدر معرفتها

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 (دفع + يدوي).

4. ما هو GitHub Pages؟

GitHub Pages استضافة ثابتة تُقدَّم مع كل مستودع عام. ننشر فيها HTML/CSS/JS بإحدى الطرق التالية:

  • من فرع gh-pages (الطريقة القديمة).
  • من مجلد /docs في main (طريقة أخرى).
  • من سيرة عمل GitHub Actions (الطريقة الحديثة، طريقة هذا المشروع).

العنوان العام هو https://<utilisateur>.github.io/<nom-du-depot>/ — قابل للوصول من أي متصفح.

5. ثلاثي الإجراءات الرسمية لـ Pages

للنشر من Actions، تتسلسل deployer-pages.yml ثلاثة إجراءات رسمية:

قيد جوهري: المهمة التي تنشر تحتاج إلى صلاحيات خاصة، وإلا نحصل على Resource not accessible by integration. تصرّح سيرة العمل المقدَّمة بها مسبقاً:

yaml
permissions:
  contents: read
  pages: write
  id-token: write

هذا أيضاً عطل المهمة 3 رقم 1 — احفظه.

6. متغيرات تلقائية يوفّرها العدّاء

في كل تنفيذ، يعرض GitHub Actions متغيرات بيئة مفيدة:

المتغيرالمحتوى
GITHUB_SHASHA للـ commit الذي حفّز التشغيل
GITHUB_REF_NAMEاسم الفرع (main، feature-x، إلخ)
GITHUB_RUN_NUMBERعدّاد يزيد في كل تشغيل

في هذا المشروع، يقرأ outils/build.py هذه المتغيرات الثلاثة لعرضها على الصفحة المنشورة. دليل مرئي أن النشر يأتي فعلاً من Actions وليس من python build.py محلياً.


المعمارية المستهدفة

الحلقة النهائية المتوقعة — يجب أن تعيد إنتاجها مرتين على الأقل:

  1. افتح site/config.json في VS Code.
  2. غيّر couleur_fond (مثلاً #0f172a#7c3aed).
  3. git add site/config.json && git commit -m "changement de fond" && git push.
  4. اذهب إلى تبويب Actions للمستودع ← راقب سيرة العمل تدور مباشرة (~40 ث).
  5. بعد أن يصير الكل أخضر، افتح 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.


قواعد اللعبة

  1. لا تعدّل outils/build.py — هو العقد بين config.json و HTML/CSS النهائي.
  2. لا تدفع المجلد dist/: موجود في .gitignore ويُعاد بناؤه في كل تشغيل.
  3. كل بيان حسّاس (كلمات مرور، مفاتيح API) يذهب إلى Settings → Secrets and variables → Actions، أبداً في ملف YAML.
  4. كل تغيير لون يجب أن يمر عبر commit — بلا تعديل يدوي على الموقع المنشور.
  5. يجب أن يكون المستودع عاماً (GitHub Pages على مستودعات خاصة يتطلّب خطة مدفوعة).

التحضير

المتطلبات السابقة

  1. Git مثبَّت (git --version).
  2. Python 3.10+ مثبَّت (python --version — لاختبار build.py محلياً).
  3. حساب GitHub.
  4. هذا المجلد projet14-github-actions-pages-tp/ على قرصك.

الخطوة 0 — إنشاء مستودعك على GitHub

  1. على GitHub، انقر New repository.
  2. اسم مقترَح: projet14-actions-pages.
  3. الرؤية: Public (إلزامي لـ GitHub Pages المجاني).
  4. بلا README ولا .gitignore (مقدَّمان مسبقاً).

الخطوة 1 — اختبار البناء محلياً

من جذر المشروع:

powershell
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.

الخطوة 2 — تهيئة المستودع المحلي والدفع

powershell
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

الخطوة 3 — تفعيل GitHub Pages في وضع Actions

  1. على GitHub، افتح المستودع ← SettingsPages.
  2. في Source، اختر GitHub Actionsليس Deploy from a branch).
  3. احفظ.

الخطوة 4 — مراقبة أول نشر

تبويب Actions لمستودعك ← يظهر تشغيل بعنوان Deployer sur GitHub Pages. انتظر حتى يصير أخضر (~40 ثانية بعد الدفع).

في نهاية المهمة deployer، تعلن رسالة:

Your site is live at https://<vous>.github.io/projet14-actions-pages/

انقر ← ترى موقعك منشوراً.


المهام

المهمة 1 — تخصيص 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.


المهمة 2 — إثبات حلقة الدفع → Pages (30 نقطة)

هذا قلب المشروع. يجب أن تثبت أن دورة CI/CD تعمل، بتحفيزها مرتين على الأقل:

  1. عدّل site/config.json (غيّر couleur_fond مثلاً إلى #dc2626 — أحمر).
  2. git add site/config.json && git commit -m "fond en rouge" && git push.
  3. انتظر حتى تصير سيرة العمل Deployer sur GitHub Pages خضراء في تبويب Actions (~40 ث).
  4. افتح https://<vous>.github.io/<votre-depot>/الخلفية حمراء.
  5. أعد العملية بلون آخر (أخضر #16a34a، أزرق #0891b2، بنفسجي #7c3aed، حسب اختيارك).

الدليل المطلوب في التقرير:

  • لقطتان للصفحة العامة بـ لونين مختلفين.
  • SHA الـ commitين المقابلين (مخرجات git log --oneline).
  • لقطة لتبويب Actions تُظهر التشغيلين الأخضرين.

النقطة الأساس: على الصفحة العامة، مربّع يعرض Commit SHA : <7 caractères>. يطابق هذا SHA آخر commit على main. هذا الدليل المرئي أن الصفحة تأتي فعلاً من Actions.


المهمة 3 — تحقيق: إصلاح 3 سير عمل معيبة (30 نقطة)

يحتوي المجلد casses/ على ثلاث سير عمل معيبة، كل منها توضّح خطأ كلاسيكياً. لكل واحدة:

  1. انسخ الملف إلى .github/workflows/ (إلى جانب السير الرسمية).
  2. ادفع — لاحظ أن سيرة العمل لا تتصرف كما يُتوقَّع (فشل أو غياب تحفيز).
  3. شخّص بقراءة السجلات (تبويب Actions ← تشغيل ← خطوة).
  4. أصلِح في نسخة .github/workflows/ (ليس في casses/ — يبقى الأصل معيباً).
  5. ادفع مجدداً — يجب أن تصير سيرة العمل خضراء الآن.
الملفالعَرَض المتوقع
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: أو يمكنك إضافته مؤقتاً.

الدليل المطلوب لكل عطل: لقطة للتشغيل الأحمر قبل، فرق التصحيح، لقطة للتشغيل الأخضر بعد.


المهمة 4 — مكافأة (+10 نقاط)

حسب اختيارك، واحد فقط يكفي:

  • أ) أضف شارة حالة في README.md في جذر مستودعك على GitHub (ليس ذاك الخاص بالتدريب):

    markdown
    ![Deploy](https://github.com/<vous>/<depot>/actions/workflows/deployer-pages.yml/badge.svg)

    تعرض passing (أخضر) أو failing (أحمر) مباشرة.

  • ب) أنشئ PR اختبار بـ config.json باطل عمداً (مثلاً "couleur_fond": "rouge"). يجب أن تفشل سيرة العمل Verifier la configuration وتحجب الدمج. قدّم لقطة الحجب.

  • ج) أنشئ سيرة عمل يدوية ثالثة (workflow_dispatch:) ترسل إشعاراً curl إلى webhook (عنوان يُوفَّر عبر Settings → Secrets، أبداً ظاهراً في YAML).


المخرجات

ملف RAPPORT.md في جذر مستودعك، يحتوي:

  1. العنوان العام لموقعك (https://<vous>.github.io/<depot>/).
  2. لـ المهمة 2: لقطتان للصفحة بلونين مختلفين + SHA الاثنين + لقطة لتبويب Actions بالتشغيلين الأخضرين.
  3. لـ المهمة 3: لكل عطل، لقطة للتشغيل الأحمر + تشخيص مكتوب + فرق التصحيح + لقطة للتشغيل الأخضر.
  4. إجاباتك على أسئلة التفكير.
  5. (مكافأة) دليل المهمة 4 إن أنجزتها.

أسئلة للتفكير

  1. سيرة عمل تحتوي on: push بلا تحديد branches: — متى تُحفَّز؟ هل هذه مشكلة؟
  2. لماذا يلزم id-token: write للنشر على GitHub Pages؟ ما فائدة رمز OIDC؟
  3. ماذا يحدث إن دفع زميلان git push على main في الوقت نفسه؟ ماذا تغيّر كتلة concurrency في السلوك؟
  4. تضيف سراً API_KEY في Settings → Secrets. يمكن لشفرة سيرة العمل قراءته عبر ${{ secrets.API_KEY }}. هل يمكن طباعته في السجلات؟ لماذا يخفي GitHub بعض القيم؟
  5. لديك سيرتا عمل: deployer-pages.yml و verifier-config.yml. هل الثانية بلا فائدة لأن الأولى تقوم بالبناء نفسه؟ برّر لماذا هما متكاملتان.
  6. عدّاء GitHub Actions آلة زائلة: لا شيء يثبت بين تشغيلين. ما أثر ذلك إن أنشأ build.py ملفاً historique.log؟ أين ينبغي حفظه؟

السلم

العنصرالنقاط
المهمة 1 — config.json مخصَّص وبناء محلي سليم20
المهمة 2 — دليل دورة الدفع → Pages (لونان + لقطات + SHA)30
المهمة 3 — 3 أعطال شُخِّصت وأُصلحت30
جودة التقرير (هيكل، لقطات، شروح)20
مكافأة — المهمة 4+10
المجموع100 (+10)

عقوبات:

  • −10 لكل سيرة عمل موجودة خارج .github/workflows/ (إذن يتجاهلها GitHub).
  • −15 لأي سر ظاهر في ملف YAML مودَع.
  • −5 لكل مجلد dist/ مودَع (يجب أن يبقى في .gitignore).
  • −10 لأي تعديل لـ outils/build.py.

صندوق أدوات GitHub Actions

yaml
# هيكل مرجعي لسيرة عمل
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

الإيماءات الخمس التي تنقذ عند التشخيص:

  1. تبويب Actions ← تشغيل أحمر ← خطوة فاشلة: رسالة الخطأ في الأعلى، بالأحمر. قراءتها حرفياً تتجنّب 90% من الأخطاء.
  2. Re-run failed jobs: يعيد تشغيل الخطوات الفاشلة فقط، أسرع.
  3. workflow_dispatch: أضفه إلى سير عملك لإعادة تشغيلها يدوياً بلا commit فارغ.
  4. echo "::debug::mon message": يطبع رسالة تصحيح في السجلات.
  5. اقرأ السجلات حتى النهاية: أحياناً الخطأ الدال في الوسط، لا في النهاية.


الملحق أ — الموقع الثابت

الملف: site/config.json

هذا الملف الوحيد الذي يجب تعديله في الحالة العادية.

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.
  • JSON صالح: علامات اقتباس مزدوجة، فاصلة بعد كل حقل إلا الأخير.

الملف: site/src/index.html.template

قالب HTML يستبدل build.py كل {{MARQUEUR}} فيه. ستعرض الصفحة:

  • العنوان والعنوان الفرعي (من config.json).
  • المؤلّف والإصدار (من config.json).
  • SHA للـ commit ورقم التشغيل والفرع (من متغيرات GITHUB_* للعدّاء).
  • تاريخ البناء.

الملف الكامل مقدَّم في site/src/index.html.template.

الملف: site/src/css/style.css.template

قالب CSS يستخدم {{COULEUR_FOND}} و {{COULEUR_TEXTE}} و {{COULEUR_ACCENT}} في كتلة :root.

مقتطف:

css
:root {
  --fond: {{COULEUR_FOND}};
  --texte: {{COULEUR_TEXTE}};
  --accent: {{COULEUR_ACCENT}};
}

body {
  background: var(--fond);
  color: var(--texte);
}


الملحق ب — سير العمل المقدَّمة

الملف: .github/workflows/deployer-pages.yml

yaml
name: 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.yml

yaml
name: 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 أربعة أشياء:

  1. يقرأ site/config.json.
  2. يتحقق من وجود المفاتيح السبعة المتوقعة.
  3. يتحقق أن الألوان الثلاثة بالصيغة #RRGGBB (تعبير نمطي).
  4. يستبدل كل {{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: يلزم إذن في سيرة العمل؟


دورة من إعداد د. هيثم رحومة — تطوير ونشر حلول البيانات