أساسيات التكامل المستمر

تشريح خط أنابيب التكامل المستمر

18 دقيقة الدرس 2 من 28

تشريح خط أنابيب التكامل المستمر

في كل مرة يدفع فيها مطور الكود، ينفذ خط أنابيب التكامل المستمر سلسلة من الفحوصات المرتبة بدقة. هذا الترتيب ليس اعتباطيًا — بل مصمم لغرض واحد: الكشف عن الأخطاء في أقرب وقت ممكن وبأقل تكلفة ممكنة. فهم تشريح خط الأنابيب يعني فهم لماذا توجد كل مرحلة، وما الذي تكتشفه، وما التكلفة إن أهملتها، وكيف تُرتب المراحل لتقليل الحوسبة الضائعة.

المراحل الأربع الأساسية

بغض النظر عن الأداة — GitHub Actions أو GitLab CI أو Jenkins أو CircleCI أو Buildkite — يمر خط الأنابيب المصمم جيدًا عبر أربع مراحل منطقية بالتسلسل: فحص الكود (Lint) ← البناء (Build) ← الاختبار (Test) ← التحزيم (Package). لكل مرحلة نمط فشل مميز وملف تكلفة مختلف.

CI Pipeline Stages: Lint, Build, Test, Package Lint ~5–30 s pass Build ~30 s–5 min pass Test ~1–20 min pass Package ~1–5 min syntax / style compile errors regression / coverage image / bundle الفشل السريع: أي مرحلة تفشل توقف خط الأنابيب فورًا
المراحل الأربع الأساسية لخط أنابيب التكامل المستمر ومدتها الزمنية النموذجية. المراحل المبكرة أرخص تكلفةً، والفشل فيها يوفر كل الحوسبة اللاحقة.

المرحلة الأولى — فحص الكود (Lint)

يعمل فحص الكود قبل الترجمة. مهمته رفض الكود المشوه أو المخالف لقواعد الأسلوب أو الذي يحتوي على أنماط مضادة معروفة — دون تنفيذ سطر واحد من منطق التطبيق. لأنه يعمل على نص المصدر فقط، يُعد فحص الكود أسرع إشارة ممكنة يمكن أن ينتجها خط الأنابيب.

على نطاق الشركات الكبرى، تُوازى مهام الفحص عبر الملفات المعدلة وتكتمل عادةً في أقل من 30 ثانية. الأدوات الشائعة: ESLint لـJavaScript/TypeScript، وpylint/ruff لـPython، وgolangci-lint لـGo، وshellcheck لسكريبتات الشل، وhadolint لـDockerfiles.

# .github/workflows/ci.yml — مرحلة فحص الكود lint: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '22' cache: 'npm' - run: npm ci --ignore-scripts - name: ESLint run: npx eslint src/ --max-warnings=0 - name: TypeScript type-check run: npx tsc --noEmit - name: Prettier format check run: npx prettier --check "src/**/*.{ts,tsx}"
خيار --max-warnings=0 في ESLint هو خيار على مستوى الإنتاج: تُعامَل التحذيرات كأخطاء. تبدأ كثير من الفرق بالسماح بالتحذيرات وتشديد القاعدة تدريجيًا. تفرض Google وMeta وAmazon صفر تحذيرات في CI — "إصلاحه الآن" يكلف دقائق؛ "إصلاحه لاحقًا" يكلف أسابيع من الديون المتراكمة.

المرحلة الثانية — البناء (Build)

تقوم مرحلة البناء بترجمة أو تحويل الكود المصدري إلى شكل قابل للتنفيذ. تُثبت أن كل استيراد يُحل، وكل نوع مُستوفى، وكل أصل يمكن تجميعه. فشل البناء يعني عدم وجود قطعة أثرية — لا شيء للاختبار أو الشحن.

قابلية إعادة إنتاج البناء هي الشاغل الرئيسي. يجب أن ينتج نفس الالتزام نفس الملف الثنائي على أي عداء وفي أي يوم. يتطلب ذلك قفل إصدارات التبعيات (package-lock.json، go.sum، Cargo.lock)، وتثبيت إصدارات الأدوات، وتسجيل إصدار المترجم في مخرجات CI.

# مرحلة البناء في نفس سير العمل build: runs-on: ubuntu-24.04 needs: lint # تعمل فقط إذا نجح الفحص steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '22' cache: 'npm' - run: npm ci --ignore-scripts - name: Production build run: npm run build env: NODE_ENV: production - name: Upload build artifact uses: actions/upload-artifact@v4 with: name: dist-${{ github.sha }} path: dist/ retention-days: 7

المرحلة الثالثة — الاختبار (Test)

مرحلة الاختبار هي عادةً الجزء الأطول والأكثر تكلفةً في خط الأنابيب. تشغل مجموعة الاختبارات الآلية على قطعة البناء الأثرية. تقسم مرحلة الاختبار المنظمة جيدًا الاختبارات حسب السرعة والنطاق: تعمل اختبارات الوحدة أولًا (مللي ثانية لكل منها، بدون إدخال/إخراج)، ثم اختبارات التكامل (اتصالات DB/cache حقيقية، أبطأ)، وأخيرًا اختبارات End-to-End (أتمتة متصفح كاملة، دقائق).

