التصحيح - المشروع 14: GitHub Actions + GitHub Pages

9 دقيقة

يحتوي هذا المستند على الحلول الكاملة للمهام الأربع، والشروح سطراً سطراً لسير العمل المقدَّمة، وإصلاحات الأعطال الثلاثة، وإجابات أسئلة التفكير.

لا تفتحه إلا بعد محاولة حقيقية. قراءة الحل قبل التجريب تعني تفويت 80% من التعلّم.


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


المهمة 1 — تخصيص config.json

مثال ملف صالح:

json
{
  "titre": "Portfolio de <votre nom>",
  "sous_titre": "Deploiement automatique via GitHub Actions",
  "couleur_fond": "#7c3aed",
  "couleur_texte": "#f8fafc",
  "couleur_accent": "#22d3ee",
  "auteur": "<Votre Nom>",
  "version": "1.0.0"
}

نقاط يجب احترامها:

  1. المفاتيح السبعة (titre، sous_titre، couleur_fond، couleur_texte، couleur_accent، auteur، version) إلزامية.
  2. يجب أن تحترم الألوان الثلاثة الصيغة #RRGGBB (6 أحرف ست عشرية بعد #).
  3. يجب أن يبقى الملف JSON صالحاً: فاصلة بعد كل حقل إلا الأخير، علامات اقتباس مزدوجة في كل مكان.

تحقق محلي:

powershell
python .\outils\build.py

يجب أن يعرض OK dist/index.html genere و OK dist/css/style.css genere. أي خطأ ERREUR : يوقف المخطوط ثم يحجب سيرة عمل GitHub.

لوحة مقترحة لاختبار الحلقة في المهمة 2:

اللونالخلفيةالنصالتمييز
ليل داكن#0f172a#f1f5f9#38bdf8
بنفسجي حيوي#7c3aed#f8fafc#22d3ee
أحمر فاقع#dc2626#fef2f2#facc15
أخضر غابة#16a34a#f0fdf4#fbbf24
أزرق محيط#0891b2#ecfeff#f472b6

المهمة 2 — فهم سير العمل المقدَّمة + إثبات حلقة الدفع → Pages

قراءة سطراً سطراً لـ deployer-pages.yml

yaml
name: Deployer sur GitHub Pages          # الاسم الذي يظهر في واجهة Actions

هذا الاسم تجميلي بحت — يساعد على تمييز سيرة العمل في تبويب Actions للمستودع.

yaml
on:
  push:
    branches: [main]
  workflow_dispatch:

محفّزان:

  • تلقائي ما إن ندفع على main (الحالة العادية).
  • يدوي عبر زر Run workflow في تبويب Actions (مفيد لإعادة النشر بلا commit جديد).
yaml
permissions:
  contents: read
  pages: write
  id-token: write

ثلاثي Pages السحري. بلا هذه الأسطر الثلاثة نحصل على Resource not accessible by integration في المهمة deployer (هذا تماماً عطل المهمة 3 رقم 1):

  • contents: read — يسمح لـ actions/checkout@v4 بقراءة الشفرة.
  • pages: write — يخوّل actions/deploy-pages@v4 النشر.
  • id-token: write — لازم لمصادقة OIDC بين العدّاء وخدمة Pages.
yaml
concurrency:
  group: pages
  cancel-in-progress: false

إن وصل git push اثنان تقريباً في الوقت نفسه، لا يشغّل GitHub Actions نشرين متوازيين على المجموعة نفسها pages — تُنفَّذ التشغيلات واحداً تلو الآخر. cancel-in-progress: false = نترك التشغيل الجاري ينتهي قبل بدء التالي. هذا حاسم لـ Pages: نشران متزامنان ينتجان حالات غير متسقة.

yaml
jobs:
  construire:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4                   # 1. استرجاع الشفرة
      - uses: actions/setup-python@v5               # 2. تثبيت Python
        with:
          python-version: "3.12"
      - run: python outils/build.py                  # 3. توليد dist/
      - uses: actions/upload-pages-artifact@v3       # 4. تجهيز الأثر
        with:
          path: dist                                 #    <-- مهم: المجلد المراد نشره

أربع خطوات متتالية. إن فشلت واحدة، لا تُنفَّذ التالية.

