اختفاء Node 20 من خطوط البناء: العطل الذي ورثته ولم تصنعه
إزالة Node 20 من منفّذي GitHub Actions أوقفت بناءات لم يتغيّر فيها سطر واحد منذ شهور: لماذا ينكسر ما لم تكتبه، وكيف تجعل خط البناء اعتمادية مرئية؟

في الثالث والعشرين من سبتمبر 2026 أُزيل Node.js 20 نهائياً من منفّذي GitHub Actions. لا إعلان صاخب، ولا صفحة هبوط، ولا تدوينة ترويجية — مجرد سطر في سجلّ التغييرات وتحديث صامت لصورة المنفّذ. ومع ذلك، هذا السطر تحديداً هو الذي أوقف خطوط بناء في فرق لم تغيّر حرفاً واحداً في مستودعاتها منذ شهور.
السبب يستحق الانتباه: العطل لا يأتي من الكود الذي كتبته، ولا من الاعتماديات التي اخترتها بنفسك، بل من الإجراءات (Actions) الجاهزة التي استدعيتها في ملف سير العمل ونسيتها. كل واحد منها برنامج JavaScript صغير يملك ملف تعريف يقول للمنفّذ: «شغّلني على هذا الإصدار من Node». وحين يختفي ذلك الإصدار من المنفّذ، يتوقف الإجراء — بغضّ النظر عن مدى سلامة مشروعك أنت.
ما الذي حدث بالضبط

