بناء أوامر YAML لأتمتة تشغيل السكربتات التلقائية عند كل Push
بناء أوامر YAML لأتمتة تشغيل السكربتات التلقائية عند كل Push
عندما ينتقل الفريق من التنفيذ اليدوي إلى الأتمتة الحقيقية، تصبح لحظة push نقطة انطلاق ذكية لسلسلة كاملة من الفحوصات والمهام. هنا يظهر دور ملفات YAML في تعريف مسارات العمل بشكل قابل للمراجعة، واضح، ومخزن داخل المستودع نفسه.
الفكرة ليست مجرد تشغيل سكربت بعد رفع الكود، بل بناء خط تنفيذ منضبط يضمن فحص الجودة، تثبيت الاعتماديات، تشغيل الاختبارات، ثم تنفيذ مهام إضافية مثل بناء الحاويات أو النشر المرحلي. وهذا جوهر ما هو الـ CI/CD؟ ولماذا نؤتمت عمليات اختبار ونشر الأكواد؟ في المشاريع الحديثة.
في هذا المقال سنبني ملف workflow متقدم يعمل عند كل push، ونشرح كيف تنظم الأوامر، وكيف تمنع التكرار، وكيف تربط ذلك بمعمارية تشغيل آمنة قابلة للتوسع على مستوى الفرق والخوادم.
لماذا نستخدم YAML لتعريف التشغيل التلقائي؟
ملف YAML ليس مجرد تنسيق نصي، بل طبقة توصيف declarative تحدد ماذا يجب أن يحدث ومتى وبأي ترتيب. هذا مهم جداً في بيئات CI/CD لأن كل خطوة تصبح موثقة ومراجعة ضمن الكود.
بدلاً من تخزين منطق التنفيذ في خادم غير مرئي، تستطيع عبر workflow file جعل مسار البناء جزءاً من دورة التطوير نفسها. بهذه الطريقة، أي تعديل في الفحوصات أو الأوامر يخضع إلى مراجعة pull request وليس إلى تغييرات يدوية مبعثرة.
البنية الأساسية لملف التشغيل عند كل Push
إذا كنت قد قرأت مقدمة في GitHub Actions: كتابة أول مسار عمل (Workflow) فستعرف أن الملف يوضع عادة داخل المسار .github/workflows/. ما يهمنا هنا هو تصميمه بشكل هندسي يسمح بإضافة مراحل لاحقة دون كسر البنية.
أبسط عناصر الملف هي:
- اسم المسار
name - المحفز
on - الوظائف
jobs - بيئة التشغيل
runs-on - الخطوات
steps
مثال عملي كامل لتشغيل سكربتات عند كل عملية رفع
الملف التالي ينفذ أربع مراحل أساسية: سحب الكود، تثبيت البيئة، تشغيل سكربت فحص، ثم تشغيل سكربت نشر أو بناء داخلي. المثال مناسب لمشروع يحتوي على سكربتات Bash داخل مجلد scripts.
name: Run automation scripts on push
on:
push:
branches:
- main
- develop
- "feature/**"
jobs:
automation:
runs-on: ubuntu-latest
env:
APP_ENV: ci
PROJECT_NAME: sample-app
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set execute permissions
run: chmod +x scripts/*.sh
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y jq curl
- name: Run lint script
run: ./scripts/lint.sh
- name: Run test script
run: ./scripts/test.sh
- name: Run build script
run: ./scripts/build.sh
هذا النموذج بسيط في ظاهره، لكنه يعكس مفهوماً مهماً: كل خطوة قابلة للعزل والتشخيص. إذا فشل سكربت الفحص فلن ينتقل المسار إلى البناء. هذا يقلل من استهلاك الموارد ويحسن قابلية تتبع الخطأ داخل pipeline logs.
كيف نبني السكربتات نفسها بطريقة مناسبة للأتمتة؟
من الأخطاء الشائعة أن يكون ملف YAML جيداً، بينما السكربتات نفسها غير حتمية أو تعتمد على تدخل يدوي. لذلك يجب أن تكون السكربتات:
- قابلة للتشغيل دون أسئلة تفاعلية.
- تعيد
exit codeصحيحاً. - تكتب سجلات واضحة تسهل التتبع.
- لا تخزن أسراراً داخل النص البرمجي.
مثال على سكربت فحص جيد:
#!/usr/bin/env bash
set -euo pipefail
echo "Starting lint checks..."
npm ci
npm run lint
echo "Lint completed successfully."
استخدام set -euo pipefail يجعل السكربت أكثر صرامة، لأنه يوقف التنفيذ فور حدوث خطأ أو استخدام متغير غير معرف. هذا مهم جداً في بيئات الأتمتة حيث الصمت عند الفشل قد يقود إلى نشر نسخة معيبة.
تشغيل الأوامر وفق الفروع والسياسات التشغيلية
ليس من الحكمة تنفيذ نفس الإجراءات على كل الفروع. عادةً تنفذ اختبارات سريعة على فروع الميزات، بينما يتم تشغيل بناء كامل أو نشر مرحلي على main. هذه الممارسة تتكامل مع حماية الفروع (Branch Protection) ومنع رفع الأكواد الخاطئة للنسخة الحية.
يمكنك إضافة شروط دقيقة داخل الخطوات نفسها:
- name: Deploy staging
if: github.ref == 'refs/heads/main'
run: ./scripts/deploy-staging.sh
هذه الآلية تعطيك تحكماً دقيقاً دون الحاجة إلى تكرار ملف كامل لكل فرع. كما أنها تجعل المسار قابلاً للتوسع مع نمو الفريق وتعدد البيئات مثل dev وstaging وproduction.
ربط الأتمتة ببناء الحاويات والخدمات
في المشاريع الحديثة، تشغيل السكربتات بعد push لا يتوقف عند الفحص فقط، بل يمتد لبناء صور Docker وتشغيل اختبارات التكامل. وإذا كنت تريد تأسيس هذا الجزء جيداً فراجع مشكلة “الكود يعمل على جهازي فقط” وكيف يحلها Docker نهائياً؟ وكتابة أول ملف docker-compose.yml خطوة بخطوة.
مثال على إضافة خطوة بناء حاوية:
- name: Build Docker image
run: docker build -t sample-app:${{ github.sha }} .
عند دمج هذه الخطوة مع فحص الاعتماديات واختبارات الوحدة، تحصل على خط تحقق متكامل قبل وصول التغييرات إلى البيئات الحساسة. هذا يقلل كثيراً من مشاكل الانحراف بين بيئة المطور وبيئة الخادم.
أفضل الممارسات الأمنية عند تشغيل السكربتات تلقائياً
لا تضع مفاتيح
APIأو بياناتSSHأو كلمات المرور داخل ملفاتYAMLأو سكربتاتBash. استخدم مخزن الأسرار الخاص بالمنصة، ومرر القيم عبر متغيرات بيئية محكومة الصلاحيات ومقيدة على مستوى البيئة أو الفرع.
احرص على مبدأ أقل صلاحية
Least Privilege. أي حساب خدمة يستخدمه مسار الأتمتة يجب أن يملك فقط الصلاحيات اللازمة للتنفيذ، لأن منح صلاحيات نشر أو حذف واسعة قد يحول خطأ بسيطاً في سكربت إلى كارثة تشغيلية أو توقف خدمة.
كما يُفضّل تفعيل ضوابط التوازي، خاصة إذا كان الفريق يرفع تعديلات كثيرة خلال وقت قصير. الهدف هو منع أكثر من تنفيذ متزامن من التنافس على نفس البيئة أو نفس المورد.
concurrency:
group: deploy-main
cancel-in-progress: true
أخطاء شائعة عند كتابة أوامر YAML
- الخلط بين المسافات و
tabsمما يكسر البنية. - كتابة سكربتات طويلة جداً داخل الحقل
runبدلاً من فصلها في ملفات مستقلة. - تشغيل كل شيء على كل
pushدون فلترة للفروع. - إهمال حالات الفشل وعدم التحقق من
exit status. - ربط النشر المباشر بالرفع إلى الفرع الرئيسي دون مراجعة أو اختبارات كافية.
وفي المشاريع الجماعية، من المفيد الجمع بين الأتمتة على مستوى المستودع، وبين الفحوصات المحلية قبل الرفع عبر استخدام Git Hooks لأتمتة فحص وتنسيق الكود قبل أي عملية Push. هذا يوزع مسؤولية الجودة بين جهاز المطور ومنصة التنفيذ المركزية.
خاتمة
بناء أوامر YAML لتشغيل السكربتات التلقائية عند كل push ليس مهمة شكلية، بل قرار معماري يؤثر مباشرة في جودة الكود، سرعة التسليم، واستقرار الخوادم. كل سطر تكتبه في ملف التشغيل يمثل قاعدة تنفيذ يمكن الاعتماد عليها لاحقاً في الفحص، البناء، النشر، وإدارة الحاويات.
عندما تصمم المسار بوضوح، وتفصل السكربتات، وتطبق الشروط الأمنية، فإنك تبني نظاماً يمكن للفريق الوثوق به. وهذه هي الروح العملية لمجال ما هو DevOps؟ ولماذا تدفع الشركات ثروات لمهندسي الأتمتة السحابية؟: تحويل العمليات المتكررة إلى أنظمة دقيقة، قابلة للتوسع، وآمنة.
7 comments