GitLab CI
One job in the .post stage, gated on when: on_failure. It reads the pipeline it belongs to and writes its report as
artifacts.
The minimal job
Deterministic report, artifacts on every failure, no model required:
# runs only on failure; always exits 0.
ci-doctor:
stage: .post
image: registry.internal.example.com/ci-doctor:latest
rules:
- when: on_failure
allow_failure: true
variables:
CI_DOCTOR_GITLAB_TOKEN: "$CI_DOCTOR_TOKEN" # a read-scoped API token
script:
- ci-doctor analyze "$CI_PIPELINE_ID"
artifacts:
when: always
expire_in: 1 week
paths:
- report.md
- report.json
CI_PIPELINE_ID and CI_PROJECT_ID are predefined
by GitLab, so the only thing you supply is the token. It needs read_api — and api
only if you turn on merge-request notes below.
Add an LLM explanation
Point at any OpenAI-compatible endpoint (swap in place of the deterministic-only variables):
variables:
CI_DOCTOR_GITLAB_TOKEN: "$CI_DOCTOR_TOKEN"
CI_DOCTOR_LLM__MODEL: "qwen2.5-coder:32b"
CI_DOCTOR_LLM__API_BASE: "http://vllm.internal:8000/v1"
# CI_DOCTOR_LLM__API_KEY... uses api_key_env if your gateway needs one
Optional at every step: with no model the job still emits the full deterministic report. The other backends are on Configuration.
Post a note on the merge request
Idempotent — it updates its own previous comment instead of spamming. Gated on confidence ≥ medium.
variables:
CI_DOCTOR_GITLAB_TOKEN: "$CI_DOCTOR_TOKEN"
CI_DOCTOR_OUTPUT__MR_NOTE: "true" # post/update one note when confidence >= medium
Needs CI_MERGE_REQUEST_IID, which GitLab sets on
merge-request pipelines, and a token with api scope rather than
read_api.
Self-hosted GitLab
Override the API host and hand it your internal CA. Proxy variables are honoured as-is:
variables:
CI_DOCTOR_GITLAB_TOKEN: "$CI_DOCTOR_TOKEN"
CI_DOCTOR_GITLAB__BASE_URL: "https://gitlab.internal.example.com"
CI_DOCTOR_GITLAB__CA_BUNDLE: "/etc/ssl/certs/internal-ca.pem"
HTTPS_PROXY, NO_PROXY and REQUESTS_CA_BUNDLE behave the way you would expect. For runners with no egress at all, see
Local & air-gapped.
allow_failure: true is belt-and-braces.