Skip to content

الأدلة الإرشادية

التشغيل في CI

تشرح صفحة تطبيق البوابة في CI سياسة الإصدار ومعنى كل رمز خروج. أما هذه الصفحة فهي الجزء الذي يحدث داخل مهمة CI: كيف تثبّت Oloproof هناك، وأين تقيم الأدلة أثناء تشغيل المهمة، وكيف تقرأ النتيجة دون تحليل جدول، وكيف تقارن طلب سحب بالفرع الذي يستهدفه.

رمز الخروج هو البوابة

يخرج oloproof run وفق بوابته. خطوة CI التي تشغّله تفشل حين تحجب البوابة، وهذا عادةً ما تريده ولا يحتاج إلى توصيل إضافي:

الرمزينبغي لخطوة CI أن
0تنجح
1تفشل: فشلت قاعدة
2تفشل بوصفها بناءً معطَّلًا: كان الإعداد خاطئًا ولم يُقَس شيء
3تفشل، أو تُحذِّر: لم تستطع مجموعة الاختبار أن تحسم
4تفشل وتُحال إلى شخص
5تفشل بوصفها بناءً معطَّلًا: لم يكتمل التشغيل

الرمز 3 هو الذي تتجادل الفرق حوله. السياسة الافتراضية تحجب عنده، لأن مجموعة الاختبار الأصغر من أن تحسم لم تُثبت أن التغيير آمن. والفريق الذي يريد أن تصبح المهمة خضراء بينما يوسّع مجموعة اختباره يمكنه أن يقول ذلك في السياسة، بدلًا من تجاهل رمز الخروج:

version: 1
block_on: [FAIL, MANUAL_REVIEW]
warn_on: [INSUFFICIENT_EVIDENCE]
rules:
  - id: exact-label-floor
    metric: exact_label
    min: 0.70

التشغيل المخزَّن نفسه لـ examples/support_bot/، الذي يحجب بالرمز 3 وفق سياسته الخاصة، وفق هذه السياسة:

oloproof gate RUN_ID --policy advisory.yaml
exact-label-floor: INSUFFICIENT_EVIDENCE (interval_overlaps_threshold)
  about 1614 more cases would decide it, if the observed rate holds (1632 in total)

يخرج بالرمز 0. القرار لم يتغير. لم يتغير إلا إجراء الإصدار، وقد تغير لأن ملفًا خاضعًا للمراجعة ينص على ذلك.

قراءة النتيجة بوصفها بيانات

يكتب --json حدث JSON واحدًا لكل سطر إلى المخرج القياسي، والجداول المقروءة للبشر إلى الخطأ القياسي، فيحتفظ سجل CI بالجداول ويقرأ السكربت الأحداث. عند حفظها في run.ndjson، يكون معرّف التشغيل في كل سطر، ويحمل السطر الأخير رمز الخروج:

jq -r 'select(.type == "run_started") | .run_id' run.ndjson
jq -c 'select(.type == "run_finished")' run.ndjson
run_01M3C3WS0SBTFAG55M7ECM1EZ4
{"run_id":"run_01M3C3WS0SBTFAG55M7ECM1EZ4","timestamp":"2026-09-25T11:06:03.646415Z","type":"run_finished","status":"DECIDED","completeness":"COMPLETE","exit_code":3}

تسرد صفحة التقدُّم والتزامن كل أنواع الأحداث.

أين تقيم الأدلة

يُخزَّن التشغيل في .oloproof/store.sqlite بجوار oloproof.yaml الذي شُغِّل منه، ويضع oloproof init المجلد .oloproof/ في .gitignore. تبدأ مهمة CI بمخزن فارغ، لذا تعيد كل مهمة تنفيذ كل حالة، ولا يظهر شيء من مهمة للمهمة التالية.

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

Configuration error: unknown run 'run_01M3C3XSX9KY5T8VFZXA1CVBES'

يوجّه OLOPROOF_HOME كل أمر إلى مخزن واحد، أينما كان oloproof.yaml الخاص به. والمجلد الذي يسمّيه يحتوي على .oloproof/store.sqlite.

تثبيت Oloproof في مهمة

لا يُنشر Oloproof في فهرس حزم، لذا لا يوجد سطر pip install يجلبه بالاسم. تثبّته المهمة من نسخة مسحوبة من مستودع Oloproof، بالطريقة نفسها التي يتبعها المطوّر: actions/checkout مع repository: يسمّي الموضع الذي توجد فيه نسخة فريقك من ذلك المستودع، وtoken: قادر على قراءته إن كان خاصًا. جذر المستودع يبني الحزمة، والحزمة توفّر الأمر oloproof.

مقارنة طلب سحب بقاعدته

اسحب الفرع الأساسي وطلب السحب جنبًا إلى جنب، وشغّل كلًّا منهما في المخزن نفسه، ثم قارن:

name: oloproof
on: pull_request
jobs:
  evaluate:
    runs-on: ubuntu-latest
    env:
      OLOPROOF_HOME: ${{ github.workspace }}/evidence
    steps:
      - uses: actions/checkout@v4
        with:
          path: pr
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.base_ref }}
          path: main
      - uses: actions/checkout@v4
        with:
          repository: YOUR_ORG/Oloproof
          token: ${{ secrets.OLOPROOF_REPO_TOKEN }}
          path: oloproof-src
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: python -m pip install ./oloproof-src
      - run: mkdir -p "$OLOPROOF_HOME"
      - run: oloproof run --config main/oloproof.yaml --json > base.ndjson || true
      - run: oloproof run --config pr/oloproof.yaml --json > cand.ndjson || true
      - name: compare
        run: |
          candidate=$(jq -r 'select(.type == "run_started") | .run_id' cand.ndjson)
          baseline=$(jq -r 'select(.type == "run_started") | .run_id' base.ndjson)
          oloproof compare "$candidate" "$baseline" --config pr/oloproof.yaml --policy pr/compare.yaml

YOUR_ORG/Oloproof وOLOPROOF_REPO_TOKEN عنصران نائبان عن نسختك من المستودع وعن سرّ قادر على قراءته. يحمل التشغيلان || true لأن بوابتيهما ليستا موضع السؤال هنا؛ بل رمز خروج المقارنة هو موضعه.

الخطوات نفسها على حاسوب محمول، مع سحب المشروع المُنشأ في الهيكل الأولي مرتين:

Comparison sha256:31ac104779bda1726ba55b661107cbe256fae5f1d831c79b1283a07651b58cbf of run_01M3C3XX4TM0WSZW8K81PHRGAW against run_01M3C3XW7X64B0Z0ACZV27WSW6 · 30 paired cases
exact_label: +0.0 points [-16.5, +16.5] · 30 paired · 0 missing · 0 excluded
Decisions
  no-regression  exact_label  non-inferiority, margin 5.0 points  INSUFFICIENT_EVIDENCE  interval_overlaps_margin
Gate: BLOCK (exit 3)

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

Configuration error: runs 'run_01M3C3XXVZ1X0J2PR1ZW1WVGVZ' and 'run_01M3C3XW7X64B0Z0ACZV27WSW6' used different suites (sha256:baff4f101901d9a37cd440f99b9a70032f9488891b4f590f18a81017c26ba794 and sha256:2011286a7ec00c8c31560bb6d037b54a501a15f6251ac7c2d578d0c40e226d72); comparisons pair scenario by scenario over one suite

مقارنة تشغيل لم ينتهِ تخرج بالرمز 5، تمامًا كالبوابة على تشغيل كهذا: لا توجد نتيجة مقترنة يُتخذ القرار بناءً عليها.

إلى أين بعد ذلك