CI بطيء؟ npm cache في GitHub Actions ينزل install لـ 55 ثانية
لو كل Pull Request عندك بيستنى 4 دقائق على npm ci، المقال ده هيخليك تقلل الانتظار غالبًا لأقل من دقيقة في التشغيلات المتكررة، بدون ما تخبي تغييرات dependencies.
مستوى القارئ: متوسط
المشكلة باختصار
اللي بيحصل فعلاً إن GitHub Actions بيبدأ غالبًا من runner نظيف. معنى كده إن npm ci بينزل packages من جديد، حتى لو package-lock.json متغيرش من آخر run. في مشروع React أو Next.js متوسط، خطوة التثبيت ممكن تاخد من 3 إلى 5 دقائق، بينما الاختبارات نفسها تاخد أقل من دقيقة.
الطريقة الشائعة الغلط هي إنك تعمل cache لـ node_modules كله. الطريقة دي بتفشل لما نظام التشغيل أو نسخة Node أو lockfile يتغيروا. البديل الأفضل: خلّي actions/setup-node يعمل cache لمجلد npm العالمي، وسيب npm ci يبني node_modules بشكل نظيف من lockfile.
مثال واقعي قبل الحل
افترض إن عندك repo فيه 900 dependency، وبيطلع 25 Pull Request في اليوم. لو npm ci بياخد 250 ثانية في كل run، فأنت بتصرف حوالي 104 دقائق يوميًا في تثبيت مكرر. بعد cache hit مستقر، الرقم ممكن ينزل إلى 55–65 ثانية. ده وفر تقريبي 75% في خطوة التثبيت، وليس في كامل الـ pipeline.
ركز في النقطة دي: الـ cache لا يسرّع الاختبارات نفسها. هو يقلل وقت تحميل tarballs وإعادة بناء جزء من dependency install. لو test suite عندك بطيء 12 دقيقة، cache npm مش هيحل أصل المشكلة.
الإعداد العملي
أفضل طريقة في npm هي استخدام actions/setup-node مع cache: 'npm'. الأداة تستخدم lockfile لتوليد cache key، وتستدعي actions/cache تحت الغطاء. مثال workflow كامل:
name: ci
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
cache-dependency-path: package-lock.json
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
لو عندك monorepo، متحطش مسار واحد بالغلط. استخدم مسارات lockfiles الفعلية: