إعداد التكامل المستمر


إعداد التكامل المستمر (CI) لمسارك مهم جدًا، لأنه يساعد في اكتشاف الأخطاء.

GitHub Actions

تستخدم مستودعات Exercism (بما فيها مستودعات المسارات) GitHub Actions لتشغيل التكامل المستمر. تستند GitHub Actions إلى سير العمل، وهي تُعرّف سكربتات تُشغَّل تلقائيًا كلما وقع حدث معيّن (مثل دفع commit). لمزيد من المعلومات عن سير عمل GitHub Actions، اطّلع على وثائق سير العمل.

سير العمل المثبّتة مسبقًا

تأتي المسارات مزوّدة مسبقًا بعدد من سير العمل، ومعظمها لا ينبغي أن تعدّله (وتُسمّى سير العمل المشتركة). لكن هناك سير عمل واحد ينبغي أن تغيّره، وهو سير العمل test.yml.

سير عمل الاختبار

هدف سير العمل test.yml هو التحقق من أن تمارين المسار في حالة سليمة. سير العمل مهيّأ ليعمل تلقائيًا (بمصطلحات GitHub Actions: يُطلَق) عند دفع إلى فرع main أو إلى فرع طلب سحب.

سير العمل نفسه لا ينبغي أن يفعل الكثير، عدا ما يلي:

  • سحب الكود (مُنفَّذ بالفعل)
  • تثبيت التبعيات (مثل تثبيت الحزم، اختياري)
  • تثبيت الأدوات (مثل تثبيت SDK، اختياري)
  • تشغيل سكربت التحقق من التمارين (مُنفَّذ بالفعل)

تنفيذ سكربت التحقق من التمارين

كما ذُكر، تُتحقق التمارين عبر سكربت، هو سكربت bin/verify-exercises (بلغة bash). هذا السكربت شبه جاهز، وهو يقوم بما يلي:

  • يمرّ على جميع مجلدات التمارين
  • لكل مجلد تمرين، يقوم بعد ذلك بـ:
    • نسخ حل المثال/النموذج إلى ملفات الحل (الهيكل) (مُنفَّذ بالفعل)
    • استدعاء الدالة unskip_tests التي يمكنك فيها إلغاء تخطي الاختبارات في ملفات الاختبار (اختياري)
    • استدعاء الدالة run_tests التي ينبغي أن تشغّل فيها الاختبارات (مطلوب)

دالتا run_tests وunskip_tests هما الشيء الوحيد الذي تحتاج إلى تنفيذه.

إلغاء تخطي الاختبارات

إذا كان مسارك يدعم تخطي الاختبارات، علينا التأكد من عدم تخطي أي اختبار عند التحقق من حل المثال/النموذج لأي تمرين. عمومًا، هناك أسلوبان تدعم بهما المسارات "إلغاء التخطي":

  1. إزالة التعليقات/الكود/النص من ملفات الاختبار. على سبيل المثال، تغيير test.skip إلى test.
  2. توفير متغير بيئة. على سبيل المثال، ضبط SKIP_TESTS=false.

إزالة التعليقات/الكود/النص من ملفات الاختبار

إذا كان تخطي الاختبارات قائمًا على الملفات (الخيار الأول أعلاه)، عدّل الدالة unskip_tests لتعديل ملفات الاختبار (الكود الحالي يتعامل بالفعل مع المرور على ملفات الاختبار).

Note

تعمل الدالة unskip_test على نسخة من مجلد التمرين، فلا تتردد في تعديل الملفات كما تراه مناسبًا.

مثال

يستخدم ملف bin/verify-exercises في مسار Arturo الأمر sed لإلغاء تخطي الاختبارات داخل ملفات الاختبار:

unskip_tests() {
    jq -r '.files.test[]' .meta/config.json | while read -r test_file; do
        sed -i 's/test.skip/test/g' "${test_file}"
    done
}

توفير متغير بيئة

Caution

إذا كان إلغاء تخطي الاختبارات يتطلب ضبط متغير بيئة، فتأكد من ضبطه داخل الدالة run_tests.

تشغيل الاختبارات

الدالة run_tests مسؤولة عن تشغيل اختبارات التمرين. عند استدعاء الدالة، ستكون ملفات المثال/النموذج قد نُسخت بالفعل إلى ملفات الحل (الهيكل)، لذا تحتاج فقط إلى استدعاء الأمر الصحيح لتشغيل الاختبارات.