على نطاق واسع، تُجزَّأ مجموعات الاختبار الكبيرة عبر N من العداءات المتوازية بحيث يظل إجمالي وقت التشغيل محدودًا. يستخدم GitHub Actions strategy.matrix لهذا؛ بينما يمتلك Buildkite أساسيات توازي أصلية. يُقاس التغطية وتُفرض حد أدنى (عادةً 80%) كبوابة جودة — يُرفض أي طلب سحب يخفض التغطية دون العتبة.

شغّل اختبارات الوحدة أولًا ضمن مرحلة الاختبار. إن فشل أي اختبار وحدة، أوقف قبل إنفاق المال على التكامل أو E2E. تفصل كثير من الفرق هذه إلى ثلاث مهام مستقلة حتى يكون مخطط التبعية صريحًا والإخفاقات واضحة فورًا في واجهة PR.

المرحلة الرابعة — التحزيم (Package)

التحزيم يعمل فقط بعد نجاح جميع الاختبارات. ينشئ القطعة الأثرية القابلة للنشر: صورة Docker أو JAR أو ملف ثنائي مُجمَّع أو حزمة Helm. هذا هو المخرج الذي سيرقيه نظام CD عبر البيئات. نظرًا لتكلفة التحزيم (طبقات الصورة، تسخين الذاكرة المؤقتة، الدفع إلى السجل)، يُبوَّب خلف مرحلة اختبار ناجحة — لا تريد أبدًا دفع صورة غير مختبرة.

أفضل الممارسات: وسم الصور بالـ SHA الكامل لـgit، وليس بـlatest. هذا يجعل كل قطعة أثرية قابلة للتتبع حتى الالتزام الدقيق الذي أنتجها، وهو أمر ضروري للتراجع والتحقيق في الحوادث.

# مرحلة التحزيم — بناء ودفع صورة Docker package: runs-on: ubuntu-24.04 needs: test # تعمل فقط إذا نجحت جميع مهام الاختبار permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Download build artifact uses: actions/download-artifact@v4 with: name: dist-${{ github.sha }} path: dist/ - name: Build and push image uses: docker/build-push-action@v5 with: context: . push: true tags: | ghcr.io/${{ github.repository }}:${{ github.sha }} ghcr.io/${{ github.repository }}:latest cache-from: type=gha cache-to: type=gha,mode=max

ترتيب الفشل السريع — اقتصاديات ترتيب المراحل

ترتيب Lint → Build → Test → Package ليس مجرد اتفاقية — بل قرار اقتصادي. كل مرحلة أكثر تكلفةً من سابقتها. إذا كان خطأ فحص الكود سيكتشف مشكلة في 10 ثوانٍ، فإن تشغيل مجموعة اختبار مدتها 15 دقيقة قبل فحص الكود يضيع 14 دقيقة و50 ثانية من الحوسبة، مضروبةً في كل مطور وكل عملية دفع في المنظمة.

يُفرض هذا المبدأ بكلمة المفتاح needs في GitHub Actions (أو dependencies/needs في GitLab CI). المرحلة التي تُعلن needs: [lint] لن تبدأ حتى ينجح الفحص، وستُتخطى كليًا إن فشل. الفشل السريع هو السلوك الافتراضي عند ربط المراحل بشكل صحيح.

خطأ شائع هو تشغيل جميع المهام بالتوازي دون قيود لـ"توفير الوقت". هذا قد يدفع صورة Docker إلى السجل حتى وهي الاختبارات لا تزال تعمل — أو على وشك الفشل. ارتبط دائمًا بين المراحل بتبعيات صريحة حتى لا تُنتج أي قطعة أثرية من كود مكسور.

الصورة الكاملة — مخطط التبعية الكامل

CI Pipeline Dependency Graph with Parallel Jobs git push / PR Trigger lint ESLint · tsc · prettier build npm run build → artifact test:unit Jest / Vitest (parallel) test:integration Supertest + DB → package (Docker push)
مخطط تبعية خط الأنابيب الواقعي: الفحص والبناء متسلسلان؛ اختبارات الوحدة والتكامل تعمل بالتوازي بعد البناء؛ التحزيم يعمل فقط عند نجاح جميع مهام الاختبار.

تتبع الأنابيب الفعلية في شركات مثل Shopify وStripe وAirbnb نفس هيكل المخطط. التوازي في طبقة الاختبار هو حيث تسترد معظم الفرق وقتًا كبيرًا — لكن البوابة التسلسلية عند الفحص والبناء تضمن عدم إهدار الحوسبة على كود مكسور بشكل واضح.

قس خط أنابيبك. تتبع متوسط مدة خط الأنابيب ومعدل الفشل لكل مرحلة كمقاييس هندسية. إذا فشل الفحص أكثر من 5% من الوقت، فسير عمل المطور المحلي مكسور. إذا كانت الاختبارات السبب الأول للفشل، استثمر في اكتشاف الاختبارات المتقلبة. قِس قبل أن تُحسِّن.