This document defines the provider-neutral CI path for ota.
Use this when the runner is not GitHub Actions, or when you want the generic shell shape that can be translated into GitLab CI, Jenkins, CircleCI, Buildkite, or another pipeline system without changing the ota contract.
The provider-neutral path keeps the pipeline honest:
ota validate --jsonchecks the contractota doctor --jsongives machine-readable readiness findingsota annotations --format plainturns those findings into portable log linesota receipt --json --archiveproduces the durable readiness artifact
That separates:
- feedback and blocking logic
- portable log rendering
- archived receipt output
Use this path when:
- the CI runner is GitLab CI, Jenkins, CircleCI, Buildkite, or another non-GitHub provider
- the team wants one copyable shell shape across multiple runners
- the pipeline needs durable receipt artifacts without provider-specific wrapper code
- use
ota doctor --jsonas the readiness feedback surface - use
ota annotations --mode doctor --format plain --input ...as the portable log adapter - use
ota receipt --json --archivewhen you want the same readiness scan packaged as a durable artifact
Do not treat receipt as the annotation source. ota annotations currently consumes doctor-style
JSON, not receipt JSON.
The generic CI shape is:
#!/usr/bin/env bash
set -uo pipefail
mkdir -p .ota/ci
ota validate --json . | tee .ota/ci/validate.json
validate_status=${PIPESTATUS[0]}
ota doctor --json . | tee .ota/ci/doctor.json
doctor_status=${PIPESTATUS[0]}
ota annotations --mode doctor --format plain --input .ota/ci/doctor.json \
| tee .ota/ci/annotations.log
ota receipt --json --archive . | tee .ota/ci/receipt.json
receipt_status=${PIPESTATUS[0]}
if [ "${validate_status}" -ne 0 ] || [ "${doctor_status}" -ne 0 ] || [ "${receipt_status}" -ne 0 ]; then
exit 1
fiUse this shape when the provider can still upload artifacts after the shell step. If the provider
stops the job immediately on a non-zero command and skips artifact upload, capture the statuses,
upload .ota/ci/ and .ota/receipts/, and fail the job in a final step after the artifacts are
persisted.
Once a repo has one accepted archived receipt, keep later compare gates on the same receipt surface:
ota receipt --json --archive --promote-baseline .
ota receipt --json --baseline promoted . | tee .ota/ci/receipt-diff.json
diff_status=${PIPESTATUS[0]}Use promoted when the team wants an explicit accepted repo state owned by the repo itself. Use
latest when the newest archived receipt is enough for a lighter local or branch-level compare.
If the pipeline needs a PR or step-summary rendering for the compare result, reuse:
ota annotations --mode receipt-diff --format markdown --input .ota/ci/receipt-diff.jsonFor direct ota commands in CI:
- install ota in every job that executes ota directly
- ensure the install directory is on
PATHin that same job before later steps run
The current installer defaults to ~/.local/bin on Unix-like runners.
- GitLab CI: use
artifacts:when: alwaysso blocked readiness jobs still keep.ota/ci/and.ota/receipts/ - Jenkins: archive
.ota/ci/**and.ota/receipts/**inpost { always { ... } } - CircleCI: store
.ota/ciand.ota/receiptsas artifacts before the final fail step
If the runner is GitHub Actions and you want GitHub-native summaries, annotations, pull-request comments, and uploaded receipt artifacts, prefer the official wrapper:
The generic CI path stays the canonical cross-provider shape underneath that wrapper.