Skip to content

Anleitungen

In CI ausführen

Gating in der CI beschreibt die Release-Policy und die Bedeutung jedes Exit-Codes. Diese Seite behandelt den Teil, der innerhalb eines CI-Jobs passiert: wie Sie Oloproof dort installieren, wo die Evidenz während des Jobs liegt, wie Sie ein Ergebnis lesen, ohne eine Tabelle zu parsen, und wie Sie einen Pull Request mit dem Branch vergleichen, auf den er zielt.

Der Exit-Code ist das Gate

oloproof run endet mit dem Ergebnis seines Gates. Ein CI-Schritt, der den Befehl ausführt, schlägt fehl, wenn das Gate blockiert. Das ist meist gewünscht und erfordert keine zusätzliche Verdrahtung:

CodeEin CI-Schritt sollte
0bestehen
1fehlschlagen: eine Regel ist gescheitert
2als defekter Build fehlschlagen: die Konfiguration war falsch, und nichts wurde gemessen
3fehlschlagen oder warnen: die Suite konnte nicht entscheiden
4fehlschlagen und an eine Person weiterleiten
5als defekter Build fehlschlagen: der Lauf wurde nicht abgeschlossen

Über Code 3 streiten Teams. Die Standard-Policy blockiert dabei, denn eine Suite, die zu klein ist, um zu entscheiden, hat nicht gezeigt, dass die Änderung sicher ist. Ein Team, das den Job grün sehen will, während es seine Suite ausbaut, kann das in der Policy festlegen, statt den Exit-Code zu ignorieren:

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

Derselbe gespeicherte Lauf von examples/support_bot/, der unter seiner eigenen Policy mit 3 blockiert, unter dieser hier:

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)

Er endet mit 0. Die Entscheidung ist unverändert. Nur die Release-Aktion hat sich verschoben, und zwar weil eine Datei, die dem Review unterliegt, es so festlegt.

Das Ergebnis als Daten lesen

--json schreibt ein JSON-Ereignis pro Zeile auf die Standardausgabe und die menschenlesbaren Tabellen auf die Standardfehlerausgabe, sodass ein CI-Log die Tabellen behält und ein Skript die Ereignisse liest. In run.ndjson gespeichert, steht die ID des Laufs in jeder Zeile, und die letzte Zeile enthält den Exit-Code:

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}

Fortschritt und Nebenläufigkeit listet jeden Ereignistyp auf.

Wo die Evidenz liegt

Ein Lauf wird in .oloproof/store.sqlite neben der oloproof.yaml gespeichert, aus der er gestartet wurde, und oloproof init trägt .oloproof/ in .gitignore ein. Ein CI-Job beginnt mit einem leeren Speicher, daher führt jeder Job jeden Fall erneut aus, und nichts aus einem Job ist für den nächsten sichtbar.

Das ist vor allem für einen Vergleich wichtig, der beide Läufe in einem Speicher benötigt. Zwei Checkouts eines Projekts erhalten jeweils ihren eigenen Speicher, sodass ein Baseline-Lauf aus dem einen im anderen nicht gefunden werden kann:

Configuration error: unknown run 'run_01M3C3XSX9KY5T8VFZXA1CVBES'

OLOPROOF_HOME richtet jeden Befehl auf einen einzigen Speicher aus, unabhängig davon, wo seine oloproof.yaml liegt. Das Verzeichnis, das die Variable angibt, enthält .oloproof/store.sqlite.

Oloproof in einem Job installieren

Oloproof ist in keinem Paketindex veröffentlicht, daher gibt es keine pip install-Zeile, die es über seinen Namen holt. Ein Job installiert es aus einem Checkout des Oloproof-Repositorys, genauso wie ein Entwickler: actions/checkout mit repository:, das auf den Ort verweist, an dem die Kopie dieses Repositorys Ihres Teams liegt, und einem token:, das es lesen kann, falls es privat ist. Das Wurzelverzeichnis des Repositorys baut das Paket, und das Paket stellt den Befehl oloproof bereit.

Einen Pull Request mit seiner Basis vergleichen

Checken Sie den Basis-Branch und den Pull Request nebeneinander aus, führen Sie beide in denselben Speicher aus und vergleichen Sie:

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 und OLOPROOF_REPO_TOKEN sind Platzhalter für Ihre Kopie des Repositorys und ein Secret, das es lesen kann. Die beiden Läufe tragen || true, weil ihre eigenen Gates hier nicht die Frage sind; der Exit-Code des Vergleichs ist es.

Dieselben Schritte auf einem Laptop, mit dem erzeugten Projekt zweimal ausgecheckt:

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)

Beide Läufe müssen über dieselbe Suite gehen. Ein Pull Request, der einen Fall bearbeitet, ändert den Digest der Suite, und der Vergleich verweigert sich, statt Fälle zu paaren, die nicht mehr derselbe Fall sind:

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

Ein Vergleich mit einem Lauf, der nicht abgeschlossen wurde, endet mit 5, genauso wie ein Gate über einen solchen Lauf: Es gibt kein gepaartes Ergebnis, über das entschieden werden könnte.

Wie es weitergeht

  • Gating in der CI behandelt die Policy und die Reihenfolge, in der Exit-Codes gemeldet werden.
  • Vergleichsregeln behandelt, was eine Vergleichs-Policy verlangen kann.