التعبيرات والسياقات والشروط
التعبيرات والسياقات والشروط
حين تحفظ ملف workflow، فأنت قد كتبت YAML — لكن GitHub Actions يُقيّم لغةً ثانية مضمّنة داخل ذلك الـ YAML: لغة التعبيرات. فهمُها هو الفرق بين نسخ مقتطفات ومحادثات وبين تأليف workflows تتصرف بالضبط كما تنوي في كل الأحوال. يغطي هذا الدرس تلك اللغة بالكامل: صياغتها، والسياقات المدمجة التي تقرأ منها، والمفتاح if: الذي يتحكم في تنفيذ الـ jobs والخطوات.
صياغة التعبيرات
تُحاط التعبيرات بـ ${{ }}. يمكن استخدامها في أي موضع قيمة داخل ملف الـ workflow — حقول على مستوى الـ job مثل runs-on، وحقول الخطوات مثل run وenv وwith، وكذلك مفتاح if:. قيمة if: المجردة هي تعبير ضمني في حد ذاتها (الأقواس اختيارية هناك).
تدعم اللغة:
- القيم الحرفية — منطقية (
true/false)، ونول، وأرقام، وسلاسل بأقواس مفردة:'main'. الأقواس المزدوجة ليست محددات سلاسل صالحة. - العوامل —
==،!=،<،<=،>،>=،&&،||،!. المقارنة غير حساسة لحالة الأحرف في السلاسل. - الوصول للخصائص — النقطة (
github.event_name) أو الأقواس المربعة للمفاتيح الديناميكية (fromJson(inputs.matrix)[0]). - الدوال — مكتبة صغيرة مدمجة:
contains()،startsWith()،endsWith()،format()،join()،toJson()،fromJson()،hashFiles()،always()،success()،failure()،cancelled().
false؛ أي سلسلة غير فارغة تساوي true. هذا مقصود ومفيد — if: inputs.flag صحيحة متى كان المدخل قد أُعطي — لكنها تُخطئ المهندسين الذين يتوقعون مساواة صارمة.
السياقات
السياق (context) هو كائن مُسمّى تملؤه Actions قبل تشغيل الـ workflow. تصل إلى خصائصه بالتعبيرات. السياقات السبعة الأكثر استخداماً هي:
github— بيانات عن الحدث المُطلِق:github.event_name،github.ref،github.sha،github.actor،github.repository، وحمولة الحدث الكاملة فيgithub.event.env— متغيرات البيئة المُعرّفة على مستوى الـ workflow أو الـ job أو الخطوة. داخلrunيمكن قراءتها كمتغيرات shell ($VAR)، لكن${{ env.VAR }}متاحة في أي مكان بما فيه شروطif:.secrets— القيم المخزّنة في مخزن الأسرار. الوصول إليها عبر التعبيرات (${{ secrets.TOKEN }}) هو الطريقة الوحيدة الآمنة لتمريرها للخطوات. تُخفي Actions قيم الأسرار تلقائياً من السجلات.vars— متغيرات الإعداد غير الحساسة (على مستوى المستودع أو المنظمة). استخدمها لأشياء مثلAWS_REGIONأوNODE_VERSIONالتي تختلف بين البيئات لكنها ليست سرية.jobs— قيم المخرجات من jobs أخرى في نفس الـ workflow. متاحة فقط في الـ jobs التي تُعلنneeds:.steps— قيم المخرجات والنتيجة (success/failure/skipped/cancelled) للخطوات السابقة داخل نفس الـ job.runner— معلومات وقت التشغيل:runner.os،runner.arch،runner.temp،runner.tool_cache.
vars لكل ما يتغير بين البيئات وليس حساساً — سجلات Docker الأساسية، وأسماء الـ clusters، وعلامات الميزات، وقنوات الإشعارات. هذا يبقي secrets صغيرة وقابلة للتدقيق، ويتيح لك تغيير الإعداد غير السري دون تدوير أي اعتمادات.
مفتاح if: — التنفيذ الشرطي
كل job وكل خطوة تقبل مفتاح if:. حين يُقيّم التعبير إلى قيمة خاطئة، تتخطى GitHub تلك الـ job أو الخطوة كلياً — وتظهر كـ "skipped" في الواجهة. الشرط الضمني الافتراضي هو success()، مما يعني أن الـ jobs والخطوات تعمل فقط حين تنجح جميع الخطوات السابقة في نفس الـ job (أو جميع الـ jobs المطلوبة).
دوال التحقق من الحالة الأربع بالغة الأهمية:
success()— صحيحة حين تنتهي كل خطوة/job سابقة دون خطأ (الافتراضية).failure()— صحيحة حين تفشل خطوة/job سابقة واحدة على الأقل. استخدمها لخطوات الإشعار أو التنظيف.always()— صحيحة بصرف النظر عن النتيجة السابقة، حتى لو أُلغي الـ workflow. استخدمها لخطوات التنظيف التي يجب أن تعمل مهما حدث.cancelled()— صحيحة فقط حين أُلغي تشغيل الـ workflow صراحةً.
if: failure() على خطوة تحتاج أيضاً إلى مخرجات من خطوة سابقة. إذا فشلت الخطوة السابقة، فمخرجاتها فارغة أو غير معرّفة — وقد يُخطئ مُعالج الفشل بدوره. احرص دائماً على أن معالجات الفشل تعتمد على الحد الأدنى من البيانات التي تحتاجها فعلاً.
أنماط عملية
يوضح الـ workflow التالي المفاهيم الثلاثة معاً — التعبيرات، والسياقات المتعددة، والخطوات الشرطية — في سيناريو إنتاجي واقعي: نشر يعمل فقط على main، ويُخطر Slack عند الفشل، ويرفع السجلات دائماً كـ artifacts.
لاحظ التفاعل: vars.* يحمل الإعداد الخاص بالبيئة وغير الحساس؛ secrets.* يحمل ARN الدور ورابط الـ webhook؛ github.* يقود منطق الشرط؛ ومخرجات steps.* تُمرّر عنوان الصورة بين الـ jobs.
التحقق من نتائج الخطوات
لكل خطوة خاصية outcome: success، failure، cancelled، أو skipped. يمكنك التفريع بناءً عليها صراحةً حين تحتاج تحكماً دقيقاً:
يتيح لك continue-on-error: true أن تفشل خطوة دون إيقاف الـ job فوراً — تلتقط النتيجة وتقرر ما تفعله بعدها. هذا مفيد لمجموعات الاختبارات المتقلبة التي تريد رفع نتائجها قبل التوقف.
دوال التعبيرات في الممارسة
دالتان تستحقان اهتماماً خاصاً على نطاق واسع. hashFiles('**/package-lock.json') تُعيد تجزئة حتمية للملفات المطابقة — مثالية كمفتاح كاش يبطل فقط حين تتغير التبعيات فعلاً. fromJson() وtoJson() تتيحان تمرير بيانات هيكلية بين الخطوات والـ jobs، متجاوزتَين القيد النصي لـ GITHUB_OUTPUT.
contains(github.event.pull_request.labels.*.name, 'skip-ci') للسماح للمهندسين بوضع تسمية على الـ PR وتخطي الـ jobs المكلفة. حرف البدل *.name يُسطّح مصفوفة كائنات التسميات إلى أسمائها — أسلوب أنيق تراه في workflows الإنتاج على نطاق واسع.