yaml
  deployer:
    needs: construire                                # ينتظر نجاح "construire"
    environment:
      name: github-pages                             # بيئة Pages الرسمية
      url: ${{ steps.publication.outputs.page_url }} # يعرض URL في الواجهة
    steps:
      - uses: actions/deploy-pages@v4
        id: publication

تنتظر المهمة deployer صراحة construire بفضل needs:. في النهاية تعرض واجهة GitHub رابطاً قابلاً للنقر نحو https://<vous>.github.io/<depot>/.

قراءة سطراً سطراً لـ verifier-config.yml

yaml
on:
  pull_request:
    branches: [main]
  push:
    branches-ignore: [main]

تُحفَّز عند PR نحو main وعند دفع نحو أي فرع إلا main. الهدف: التحقق قبل دمج الشفرة. لاحظ أننا نستثني main من push: — وإلا نضاعف العمل مع deployer-pages.yml.

بقية الخطوات مطابقة لـ deployer-pages.yml إلا أننا لا ننشر: نكتفي بالتحقق أن dist/ يُولَّد بلا خطأ.

دليل حلقة الدفع → Pages

تسلسل يُعاد مرتين:

powershell
# --- التكرار 1: خلفية بنفسجية ---
# حرّر site/config.json : "couleur_fond": "#7c3aed"
git add site/config.json
git commit -m "fond violet"
git push
# انتظر ~40 ث -> افتح https://<vous>.github.io/<depot>/ -> خلفية بنفسجية

# --- التكرار 2: خلفية حمراء ---
# حرّر site/config.json : "couleur_fond": "#dc2626"
git add site/config.json
git commit -m "fond rouge"
git push
# انتظر ~40 ث -> افتح العنوان نفسه -> خلفية حمراء

تحققات تُدرَج في التقرير:

  • git log --oneline: يعرض الـ commitين مع SHA القصير.
  • تبويب Actions: التشغيلان لـ Deployer sur GitHub Pages أخضران.
  • لقطتان للصفحة العامة باللونين المختلفين.
  • في كل لقطة، يجب أن يطابق مربّع « Commit SHA » الـ commit الذي حفّز هذا التشغيل.

المهمة 3 — إصلاح الأعطال الثلاثة

العطل 1 — صلاحيات ناقصة

فرق التصحيح في .github/workflows/casse-1-permissions-manquantes.yml:

diff
 name: Casse 1 - Permissions manquantes

 on:
   push:
     branches: [main]
   workflow_dispatch:

+permissions:
+  contents: read
+  pages: write
+  id-token: write
+
 concurrency:
   group: pages
   cancel-in-progress: false

 jobs:
   ...

تشخيص مكتوب يُوضَع في التقرير:

بلا كتلة permissions:، يمنح GitHub سيرة العمل فقط contents: read (قراءة الشفرة) — لا صلاحية النشر على Pages. يطلب الإجراء actions/deploy-pages@v4 صراحة pages: write و id-token: write (رمز OIDC). ومن هنا الخطأ Resource not accessible by integration (403 Forbidden) في المهمة deployer.

قاعدة أمنية: نعطي أقل صلاحيات ممكنة، لكن ليس أقل. مبدأ أدنى امتياز مطبَّق على CI/CD.

العطل 2 — محفّز خاطئ

فرق التصحيح:

diff
 name: Casse 2 - Declencheur errone

 on:
-  pushh:
+  push:
     branches: [main]

تشخيص مكتوب يُوضَع في التقرير:

لا يبلّغ GitHub Actions عن المحفّزات المجهولة في on: — يتجاهلها بصمت. مجرد pushh (بحرفَي h) يجعل لا تشغيل يُحفَّز: لا نرى خطأ، لا نرى شيئاً على الإطلاق في تبويب Actions.

كيف نكتشف العطل؟ ندفع commit، نذهب إلى Actions، نلاحظ أن لا تشغيل يظهر لهذه سيرة العمل. الدليل الوحيد هو الغياب.

تلميح ضد الفخ: Run workflow سريع (زر يدوي، متاح إن وُجد workflow_dispatch:) يتحقق أن سيرة العمل نفسها صالحة نحوياً. يعمل workflow_dispatch: مستقلاً عن push:.

العطل 3 — مسار أثر خاطئ