منذ 16 يونيو 2026 صار المنفّذون يشغّلون إجراءات JavaScript على Node 24 افتراضياً، مع إبقاء Node 20 مثبّتاً كشبكة أمان، ومع متغيّر بيئة مؤقّت (ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION) يسمح بالعودة إلى القديم. تلك المرحلة الانتقالية انتهت في 23 سبتمبر: لم يعد Node 20 موجوداً على المنفّذ أصلاً، وبالتالي لم يعد متغيّر التراجع يعني شيئاً — فهو يطلب تشغيل ملفّ تنفيذي غير موجود.
العواقب العملية تنقسم إلى ثلاث فئات مختلفة تماماً، وكثير من الفرق يخلط بينها:
| الحالة | ما الذي ينكسر | من يصلحه |
|---|---|---|
إجراء خارجي يعلن runs.using: node20 |
الخطوة تفشل فوراً عند التشغيل | صائن الإجراء، ثم أنت بترقية الإصدار |
| إجراء داخلي كتبته شركتك | نفس الفشل، بلا أحد ينتظر منه إصلاح | أنت وحدك |
| منفّذ ذاتي الاستضافة قديم | Node 24 لا يعمل على macOS 13.4 أو أقدم ولا على ARM32 | فريق البنية التحتية |
الفئة الثالثة هي الأخبث، لأنها لا تُحلّ بتحديث سطر في ملف YAML: الجهاز نفسه خرج من دائرة الدعم. من يشغّل منفّذاً على ماك قديم أو على لوحة ARM بمعمارية 32 بت لم يعد أمامه ترقية، بل قرار عتاد.
العطل الذي يزعجك ليس العطل الذي تسبّبت فيه، بل العطل الذي ورثته من إعداد صحيح كتبته قبل سنتين ولم يعد صحيحاً اليوم.
لماذا فوجئت فرق كثيرة رغم عام كامل من التحذيرات
الإشعار لم يكن سرّاً؛ المنفّذ كان يطبع تحذيراً واضحاً في السجلّ لأشهر. المشكلة في طبيعة التحذيرات داخل خطوط البناء:
- تحذيرات البناء لا يقرأها أحد ما دام اللون أخضر. السجلّ يُفتح عند الفشل فقط، والتحذير لا يفشل شيئاً.
- التحذير كان غير دقيق في بعض الحالات بسبب خلل معروف في المنفّذ: كان يشتكي من Node 20 في سير عمل يشتغل فعلياً على Node 24، فتعلّم الناس تجاهله باعتباره ضجيجاً.
- التاريخ تغيّر أكثر من مرة. جدول الإزالة عُدّل خلال العام، ومن قرأ النسخة القديمة خطّط لموعد مختلف عن الموعد الذي حدث فعلاً.
- أسوأ المستودعات إصابةً هي الأكثر استقراراً. مستودع يُبنى بنجاح كل أسبوع منذ سنتين بلا تعديل هو بالضبط المستودع الذي تجمّدت فيه إصدارات الإجراءات على نسخ قديمة.
هذه النقطة الأخيرة تقلب حدسنا عن الصيانة: عادة نفترض أن المشروع النشط هو الهشّ، والمشروع الهادئ هو الآمن. في منظومة يتغيّر فيها المنفّذ من تحتك، الهدوء ليس استقراراً — إنه تأجيل.
الإصلاح العملي، بالترتيب
1. ابحث عن الإجراءات التي تعلن node20 صراحة
لا تخمّن. الإجراءات الخارجية تُنزَّل وقت التشغيل، وملف action.yml الخاص بكل واحد هو مصدر الحقيقة. أسرع مسح أوّلي هو قراءة كل ملفات سير العمل عندك وجمع أسماء الإجراءات وإصداراتها:
grep -rhoP 'uses:\s*\K\S+' .github/workflows/ | sort -u
القائمة الناتجة هي سطح الخطر كاملاً. أي سطر فيه إصدار قديم من الإجراءات الشائعة (سحب الكود، تهيئة Node، التخزين المؤقت، رفع النواتج) مرشّح مباشر للترقية.
2. رقّ الإصدارات الكبرى، لا الوسمة اللطيفة
معظم الإجراءات واسعة الانتشار أصدرت نسخاً تعمل على Node 24 قبل الموعد بوقت كافٍ. الترقية إلى أحدث إصدار كبير تحلّ الغالبية العظمى من الحالات في دقائق. الانتباه المطلوب هنا: من يثبّت الإجراءات ببصمة الالتزام (commit SHA) — وهي ممارسة أمنية ممتازة ضد اختطاف الوسوم — لن تصله الترقية تلقائياً أبداً. التثبيت يشتري لك حماية، وفاتورته صيانة يدوية دورية.
3. عالج إجراءاتك الداخلية أولاً
الإجراء الذي كتبته شركتك لا ينتظره صائن خارجي. غيّر runs.using إلى node24 في ملف التعريف، ثم شغّل حزمة اختباراته فعلياً على Node 24 قبل أن تعتمدها — الانتقال بين إصدارات Node الكبرى يغيّر سلوكيات دقيقة في المعالجة غير المتزامنة وفي بعض واجهات النظام، ولا يكفي تعديل الرقم.
4. افحص منفّذيك الذاتيين قبل أن يفحصهم البناء
إن كنت تشغّل منفّذين على عتادك: تحقق من نظام التشغيل والمعمارية قبل أي شيء آخر. ماك بإصدار 13.4 أو أقدم، أو أي منفّذ بمعمارية ARM32، خارج المعادلة الآن. القرار هنا إما ترقية النظام أو نقل العمل إلى منفّذ مستضاف.
الدرس الأكبر: خط البناء اعتمادية لا تراها في ملف القفل
نحن نتعامل مع الاعتماديات بجدية داخل المشروع: ملفات قفل، فحص أمني، تحديثات آلية، مراجعة قبل الدمج. لكن خط البناء نفسه يعيش خارج هذه المنظومة كلها. صورة المنفّذ، وإصدار Node بداخلها، ونظام التشغيل المستضاف، والإجراءات الخارجية — كلها اعتماديات حقيقية يتغيّر أصحابها من طرفهم، بجدول زمني لا تملك منه شيئاً، وبلا ملف قفل يحميك.
أربع عادات تجعل هذه الفئة مرئية:
- اقرأ سجلّ تغييرات منصّة البناء كما تقرأ سجلّ إطار العمل. خمس دقائق شهرياً تكفي لالتقاط تواريخ الإزالة مبكراً.
- حوّل التحذيرات إلى فشل مُتعمَّد في وظيفة منفصلة. وظيفة أسبوعية مسموح لها بالفشل، مهمّتها الوحيدة الصراخ عند ظهور تحذير إهمال، تنقل الإشارة من سجلّ لا يُقرأ إلى إشعار يصل.
- رقّ إجراءاتك على جدول، لا عند الأعطال. تحديث آلي دوري لإصدارات الإجراءات — حتى في المستودعات النائمة — يمنع تراكم سنوات من التأخّر دفعة واحدة.
- احتفظ بقائمة مكتوبة بمنفّذيك الذاتيين وأنظمتها. هذه القائمة هي ما يفرّق بين إصلاح في ساعة وتحقيق في يوم كامل.
أسئلة شائعة
هل يكفي ضبط متغيّر التراجع لتأجيل المشكلة؟
لا، ولم يعد له أثر. متغيّر السماح بإصدار Node قديم كان يعمل فقط ما دام Node 20 مثبّتاً على المنفّذ. بعد إزالته في 23 سبتمبر لم يعد هناك ما يُرجَع إليه، والنتيجة فشل مباشر في الخطوة.
مشروعي يبني حاويات ولا يستخدم Node إطلاقاً، هل أنا في أمان؟
ليس بالضرورة. الإصدار المقصود هنا هو الذي يشغّل الإجراءات نفسها، لا لغة مشروعك. مستودع بلغة Go أو بايثون أو Rust يستدعي إجراءات JavaScript جاهزة في كل سير عمل تقريباً — وهذه هي المتأثّرة، مهما كانت لغة الكود الذي تبنيه.
هل تثبيت الإجراءات ببصمة الالتزام كان قراراً خاطئاً إذن؟
على العكس، هو قرار سليم أمنياً ويحميك من تغيّر محتوى الوسم تحت قدميك. لكنه يحوّل الترقية من حدث تلقائي إلى مهمة صيانة صريحة. التثبيت بلا مراجعة دورية يعني تجميد نسخة قديمة إلى أجل غير مسمّى — والنتيجة ما رأيناه هذا الأسبوع.
الخلاصة
إزالة Node 20 من منفّذي GitHub Actions ليست حدثاً تقنياً كبيراً بحدّ ذاته، وإصلاحها في معظم الحالات لا يتجاوز رفع أرقام إصدارات في ملف YAML. قيمتها الحقيقية في ما تكشفه: خط البناء منظومة اعتماديات كاملة تعيش خارج ملف القفل، ويتحرّك أصحابها بجدولهم لا بجدولك، والمستودع الذي لم يلمسه أحد منذ سنتين هو أكثر ما فيها عرضة للانكسار.
عامل منصّة البناء كما تعامل إطار العمل واللغة: راقب إعلانات الإهمال، رقّ على جدول ثابت، واجعل التحذيرات تصل إلى إنسان بدل أن تموت في سجلّ أخضر لا يفتحه أحد.