يجب أن تُرجع الدالة صفرًا كرمز خروج إذا نجحت جميع الاختبارات، وإلا فعليها إرجاع رمز خروج غير صفري.

Note

تعمل الدالة run_tests على نسخة من مجلد التمرين، فلا تتردد في تعديل الملفات كما تراه مناسبًا.

الخيار الأول: استخدام أدوات اللغة

الخيار الافتراضي لسكربت التحقق من التمارين هو استخدام أدوات اللغة (SDK/ملف تنفيذي/إلخ)، وهو ما تستخدمه معظم المسارات. سيكون لكل مسار أسلوبه الخاص في تشغيل الاختبارات، لكنه عادة أمر واحد فقط.

مثال

يعدّل ملف bin/verify-exercises في مسار Arturo الدالة run_tests ليستدعي ببساطة الأمر arturo على ملف الاختبار:

run_tests() {
    arturo tester.art
}

الخيار الثاني: استخدام صورة Docker لمُشغّل الاختبارات

الخيار الثاني هو التحقق من التمارين بتشغيل مُشغّل الاختبارات الخاص بالمسار. وهذا بالطبع يعتمد على أن يكون لدى المسار مُشغّل اختبارات يعمل.

إذا لم يكن لدى مسارك مُشغّل اختبارات بعد، فيمكنك إما:

  • بناء مُشغّل اختبارات يعمل، أو
  • استخدام الخيار الأول والاعتماد مباشرة على أدوات اللغة

يجب إجراء التعديلات التالية على سكربت bin/verify-exercises file الافتراضي:

  1. التأكد من توفر الأمر docker
  2. سحب (تنزيل) صورة Docker الخاصة بمُشغّل الاختبارات
  3. استخدام docker run لتشغيل صورة Docker الخاصة بمُشغّل الاختبارات على كل تمرين
  4. استخدام jq للتحقق من أن ملف results.json الذي تُرجعه حاوية Docker يشير إلى نجاح جميع الاختبارات
  5. إزالة الدالة unskip_test والاستدعاء الخاص بها
Note

الميزة الأساسية لهذا الأسلوب أنه يحاكي على أفضل وجه الكيفية التي تُشغَّل بها الاختبارات في الإنتاج (على الموقع). وبهذا الأسلوب، تقل احتمالية فشل أشياء في الإنتاج كانت قد نجحت في التكامل المستمر. أما العيب في هذا الأسلوب فهو أنه عادة أبطأ، بسبب الحاجة إلى سحب صورة Docker وما يضيفه Docker من عبء.

مثال

يضيف ملف bin/verify-exercises file في مسار Unison التحقق من أن الأمر docker مثبّت أيضًا:

required_tool docker

ثم يسحب صورة مُشغّل الاختبارات الخاص بالمسار:

docker pull exercism/unison-test-runner

ثم يعدّل الدالة run_tests لتستخدم docker run لتشغيل مُشغّل الاختبارات على التمرين الحالي (الموجود في مجلد العمل)، يتبع ذلك أمر jq للتحقق من الحالة الصحيحة:

run_tests() {
    local slug

    slug="${1}"

    docker run \
        --rm \
        --network none \
        --mount type=bind,src="${PWD}",dst=/solution \
        --mount type=bind,src="${PWD}",dst=/output \
        --tmpfs /tmp:rw \
        exercism/unison-test-runner "${slug}" "/solution" "/output"
    jq -e '.status == "pass"' "${PWD}/results.json" >/dev/null 2>&1
}

أخيرًا، نحتاج إلى تعديل استدعاء الأمر run_tests، لأنه يتطلب الآن المعرّف:

run_tests "${slug}"

تنفيذ سير عمل الاختبار

الآن وقد انتهى سكربت verify-exercises، حان وقت إنهاء سير العمل test.yml. تعتمد كيفية القيام بذلك على الخيار الذي اخترته لتنفيذ سكربت verify-exercises.

الخيار الأول: استخدام أدوات اللغة

إذا كان سكربت verify-exercises يستخدم أدوات اللغة مباشرة، فسيحتاج سير عمل الاختبار إلى تثبيت:

  • تبعيات أدوات اللغة، مثل openssh أو مترجم C/C++.
  • أدوات اللغة، مثل SDK أو ملف تنفيذي. إذا كان تثبيت أدوات اللغة لا يضيف الملف التنفيذي/الملفات التنفيذية المثبّتة إلى المسار، فتأكد من إضافته إلى مسار نظام GitHub Actions.