فرق التصحيح:

diff
       - name: Preparer le dossier a publier
         uses: actions/upload-pages-artifact@v3
         with:
-          path: public
+          path: dist

تشخيص مكتوب يُوضَع في التقرير:

يكتب outils/build.py دائماً في dist/ (الثابت DIST = RACINE / "dist" في شفرة Python). طلبت سيرة العمل path: public — مجلد غير موجود. ومن هنا:

Error: Path does not exist: ./public

يتحقق actions/upload-pages-artifact@v3 من وجود المجلد قبل إنشاء الأثر ويفشل فوراً إن كان المسار خاطئاً. الممارسة الجيدة: دائماً توجيه سيرة العمل نحو المجلد الدقيق الذي ينتجه مخطوط البناء.


المهمة 4 — مكافأة

4.أ — شارة حالة في README.md

في README.md لمستودعك على GitHub (في الجذر، ليس في مجلد projet14-github-actions-pages-tp/ للدورة)، أضف:

markdown
# Mon site portfolio

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

Site publie : https://<vous>.github.io/<depot>/

تصير الشارة خضراء إن نجح آخر تشغيل، حمراء وإلا. تتحدّث وحدها.

4.ب — PR اختبار بـ config.json باطل

powershell
git checkout -b test-config-cassee
# حرّر site/config.json واستبدل "couleur_fond": "#0f172a" بـ "couleur_fond": "rouge"
git add site/config.json
git commit -m "test : couleur invalide"
git push -u origin test-config-cassee
# ثم على GitHub: افتح PR من test-config-cassee نحو main

ستُحفَّز سيرة العمل Verifier la configuration، وسيصرخ build.py:

ERREUR : couleur_fond doit etre au format #RRGGBB (recu : 'rouge')

تعرض PR مربعاً أحمر ويُحجب زر Merge pull request إن ضبطت حماية الفرع (Settings → Branches → Add rule → Require status checks to pass).

4.ج — سيرة عمل بسر

.github/workflows/notifier.yml:

yaml
name: Notifier une URL

on:
  workflow_dispatch:

jobs:
  notifier:
    runs-on: ubuntu-latest
    steps:
      - name: Ping du webhook
        env:
          URL: ${{ secrets.URL_WEBHOOK }}
        run: |
          curl -X POST "$URL" \
            -H "Content-Type: application/json" \
            -d '{"texte":"Un utilisateur a declenche notifier.yml"}'

يُعرَّف السر URL_WEBHOOK في Settings → Secrets and variables → Actions → New repository secret. لن يظهر أبداً في السجلات: يخفي GitHub تلقائياً أي قيمة تطابق سراً.


إجابات أسئلة التفكير

1. on: push بلا branches: — متى يُحفَّز؟

يُحفَّز عند كل دفع على كل الفروع. نادراً ما يكون مرغوباً: git push على فرع ميزة قيد التطوير سيشغّل نشراً لـ Pages في الإنتاج. دائماً قيّد بـ branches: [main] أو قائمة صريحة.

2. لماذا يلزم id-token: write لـ Pages؟ ما فائدة رمز OIDC؟

يستخدم actions/deploy-pages@v4 OIDC (OpenID Connect) لإثبات لخدمة Pages أن الطلب يأتي فعلاً من تشغيل GitHub Actions مخوَّل، بلا كلمة مرور ولا PAT (Personal Access Token). رمز OIDC زائل (عمره محدود بالتشغيل) وموقَّع من GitHub — أكثر أماناً من سر ثابت. يخوّل id-token: write العدّاء توليد هذا الرمز لهذه سيرة العمل.

3. دفعان متزامنان على main — ماذا يفعل concurrency؟

بلا concurrency، يشغّل GitHub سبرتَي عمل متوازيتين. ينهي الأول رفع أثره، يبدأ الثاني رفعه الخاص، وقد تتلقى Pages النشرين بترتيب معكوس ← الترتيب النهائي غير مضمون. مع concurrency: { group: pages, cancel-in-progress: false }، التشغيلان مُسلسلان: ينتظر الثاني انتهاء الأول. لـ cancel-in-progress: true أثر آخر — إلغاء التشغيل الجاري ما إن يبدأ جديد، مفيد لإعادة بناء فائقة السرعة لكنها خطرة لـ Pages.

