تصحيح إخفاقات خط الأنابيب
تصحيح إخفاقات خط الأنابيب (Debugging Pipeline Failures) يحوّل عبارة «البناء أحمر» إلى إخفاق مفهوم: أي مهمة (job) وأي خطوة وأي اختبار فشل، وما الخطأ الأول، وهل يتكرر على الإيداع (commit) نفسه، وأي تغيير سبّبه. وقد طبّق درس «تقارير الأتمتة وتصحيح الأخطاء» دورة تصحيح الأخطاء (Debugging) في منهج ISTQB، أي إعادة إنتاج الإخفاق وتشخيص سببه ثم إصلاحه، على اختبار فاشل واحد؛ أما على مستوى خط الأنابيب فيصبح الإيداع والمشغّل (runner) وتاريخ التشغيلات جزءاً من الأدلة. وكثيراً ما يقود المختبِر إعادة الإنتاج والتشخيص الأولي، ثم يسلّم المطور الأدلة.
مثال: يفشل تشغيل ليلي للاختبار الشامل (end-to-end) في جزء واحد من أربعة أجزاء. يسمّي السجل الاختبار، ويُظهر التتبّع البيانات التي استلمتها الصفحة، ويفشل الاختبار على حاسوبك حين يستخدم المتصفح المنطقة الزمنية للمشغّل، ثم يحدد التنصيف (bisect) الإيداع الذي أفسد تنسيق التاريخ.
لماذا يهم
خط الأنابيب الأحمر يوقف كل دمج وكل إصدار خلفه، والتصنيف الخاطئ يكلّف أكثر من التصحيح البطيء. فوصف عيب في المنتج بأنه «متذبذب» يتركه يصل إلى المستخدمين، ووصف مشكلة بيئة بأنها عيب في المنتج يرسل المطورين للبحث عن عيب ليس في الشيفرة. وإعادة التشغيل حتى يخضرّ تفعل الأمرين معاً: تُخفي الأدلة وتعلّم الفريق أن الأحمر لا يعني شيئاً.
وبالنسبة إلى المختبِر، هنا تُكتسب الثقة. فالتقرير الذي يسمّي الاختبار الفاشل والخطأ الأول والمخرجات وأمراً لإعادة الإنتاج وأول إيداع معيب يُصلَح سريعاً؛ أما «خط الأنابيب أحمر، هل يمكن لأحد أن ينظر فيه؟» فلا.
ابدأ بالسجل والمخرجات
كما في درس «تقارير الأتمتة وتصحيح الأخطاء»، ابدأ من الخطأ الأول وأدلته؛ وفي خط الأنابيب يعني ذلك أن تحدّد أولاً أول مهمة فاشلة وأول خطوة فاشلة داخلها. ابحث في السجل عن اسم الاختبار أو عن علامة الخطأ (##[error] في السجل الخام لـGitHub Actions) بدلاً من التمرير، وتذكّر أن الطوابع الزمنية في المشغّلات تكون عادةً بتوقيت UTC.
ثم نزّل مخرجات المهمة (artifacts) قبل انتهاء فترة الاحتفاظ بها، مثل تقرير JUnit XML وتتبّع Playwright (trace) وسجلات التطبيق، واقرأها كما بيّن ذلك الدرس. وقارنها بآخر تشغيل أخضر للمهمة: صورة مشغّل جديدة، أو اعتمادية متغيرة، أو عدد أقل من الاختبارات المنفّذة، كلها أدلة قبل أن تقرأ سطراً من الشيفرة.
# GitHub CLI: logs of the failed steps only, then the artifacts of one job
gh run view 9876543 --log-failed
gh run download 9876543 -n e2e-results-3
# open the trace recorded for the failing test
npx playwright show-trace test-results/invoice-order-date/trace.zipأعد إنتاج الإخفاق محلياً
الإخفاق الذي تستطيع إعادة إنتاجه على جهازك يمكن فحصه بمصحّح الأخطاء (debugger) وسجلات إضافية وبعدد التشغيلات الذي تحتاجه. وإعادة الإنتاج مسألة تطابق: طابِق مهمة CI في كل ما قد يغيّر النتيجة، ثم غيّر شيئاً واحداً في كل مرة. وإذا بقي الاختبار ناجحاً محلياً، فالفرق المتبقي هو أفضل دليل لديك.
- الإيداع: رمز SHA الدقيق من سجل المهمة. فتشغيل طلب الدمج في GitHub Actions يختبر دمجاً مؤقتاً في الفرع الهدف، لا آخر إيداع في فرعك؛ وهذا الإيداع لا ينتمي إلى أي فرع، فاجلبه بالأمر
git fetch origin pull/<number>/mergeوتأكد أن رمز SHA يطابق السجل. - الصورة والإصدارات: صورة الحاوية (container image) التي تستخدمها المهمة إن وُجدت، والاعتماديات من ملف القفل (
npm ciلاnpm install)، وإصدارات المتصفحات التي يثبّتها إصدار Playwright لديك. - متغيرات البيئة: القيم غير السرية التي تضبطها المهمة، ومنها
CI=trueالذي تقرؤه إعدادات اختبار كثيرة؛ وبيانات اعتماد اختبار خاصة بك، لا أسرار بيئة الإنتاج أبداً. - المنطقة الزمنية واللغة والبذرة: تعمل المشغّلات غالباً بتوقيت UTC وبلغة إنجليزية، والاختبارات التي تولّد بيانات عشوائية يجب أن تطبع بذرتها (seed) ليمكن تكرار البيانات نفسها.
- المجموعة والترتيب: الجزء (shard) نفسه وعدد العمّال (workers) نفسه، ثم الاختبار الفاشل وحده ومكرراً، لتميّز «يفشل وحده» من «يفشل فقط بجوار اختبارات أخرى».
git checkout 9f8e7d6 # the SHA the job tested
npm ci && npx playwright install --with-deps chromium
export CI=true TEST_SEED=48151 # non-secret values copied from the job
npx playwright test --shard=3/4 --workers=2
npx playwright test invoice.spec.ts --repeat-each=10المهلات والإيقاف عند أول إخفاق وإعادة التشغيل
انتهاء المهلة (timeout) يعني أن شيئاً كان الاختبار ينتظره لم يحدث: عنصر، أو استجابة، أو خدمة. ونادراً ما يُصلحه رفع الحد؛ فسجل الاستدعاءات أو التتبّع يبيّن ما الذي كان منتظراً. ورتّب الحدود في طبقات: مهلة الاختبار (30 ثانية افتراضياً في Playwright) تُفشل اختباراً واحداً مسمّى ويُكتب التقرير مع ذلك، أما مهلة المهمة (timeout-minutes في GitHub Actions، و360 دقيقة إن لم تُضبط) فتلغي المهمة كلها وقد لا تترك تقريراً. لذا اجعل مهلات الاختبارات أقل بكثير من مهلة المهمة.
الإيقاف عند أول إخفاق (fail-fast) يتوقف عند الإخفاق الأول (في Playwright بالخيار -x، وهو اختصار --max-failures=1)، وتشغيل الكل (run-all) يجمع كل الإخفاقات. استخدم الأول في المراحل المبكرة الرخيصة مثل التدقيق الساكن (linting) واختبارات الوحدة. أما في المراحل المكلفة والتشغيلات الليلية فشغّل كل شيء: فاختبار واحد فاشل وأربعون اختباراً فاشلاً يرويان قصتين مختلفتين، والأربعون تشير عادةً إلى سبب مشترك كالبيئة. والمصفوفة (matrix) في GitHub Actions تلغي مهامها الأخرى عند فشل إحداها ما لم تضبط fail-fast: false.
أعد التشغيل بأضيق نطاق ممكن: المهام الفاشلة فقط (gh run rerun <run-id> --failed، ومعرّف التشغيل هو الرقم الطويل في رابط التشغيل لا رقمه المسبوق بـ#) أو الاختبار الفاشل فقط. وإعادة التشغيل في GitHub Actions تستخدم الإيداع نفسه، فالنجاح عندها يثبت اللاحتمية (non-determinism) لا الإصلاح؛ فسجّله دليلاً.
وخلاصة إعادة المحاولة والحجر: إعادة المحاولة الآلية (retry) تجعل التشغيل أخضر حين ينجح اختبار متذبذب في محاولة لاحقة، والحجر (quarantine) ينقل اختباراً معروف التذبذب إلى مهمة لا تمنع الدمج يستمر فيها بالعمل. وكلاهما احتواء لا علاج: اسمح بإعادة محاولة واحدة على الأكثر، وأبلغ عن «نجح عند إعادة المحاولة» على أنه تذبذب، وأعطِ كل اختبار محجور مالكاً وتذكرة وموعداً نهائياً. ويشرح دليل الاختبارات المتذبذبة هذه السياسة، ويبيّن مسار الفرز في دليل الاختبار في خطوط CI/CD موضع إعادة التشغيل.
jobs:
e2e:
runs-on: ubuntu-latest
timeout-minutes: 30 # job limit: cuts off a hang
strategy:
fail-fast: false # one failing shard does not cancel the others
matrix:
shard: [1, 2, 3, 4]
steps:
# checkout and install steps omitted
- run: npx playwright test --shard=${{ matrix.shard }}/4تنصيف الانحدار عبر تشغيلات خط الأنابيب
حين يتحول تشغيل مجدول على الفرع الرئيسي إلى الأحمر بعد دمج تغييرات كثيرة، يعطيك سجل خط الأنابيب آخر تشغيل أخضر وأول تشغيل أحمر، ولكل منهما إيداعه. افحص أولاً تاريخ الاختبار في تقارير الاختبار في CI: فالاختبار الذي يفشل من حين لآخر منذ أسابيع مرشح للتذبذب لا انحدار جديد. وإن كان يفشل في كل مرة ابتداءً من تشغيل معيّن، فالبحث الثنائي يجد التغيير: اختبر الإيداع الأوسط، واحتفظ بالنصف الذي يتحول فيه النجاح إلى فشل، وكرّر. ستة عشر إيداعاً مرشحاً تحتاج إلى أربعة تشغيلات اختبار على الأكثر.
ويؤتمت git bisect run هذا البحث: ينتهي الأمر الذي تعطيه إياه بالرمز 0 للإيداع السليم، و125 لتخطّي إيداع لا يمكن اختباره، وبرمز آخر من 1 إلى 127 للإيداع المعيب. ولا تثق بالنتيجة إلا بشرطين: أن يفشل الاختبار في كل مرة على الإيداع المعيب، لذا كرّره في كل خطوة، وأن تبقى البيئة ثابتة. فإن فشل آخر إيداع أخضر الآن أيضاً، فالسبب خارج الشيفرة، كصورة المشغّل أو خدمة خارجية أو بيانات اختبار منتهية الصلاحية.
# last green nightly: a1b2c3d first red nightly: 9f8e7d6
git bisect start 9f8e7d6 a1b2c3d
# TZ=UTC gives the browser the runner's time zone (Linux and macOS)
git bisect run sh -c 'npm ci || exit 125
TZ=UTC npx playwright test -g "order date" --repeat-each=3'
# git prints "<sha> is the first bad commit"
git bisect resetمثال تطبيقي: تشغيل ليلي أحمر
التشغيل الليلي 412 على الفرع الرئيسي أحمر، والتشغيل 411 كان أخضر، وبينهما تسعة إيداعات. الإيقاف عند أول إخفاق معطّل، فاكتملت الأجزاء الأربعة، وفي الجزء 3 إخفاق واحد في invoice.spec.ts، في الاختبار «invoice shows the order date»: المتوقع 1 Oct 2026 والمستلم 30 Sep 2026. وقد فشل في محاولته الأولى وفي إعادة المحاولة، فيعدّه التقرير فاشلاً لا متذبذباً. ويُظهر التتبّع أن الواجهة البرمجية أعادت createdAt بالقيمة 2026-09-30T22:40:00Z، أي الساعة 01:40 يوم 1 أكتوبر في الرياض، وهي المنطقة الزمنية للمتجر: البيانات صحيحة والعرض خاطئ.
وعلى حاسوب المختبِر في الرياض ينجح الاختبار. ومع الإيداع وملف القفل وإصدار المتصفح والبذرة نفسها التي استخدمتها المهمة، يبقى فرق واحد: متصفح المشغّل يعمل بتوقيت UTC. وعند ضبط خيار timezoneId في Playwright على UTC يفشل الاختبار 10 مرات من 10. ومع إضافة TZ=UTC إلى أمر التنصيف ليعمل كل إيداع بالمنطقة الزمنية للمشغّل، يحدد git bisect run الإيداعَ c41d9a0 بعنوان «Format dates with Intl.DateTimeFormat»، الذي حذف المنطقة الزمنية للمتجر. فيسجّل المختبِر عيباً في المنتج مع رابط التشغيل والاختبار والتتبّع وإعداد إعادة الإنتاج وأول إيداع معيب، ويقترح تثبيت timezoneId في إعدادات الاختبار ليتصرف التشغيل المحلي مثل CI.
| الإعداد | CI: التشغيل الليلي 412، الجزء 3 | الحاسوب: التشغيل الأول | الحاسوب: بعد مطابقة المنطقة الزمنية |
|---|---|---|---|
| الإيداع المختبَر | 9f8e7d6 | 9f8e7d6 | 9f8e7d6 |
| الاعتماديات | ملف القفل، npm ci | ملف القفل، npm ci | ملف القفل، npm ci |
| إصدار المتصفح | Chromium الذي يثبّته Playwright | Chromium الذي يثبّته Playwright | Chromium الذي يثبّته Playwright |
| بذرة بيانات الاختبار | 48151 | 48151 | 48151 |
| المنطقة الزمنية للمتصفح | UTC | Asia/Riyadh (UTC+3) | UTCبضبط timezoneId |
| اختبار تاريخ الفاتورة | يعرض 30 Sep 2026فشل | يعرض 1 Oct 2026نجاح | يعرض 30 Sep 2026 في 10 تشغيلات من 10فشل |
- يختلف عن مهمة CI، وهذا هو الدليل
أخطاء شائعة
- ترك مخرجات المهمة تنتهي صلاحيتها قبل تنزيلها، فيضيع التتبّع والتقارير لإخفاق لم تستطع إعادة إنتاجه.
- إعادة تشغيل سير العمل (workflow) كله بدلاً من المهام الفاشلة وحدها، فتستهلك وقت المشغّلات وتكرّر مهاماً نجحت من قبل.
- إعادة الإنتاج على إيداع مختلف، كآخر إيداع في فرعك بدلاً من إيداع الدمج الذي اختبره CI، أو باستخدام
npm installبدلاً من ملف القفل. - تمرير رقم التشغيل المسبوق بـ
#إلىgh run rerunبدلاً من معرّف التشغيل الموجود في رابطه، فلا يستهدف الأمر التشغيل الذي قصدته. - إبقاء الإيقاف عند أول إخفاق مفعّلاً في مصفوفة مكلفة، فيلغي إخفاقٌ واحد الأجزاءَ التي كنت تحتاج نتائجها للفرز.
- تنصيف اختبار متذبذب أو بيئة تغيّرت: عندها يشير البحث إلى إيداع بريء.
من منظور ضمان الجودة
اتفق مع الفريق على من ينظر في خط الأنابيب الأحمر وبأي سرعة؛ فبعض الفرق تتناوب على هذا الدور، وأحياناً يتولاه مختبِر ومطور معاً. ابدأ من رابط التشغيل ودوّن ما تجده أولاً بأول: المهمة والخطوة والخطأ الأول والمخرجات وأمر إعادة الإنتاج ونتيجة إعادة التشغيل والتصنيف.
واطلب ما يجعل الإخفاق التالي أرخص: رفع المخرجات حتى عند فشل الاختبارات، وطباعة البذور والإصدارات في السجل، ومهلات اختبار أقل من مهلة المهمة. واحتفظ بسجل الإخفاقات الذي يوصي به دليل الاختبار في خطوط CI/CD؛ فخلال أسابيع قليلة يبيّن ما إذا كانت الغلبة لعيوب المنتج أو للاختبارات المتذبذبة أو للبيئات، وهذا هو الدليل الذي تحتاجه ليُصلَح خط الأنابيب.
تمرين عملي
افرز مهمة انتهت مهلتها
خط أنابيب طلب دمج أحمر. اقرأ ملخص التشغيل والسجل أدناه، ثم صنّف الإخفاق، وسمِّ الأدلة الناقصة، وخطّط لإعادة الإنتاج، واذكر ما الذي ستغيّره في خط الأنابيب ليترك الإخفاق التالي أدلة أفضل.
Pull request #214, workflow "ci", run #7731 (run ID 11407731 in its URL)
Commit 5be20d7 (merge of feature/refunds into main)
unit-tests passed 3m 10s
api-tests failed 20m 00s
e2e (1/2) skipped needs: api-tests
e2e (2/2) skipped needs: api-tests
api-tests log, last lines (UTC):
12:04:31 PASS orders.create > returns 201 for a valid order (0.8s)
12:04:32 RUN refunds.create > refunds a captured payment
12:23:58 ##[error]The job running on runner ci-runner-07 has exceeded the
maximum execution time of 20 minutes.
12:23:58 ##[error]The operation was canceled.
Artifacts: unit-reports (api-reports missing)
api-tests on main, last two runs: passed, passed- حدّد المهمة الفاشلة والخطوة وآخر اختبار بدأ.
- صنّف الإخفاق بقدر ما تسمح الأدلة، واذكر ما تخبرك به المهام المتخطّاة.
- اشرح لماذا لا يوجد تقرير لاختبارات API، وكيف تحصل عليه في المرة القادمة.
- اكتب كيف ستعيد إنتاج الإخفاق وكيف ستعيد التشغيل.
إظهار الإجابة النموذجية
تعلّقت خطوة الاختبار في المهمة api-tests: بدأ refunds.create > refunds a captured payment عند 12:04:32 ولم يُبلغ عن نتيجة؛ وبعد نحو 19 دقيقة ألغت مهلة المهمة البالغة 20 دقيقة المهمة. فهذا تعليق (hang) لا إخفاق في تأكيد (assertion). وقد تُخطّيت مهمتا e2e لأنهما تحتاجان إلى api-tests، فلا تقولان شيئاً عن المنتج.
ما زال التصنيف مفتوحاً: عيب في المنتج (استدعاء الاسترداد لا يعود أبداً)، أو مشكلة بيئة (بديل بوابة الدفع، أو قفل في قاعدة البيانات)، أو تعليق متذبذب. والمهمة خضراء على الفرع الرئيسي، وطلب الدمج يغيّر الاسترداد، فالتغيير هو المشتبه الأول، لكن إعادة الإنتاج وحدها تحسم.
أُلغيت المهمة قبل أن يكتب مشغّل الاختبار تقريره. اضبط مهلة لكل اختبار أقل بكثير من مهلة المهمة (60 ثانية مثلاً) ومهلات للعميل في استدعاءات HTTP داخل الاختبارات، ليُفشل التعليقُ اختباراً واحداً مسمّى مع تقرير؛ وارفع التقارير في خطوة تعمل حتى عند فشل الاختبارات؛ وسجّل معرّفات الطلبات وأوقاتها لتطابقها مع سجلات الخادم.
أعد تشغيل المهمة الفاشلة وحدها مرة واحدة (gh run rerun 11407731 --failed، بمعرّف التشغيل لا بالرقم #7731): النجاح يجعلها مرشحة للتذبذب لكنه لا يثبت أن التغيير آمن. ومحلياً، اجلب إيداع الدمج (git fetch origin pull/214/merge)، وانتقل إلى 5be20d7، وشغّل npm ci، وابدأ خدمات المهمة، وشغّل refunds.create وحده بالمهلة القصيرة، واقرأ سجلات التطبيق والبديل أثناء التعليق. ولا حاجة إلى التنصيف: التغيير المرشح الوحيد هو طلب الدمج هذا.
أسئلة شائعة
هل أعيد تشغيل المهمة الفاشلة قبل التحقيق؟
اقرأ الخطأ الأول ونزّل المخرجات أولاً، ثم أعد التشغيل مرة واحدة على الإيداع نفسه. تخبرك الإعادة هل الإخفاق حتمي؛ فهي دليل للفرز لا إصلاح، والنجاح فيها لا يثبت أن التغيير آمن.
ماذا لو لم أستطع إعادة إنتاج الإخفاق محلياً؟
أغلق الفروق المتبقية واحداً تلو الآخر: صورة الحاوية، وإصدار المتصفح، ومتغيرات البيئة، والمنطقة الزمنية، والبذرة، والجزء وعدد العمّال. وإن بقي الاختبار ناجحاً، فاجمع أدلة أكثر داخل CI: تتبّعات في كل محاولة، وسجلات أكثر، وإعادة تشغيل مع سجلات التصحيح (debug logging)، وتكرار الاختبار هناك لتقيس كم مرة يفشل.
متى يستحق التنصيف الجهد؟
حين يكون الإخفاق حتمياً وتفصل عدة إيداعات بين آخر تشغيل أخضر وأول تشغيل أحمر، وهذا معتاد في التشغيلات المجدولة على الفرع الرئيسي. أما في طلب دمج واحد فالتغيير نفسه هو المشتبه به، ومع اختبار متذبذب أو بيئة تغيّرت يشير التنصيف إلى الإيداع الخطأ.
المراجع
- ISTQB Certified Tester Foundation Level (CTFL) v4.0المنهج التأسيسي لاختبار البرمجيات: مستويات الاختبار وأنواعه وتقنيات تصميم الاختبارات وإدارة الاختبار والأدوات.
- GitHub Actions documentationكيف تُعرَّف مسارات العمل والمهام والمشغّلات والمخرجات في GitHub Actions — حيث تعمل الاختبارات الآلية عند كل دفع أو طلب دمج.
- Reference — Gitالمرجع الرسمي لكل أوامر Git وخياراتها، مجمّعةً حسب المهمة — الإعداد والتفريع والمشاركة وتطبيق الرقع والفحص وتتبّع الأخطاء.
- Playwright Documentationالتوثيق الرسمي لـ Playwright، إطار الاختبار الشامل الذي يشغّل Chromium وWebKit وFirefox.
تدرّب بأدوات متقن
قارن نصّين سطراً بسطر أو كلمةً بكلمة مع إبراز الاختلافات.
تنسيق XML بشكل أنيق مع مسافة بادئة قابلة للتخصيص، وإعادة تصغيره عند الحاجة.
حوّل التاريخ والوقت بين المناطق الزمنية (الرياض، عمّان، دلهي، UTC، نيويورك، لندن…).
تنسيق YAML والتحقق من صحته بمسافات بادئة وعلامات اقتباس متسقة.
للتعمّق أكثر
تقدّمك في هذا الموضوع
سجّل الدخول لتتابع تقدّمك
علّم الموضوعات مكتملة، وتابع من حيث توقفت، وافتح امتحانات التخصص.