تشغيل اختبارات Shell في مسارات CI
اربط ShellCheck وBats بـ GitHub Actions لمنع تمرير أي تغيير في Shell ما لم تنجح جميع الفحوصات
تشغيل اختبارات Shell في مسارات CI درس مجاني في Linux Command Line & Bash Scripting Mastery على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Linux Command Line & Bash Scripting Mastery، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
لماذا تهم CI للنصوص البرمجية الصدفية
النصوص البرمجية الصدفية هي شيفرة — وككل الشيفرات، تستحق بوابات جودة آلية. من دون CI، قد يصل خطأ مطبعي في نص برمجي للنشر إلى بيئة الإنتاج بصمت، ويتسبب في انقطاع الخدمة عند الساعة الثالثة صباحًا.
يفرض خط أنابيب CI متين لمشروعات Bash أمرين في كل طلب سحب:
- التحليل الساكن باستخدام
ShellCheck— إذ يكتشف أخطاء البنية، والأنماط غير الآمنة، ومشكلات قابلية النقل بين أنظمة POSIX قبل تشغيل النص البرمجي. - اختبارات الوحدة/التكامل باستخدام
Bats(Bash Automated Testing System) — إذ ينفّذ دوالكم ويتحقق من صحة سلوكها.
يشكل الأداتان معًا شبكة أمان تجعل إعادة الهيكلة أكثر اطمئنانًا وتسهل تأهيل المنضمين الجدد. يربط هذا الدرس الأداتين بمنصة GitHub Actions، وهي أكثر منصات CI المجانية شيوعًا للمشروعات مفتوحة المصدر والمشروعات التي تديرها فرق صغيرة.
مقدمة إلى GitHub Actions لمشروعات Shell
GitHub Actions هي CI/CD قائمة على الأحداث ومضمّنة في GitHub. وسير العمل هو ملف YAML مخزّن ضمن .github/workflows/. ويُشغَّل عند وقوع أحداث مثل push وpull_request، وينفّذ المهام على runners مستضافة.
المفاهيم الأساسية التي تحتاجون إليها:
on:— المشغّل، مثلpushوpull_requestjobs:— وحدات عمل متوازية، تعمل كل منها على آلة افتراضية جديدةsteps:— أوامر Shell متسلسلة أو actions قابلة لإعادة الاستخدام ضمن مهمةruns-on:— صورة runner، ونستخدمubuntu-latest
يجب تثبيت ملفات سير العمل في المستودع. يكتشفها GitHub تلقائيًا، ولا يلزم إعداد خارجي.
# Minimal skeleton — .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
shell-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Steps go here"تثبيت ShellCheck في سير العمل
يكون ShellCheck مثبتًا مسبقًا على runners التي تستخدم ubuntu-latest، لذلك لا تحتاجون في معظم الحالات إلى أي خطوات تثبيت. لكن قد يتأخر الإصدار المثبت مسبقًا عن أحدث إصدار. ولضمان قابلية إعادة الإنتاج، ثبّتوا إصدارًا محددًا.
استراتيجيتان للتثبيت:
- استخدام الملف التنفيذي المثبت مسبقًا — وهو الخيار الأبسط، وكافٍ لمعظم المشروعات.
- تثبيت إصدار محدد عبر أرشيف الإصدار الرسمي من GitHub — ويضمن استخدام إصدار المدقق نفسه محليًا وفي CI.
توضح الخطوة أدناه النهج الذي يثبت إصدارًا محددًا باستخدام سلسلة إصدار ثابتة مخزّنة كمتغير بيئي، مما يجعل الترقية تغييرًا في سطر واحد.
# .github/workflows/ci.yml — ShellCheck install step
- name: Install ShellCheck
env:
SC_VERSION: v0.10.0
run: |
curl -sSfL \
"https://github.com/koalaman/shellcheck/releases/download/${SC_VERSION}/shellcheck-${SC_VERSION}.linux.x86_64.tar.xz" \
| tar -xJf - --strip-components=1 -C /usr/local/bin shellcheck-${SC_VERSION}/shellcheck
shellcheck --versionتشغيل ShellCheck على كل نص برمجي
بعد التثبيت، تحتاجون إلى خطوة تكتشف جميع النصوص البرمجية الصدفية في المستودع وتفحصها. استخدموا find لتحديد موقع الملفات، ثم مرّروها إلى shellcheck.
أعلام مهمة ينبغي معرفتها:
-e SC2034— يستبعد قاعدة محددة، فاستخدموه باعتدال ومع تعليق.--severity=warning— يفشل فقط عند التحذيرات وما هو أعلى منها، مع تجاهل اقتراحات الأسلوب.-x— يتبع توجيهاتsourceلفحص الملفات المصدرية أيضًا.
إذا عثر shellcheck على أي مشكلة، فإنه يخرج بقيمة غير صفرية، مما يفشل خطوة CI تلقائيًا — ولا حاجة إلى منطق إضافي.
# .github/workflows/ci.yml — ShellCheck lint step
- name: Lint shell scripts
run: |
# Find all .sh files and files with a bash/sh shebang
mapfile -t scripts < <(
find . -type f -name '*.sh' -not -path './.git/*'
)
if [[ ${#scripts[@]} -eq 0 ]]; then
echo 'No shell scripts found — skipping.'
exit 0
fi
echo "Linting ${#scripts[@]} file(s)..."
shellcheck --severity=warning -x "${scripts[@]}"ما هو Bats وكيف يعمل؟
يُعد Bats (Bash Automated Testing System) إطار عمل لاختبار Bash ومتوافقًا مع TAP. وكل ملف اختبار هو ملف .bats يحتوي على كتل @test.
ينجح الاختبار عندما تخرج هيئته بالقيمة 0، ويفشل عندما تخرج بقيمة غير صفرية. ويوفر Bats متغيرات ودوال مساعدة:
$status— رمز الخروج لآخر أمرrun.$output— مخرجات stdout+stderr المجمعة لآخر أمرrun.$lines— مصفوفة أسطر المخرجات.run <cmd>— ينفّذ أمرًا من دون إفشال الاختبار عند الخروج بقيمة غير صفرية.
تُعد الدالة المساعدة run ضرورية؛ فمن دونها، سيؤدي فشل الأمر إلى إيقاف الاختبار قبل أن تتمكنوا من فحص $status.
#!/usr/bin/env bats
# tests/greet.bats
setup() {
# Runs before every @test block
source "${BATS_TEST_DIRNAME}/../lib/greet.sh"
}
@test "greet outputs hello with the given name" {
run greet "Alice"
[ "$status" -eq 0 ]
[ "$output" = "Hello, Alice!" ]
}
@test "greet fails when no argument is provided" {
run greet
[ "$status" -eq 1 ]
[[ "$output" == *"Usage"* ]]
}تثبيت Bats-Core باستخدام Git Submodule
الطريقة المعتمدة لإضافة Bats إلى مشروع هي استخدام Git submodule. فهذا يثبت commit محددًا، ويحافظ على تطابق إصدار المشغّل مع إصدار التطوير المحلي، ويجنبكم الاعتماد على مديري الحزم.
شغّلوا هذه الأوامر مرة واحدة محليًا، ثم ثبّتوا النتيجة في المستودع:
git submodule add https://github.com/bats-core/bats-core test/batsgit submodule add https://github.com/bats-core/bats-support test/test_helper/bats-supportgit submodule add https://github.com/bats-core/bats-assert test/test_helper/bats-assert
في CI، استعيدوا الوحدات الفرعية باستخدام actions/checkout@v4 وخيار submodules: recursive. وتوضح الخطوة أدناه إعداد checkout الكامل.
# .github/workflows/ci.yml — checkout with submodules
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive # restores bats-core + helpersتشغيل اختبارات Bats في CI
بمجرد إتاحة Bats، سواء عبر submodule أو تثبيت حزمة، يصبح تشغيل الاختبارات أمرًا واحدًا. وجّهوا Bats إلى مجلد، وسيكتشف كل ملف .bats بشكل متكرر باستخدام العلم --recursive.
ينتج العلم --formatter tap المخرجات بتنسيق TAP (Test Anything Protocol)، الذي تحلله العديد من أنظمة CI لإنشاء تقارير الاختبارات. أما المنسّق الافتراضي pretty فهو أفضل للقراءة البشرية في السجلات الخام.
استخدموا --timing لاكتشاف الاختبارات البطيئة مبكرًا؛ فالاختبار الذي يستغرق أكثر من 5 ثوانٍ يشير غالبًا إلى استدعاء شبكة غير مرغوب فيه أو mock مفقود.
# .github/workflows/ci.yml — Bats test step
- name: Run Bats tests
run: |
# If installed as a submodule:
./test/bats/bin/bats \
--recursive \
--timing \
tests/
# If installed via apt or brew (alternative):
# bats --recursive --timing tests/سير عمل كامل: ShellCheck وBats
اجمعوا الآن كل شيء في ملف سير عمل واحد جاهز للإنتاج. أفضل الممارسات المطبقة هنا:
- تعمل مهمتان منفصلتان (
lintوtest) بالتوازي، مما يوفر ملاحظات أسرع. - تعلن مهمة
testعنneeds: lint، لذلك لا تعمل الاختبارات إلا بعد نجاح الفحص — وهذا يتجنب إهدار دقائق تشغيل runner على شيفرة معطوبة بوضوح. - تمنع إصدارات actions المثبتة (
@v4) الأعطال المفاجئة الناتجة عن تحديثات المنبع. - تقيّد كتلة
permissions:رمز سير العمل بالحد الأدنى المطلوب.
# .github/workflows/ci.yml
name: Shell CI
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
lint:
name: ShellCheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
run: |
mapfile -t scripts < <(find . -name '*.sh' -not -path './.git/*')
[[ ${#scripts[@]} -gt 0 ]] && shellcheck --severity=warning -x "${scripts[@]}"
test:
name: Bats Tests
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Run tests
run: ./test/bats/bin/bats --recursive --timing tests/تخزين الاعتماديات مؤقتًا لتسريع عمليات التشغيل
عند تثبيت مساعدات Bats أو الأدوات الأخرى عبر مدير حزم داخل سير العمل، يسرّع التخزين المؤقت عمليات التشغيل اللاحقة بدرجة كبيرة. ويوفر GitHub Actions لهذا الغرض action باسم actions/cache.
نقاط أساسية للتخزين المؤقت الفعّال:
- استخدموا مفتاح تخزين مؤقت يتضمن نظام التشغيل واسم الأداة وتجزئة ملف القفل، بحيث تُبطَل صلاحية التخزين المؤقت تلقائيًا عند تغيير الاعتماديات.
- يتيح البديل
restore-keysلسير العمل استخدام تخزين مؤقت قديم بدلًا من البدء من الصفر عند عدم العثور على تطابق. - بالنسبة إلى Git submodules، نادرًا ما تكون الحاجة إلى التخزين المؤقت قائمة، لأن checkout للوحدات الفرعية سريع. وتظهر فائدته الأكبر مع تثبيت أدوات
npmأوpipأو الأدوات المترجمة.
# .github/workflows/ci.yml — cache step example
- name: Cache Bats npm helpers
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-bats-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-bats-
- name: Install helpers
run: npm ci # uses cache when availableحماية الفروع: فرض نجاح عمليات التحقق
يكون سير عمل CI الذي لا يمنع الدمج إرشاديًا في أفضل الأحوال. وتحول قواعد حماية الفروع في GitHub عمليات التحقق إلى بوابات إلزامية.
لإعدادها: انتقلوا إلى Settings → Branches → Add rule للفرع main، ثم فعّلوا ما يلي:
- Require status checks to pass before merging — اختاروا ShellCheck وBats Tests بالاسم.
- Require branches to be up to date before merging — يمنع طلب سحب اجتاز عمليات التحقق على أساس قديم من إدخال شيفرة معطوبة.
- Do not allow bypassing the above settings — يطبّق القواعد حتى على مسؤولي المستودع.
مع تفعيل هذه القواعد، لا يكون الدمج ممكنًا إلا عبر طلب سحب تنجح فيه جميع مهام CI — وهي شبكة الأمان المطلوبة تمامًا.
تصحيح أخطاء خطوات CI الفاشلة محليًا
عند فشل تشغيل CI، تكون أسرع دورة للإصلاح هي إعادة إنتاج الفشل محليًا قبل دفع commit آخر. إليكم تقنيتين:
- شغّلوا الأوامر نفسها من الخطوة الفاشلة في الطرفية — إذ يشغّل CI Shell عاديًا، لذلك يمكن نسخ الأوامر ولصقها وإعادة إنتاجها.
- استخدموا
act— وهي أداة تشغّل سير عمل GitHub Actions محليًا داخل Docker، وتوفر أقرب تطابق ممكن مع بيئة runner المستضافة.
من المصادر الشائعة للأعطال التي تظهر في CI فقط عدم تطابق إصدارات الأدوات بين جهاز Mac لديكم، مثل find الخاص بـ BSD على macOS، وfind الخاص بـ GNU على Ubuntu. اختبروا دائمًا باستخدام أعلام --posix أو استخدموا act لتشغيل صورة Ubuntu محليًا.
#!/usr/bin/env bash
# run_ci_locally.sh — mimic the CI lint step on your machine
set -euo pipefail
echo '=== ShellCheck ==='
mapfile -t scripts < <(find . -name '*.sh' -not -path './.git/*')
if [[ ${#scripts[@]} -eq 0 ]]; then
echo 'No .sh files found.'
else
shellcheck --severity=warning -x "${scripts[@]}"
echo "Linted ${#scripts[@]} file(s) — OK"
fi
echo '=== Bats ==='
./test/bats/bin/bats --recursive --timing tests/اختبار المعرفة: مفاهيم خط أنابيب CI
اختبروا مدى فهمكم لربط ShellCheck وBats بـ GitHub Actions.
مراجعة: CI لـ Shell باستخدام ShellCheck وBats
أنشأتم في هذا الدرس خط أنابيب CI كاملًا لمشروعات Bash باستخدام GitHub Actions. إليكم ما تناولتموه:
- أساسيات GitHub Actions — توجد ملفات YAML الخاصة بسير العمل في
.github/workflows/، وتُشغَّل عند push وpull_request، وتنفّذ المهام على runners تستخدمubuntu-latest. - ShellCheck — مثبت مسبقًا على Ubuntu runners؛ استخدموا
findلاكتشاف النصوص البرمجية، و--severity=warning -xلإنشاء بوابة فحص عملية. - Bats عبر submodule — ثبّتوا bats-core والمساعدات كـ Git submodules، واستعيدوها في CI باستخدام
submodules: recursiveضمن إجراء checkout. - ترتيب المهام — استخدموا
needs:حتى تعمل الاختبارات بعد نجاح الفحص فقط، مما يحافظ على سرعة الملاحظات ويتجنب إهدار موارد الحوسبة. - حماية الفروع — افرضوا عمليات التحقق من الحالة في إعدادات GitHub، حتى لا يمكن إدخال أي طلب سحب من دون نجاح CI.
- إعادة الإنتاج محليًا — انسخوا أوامر CI مباشرة إلى الطرفية، أو استخدموا
actلتصحيح الأعطال من دون commits إضافية.
مع تفعيل خط الأنابيب هذا، يُتحقق تلقائيًا من كل تغيير في Shell قبل وصوله إلى فرعكم الرئيسي.
الأسئلة الشائعة
هل درس «تشغيل اختبارات Shell في مسارات CI» مجاني؟
نعم — نص درس «تشغيل اختبارات Shell في مسارات CI» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Linux Command Line & Bash Scripting Mastery، انتقل إلى CoddyKit PRO. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.
ماذا ستتعلم في «تشغيل اختبارات Shell في مسارات CI»؟
اربط ShellCheck وBats بـ GitHub Actions لمنع تمرير أي تغيير في Shell ما لم تنجح جميع الفحوصات تتمرن على Linux Command Line & Bash Scripting Mastery مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Linux Command Line & Bash Scripting Mastery؟
لا تُشترط خبرة سابقة. Linux Command Line & Bash Scripting Mastery على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «تشغيل اختبارات Shell في مسارات CI»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Linux Command Line & Bash Scripting Mastery هذا؟
نعم. كل درس في Linux Command Line & Bash Scripting Mastery يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- اختبار الدوال بوحدة باستخدام Bats-core
- محاكاة الأوامر وإنشاء بدائل للأدوات الخارجية
- بيانات الاختبار والبيئات المؤقتة وتغطية الاختبارات
- تشغيل اختبارات Shell في مسارات CI