4. هل يمكن أن يظهر سر في السجلات؟

يخفي GitHub تلقائياً أي قيمة مسجَّلة كسر: تظهر بشكل *** في السجلات. لكن إن حوّلت السر (مثلاً echo "$SECRET" | base64)، لا ينطبق الإخفاء على القيمة المحوَّلة. القاعدة: لا تحوّل سراً أبداً في run:، استخدمه كما هو عبر env: ومرّره مباشرة للأداة (هنا curl).

5. لماذا الإبقاء على سيرتَي العمل deployer-pages.yml و verifier-config.yml؟

  • deployer-pages.yml تنشر على main: هذه سيرة عمل التسليم. لا تدور إلا بعد دمج.
  • verifier-config.yml تتحقق على فروع الميزة وعلى PR: هذه سيرة عمل الوقاية. تمنع وصول config.json مكسور إلى main.

تشكّلان شبكة بطبقين: التحقق يحجب في المنبع، والنشر يسلّم في المصب. هذا نمط CI (تكامل مستمر) + CD (نشر مستمر).

6. ماذا لو أنشأ build.py ملفاً historique.log؟

لا شيء دائم. العدّاء آلة زائلة تُدمَّر في نهاية التشغيل. يختفي الملف مع الآلة. للحفاظ على تاريخ بين التشغيلات، يلزم إيداعه في المستودع (سيئ، يلوّث المستودع)، أو تخزينه في أثر بـ actions/upload-artifact (عمر أقصى 90 يوماً)، أو إرساله إلى خدمة خارجية (S3، Postgres، إلخ).


أخطاء كلاسيكية يُتجنَّب

العَرَضالسبب المحتملالحل
Resource not accessible by integrationكتلة permissions: غائبة أو ناقصةأضف pages: write + id-token: write
لا تشغيل يُحفَّزخطأ إملائي في on: (pushh:، pull_requests:)تحقق من إملاء مفاتيح YAML
Path does not exist: ./xxxالمسار في upload-pages-artifact.path: لا يطابق مجلد خرج build.pyتحقق أن path: = dist
الصفحة المنشورة تعرض المحتوى القديمذاكرة المتصفح المؤقتةCtrl+Shift+R لإعادة تحميل قاسية
أول نشر يستغرق > 5 دقائقانتشار DNS لـ GitHub Pagesطبيعي في المرة الأولى، ~40 ث بعدها
سيرة عمل حمراء في خطوة python outils/build.pyconfig.json باطلشغّل python .\outils\build.py محلياً لرؤية الخطأ الدقيق
تعمل سيرة العمل لكن يعيد URL خطأ 404مصدر Pages ما زال في وضع « Deploy from a branch »انتقل إلى GitHub Actions في Settings → Pages
تشغيلان لـ Pages في الوقت نفسه ينتجان محتوى غير متسقconcurrency: غائبأضف الكتلة concurrency: { group: pages }

كيف يقيّم الأستاذ عملك

يفتح الأستاذ ثلاثة تبويبات:

  1. مستودعك على GitHub: هيكل الملفات، محتوى config.json، وجود .github/workflows/ مع 3 سير عمل مصحَّحة على الأقل (الأصليتان deployer-pages.yml و verifier-config.yml + الأعطال الثلاثة المصلحة).
  2. تبويب Actions لديك: تشغيلان أخضران على الأقل لـ Deployer sur GitHub Pages (المهمة 2)، زائد تشغيلات المهمة 3 (قبل/بعد الإصلاح).
  3. موقعك المنشور: https://<vous>.github.io/<votre-depot>/ — يجب أن يظهر بألوانك المخصَّصة و SHA حديث.

ثم يقرأ RAPPORT.md لديك:

  • يعدّ اللقطات (2 للمهمة 2، 6 للمهمة 3: قبل/بعد × 3 أعطال).
  • يتحقق أن تشخيصات الأعطال مكتوبة بكلماتك، لا منسوخة من هذا المستند.
  • يقيّم جودة الإجابات على أسئلة التفكير الستة.

تقرير نظيف + مستودع منظَّم = علامة كاملة سهلة. الصعوبة ليست تقنية، بل في صرامة الإثبات.


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