وبعد الانتهاء من ذلك، ينبغي أن يعمل verify-exercises كما هو متوقع، وتكون قد أعددت التكامل المستمر بنجاح!

للاستطلاع على مثال، اطّلع على سير العمل test.yml في مسار Arturo:

name: Test

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

jobs:
  ci:
    runs-on: ubuntu-22.04

    steps:
      - name: Checkout repository
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install libgtk-3-dev libwebkit2gtk-4.0-dev libmpfr-dev

      - name: Install Arturo
        run: bin/install-arturo
        env:
          GH_TOKEN: ${{ github.token }}

      - name: Verify all exercises
        run: bin/verify-exercises

الخيار الثاني: استخدام صورة Docker لمُشغّل الاختبارات

الخيار الثاني هو التحقق من التمارين بتشغيل مُشغّل الاختبارات الخاص بالمسار. ويتطلب هذا الخيار تحقق أمرين:

  1. أن يكون لدى المسار مُشغّل اختبارات يعمل
  2. أن يستخدم سكربت verify-exercises صورة Docker الخاصة بمُشغّل الاختبارات لتشغيل اختبارات التمرين

إذا لم يكن لدى مسارك مُشغّل اختبارات بعد، فيمكنك إما:

  • بناء مُشغّل اختبارات يعمل، أو
  • استخدام الخيار الأول والاعتماد مباشرة على أدوات اللغة

لهذا الأسلوب ميزتان:

  1. لا تحتاج إلى تثبيت أي تبعيات/أدوات داخل سير عمل الاختبار (لأنها ستكون مثبّتة داخل صورة Docker)
  2. يحاكي هذا الأسلوب على أفضل وجه الكيفية التي تُشغَّل بها الاختبارات في الإنتاج (على الموقع)، ما يقلل احتمال ظهور مشكلات في الإنتاج.

العيب الأساسي أنه غالبًا أبطأ، بسبب الحاجة إلى سحب صورة Docker وما يضيفه Docker من عبء.

هناك عدة أساليب لسحب صورة Docker الخاصة بمُشغّل الاختبارات:

  1. تنزيل الصورة داخل ملف verify-exercises. هذا هو الأسلوب الذي يتبعه مسار Unison.
  2. تنزيل الصورة داخل سير العمل. هذا هو الأسلوب الذي يتبعه مسار Standard ML.
  3. بناء الصورة داخل سير العمل. هذا هو الأسلوب الذي يتبعه مسار 8th.

فأي أسلوب تختار؟ نوصي على الأقل بتنفيذ الأسلوب رقم 1، لجعل سكربت verify-exercises مستقلًا بذاته. إذا كانت صورتك كبيرة بشكل خاص، فقد يكون من المفيد تنفيذ الأسلوب 3 أيضًا، الذي يخزّن صورة Docker المبنية في ذاكرة التخزين المؤقت لـ GitHub Actions. بعدها يمكن للتشغيلات اللاحقة قراءة صورة Docker من ذاكرة التخزين المؤقت مباشرة بدلًا من تنزيلها، وقد يكون ذلك أفضل للأداء (قِس بنفسك للتأكد).

الخيار الثالث: تشغيل سكربت التحقق من التمارين داخل صورة Docker لمُشغّل الاختبارات

الخيار الثالث، وهو خيار بديل، مزيج من الخيارين السابقين. هنا أيضًا نستخدم صورة Docker الخاصة بمُشغّل الاختبارات، لكننا هذه المرة نشغّل سكربت verify-exercises داخل تلك الصورة. ولتمكين هذا الخيار، نحتاج إلى ضبط حاوية سير العمل على مُشغّل الاختبارات:

container:
  image: exercism/vimscript-test-runner

ثم يمكننا تخطي خطوتي تثبيت التبعيات والأدوات (لأنها ستكون مثبّتة داخل صورة Docker الخاصة بمُشغّل الاختبارات) والمتابعة بتشغيل سكربت bin/verify-exercises file.

مثال

يستخدم سير العمل test.yml في مسار vimscript هذا الخيار:

name: Verify Exercises

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

jobs:
  ci:
    runs-on: ubuntu-24.04
    container:
      image: exercism/vimscript-test-runner

    steps:
      - name: Checkout repository
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

      - name: Verify all exercises
        run: bin/verify-exercises