CI
Run Dino in CI: the GitHub Action, pinning, credentials, failing a build, partial coverage, reading the result, breaking changes, and the same pattern for any CI system.
Run Dino after the API you're verifying is deployed: it tests the running API, not your source code. It returns an exit code your pipeline branches on and a DinoResult document you can keep.
GitHub Actions
name: API verification
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
dino:
runs-on: ubuntu-latest
steps:
- uses: Dino-HQ/dino/.github/actions/scan@v1
with:
api-url: ${{ secrets.API_URL }}
api-token: ${{ secrets.API_TOKEN }} # optional
cli-version: 1.1.5
fail-on-high: true- Pin the CLI.
@v1takes backward-compatible fixes to the action and never a breaking change (that would bev2).cli-versionpins the CLI, so CLI upgrades are deliberate. Don't use@main, which moves with every change. For a hardened workflow, pin the action to a full commit SHA instead of@v1. - Credentials.
api-tokenis the action's input for a bearer token. Pass it from a repository secret; the action sends it asAuthorization: Bearer …and never writes it to disk. - The result. The report is uploaded as the
dino-scan-reportartifact, and the step'sexit-codeoutput carries Dino's exit code.
| Input | Default | What it does |
|---|---|---|
api-url | required | The endpoint to verify |
protocol | graphql | graphql or rest |
spec-url | OpenAPI URL or path, required for rest | |
api-token | Bearer token for the target API | |
fail-on-high | false | Fail (exit 3) on any HIGH or CRITICAL finding |
accept-partial | false | Treat partial coverage as success instead of exit 6 |
fail-on-breaking | false | Also fail (exit 3) on breaking schema changes; see Breaking changes |
format | markdown | Report format: markdown or json |
cli-version | latest | CLI version to install |
Failing a build
The exit code is the signal. Nothing needs to parse the report.
| Exit | Meaning in CI |
|---|---|
0 | Clean, or findings below your gate: pass |
3 | A gate failed (--fail-on-high, --fail-on-breaking): do not ship |
6 | Partial coverage: some checks couldn't complete, so this is not a pass |
4 | Transient (network, rate limit): retry |
2, 5 | Usage or config error: fix the input |
70 | Dino crashed: report it |
Partial coverage means Dino couldn't verify everything it planned, for example because introspection was limited or a check couldn't run. The verdict is honest about it: verdict.coverage is "partial" and verdict.reasons says why. Decide explicitly whether that's acceptable. accept-partial (or --accept-partial) turns 6 into 0; it never hides a failed gate. The full table and precedence: Machine contracts.
Breaking changes
fail-on-breaking runs dino diff, which compares the API with the snapshot saved by the previous run. On a fresh CI runner there is no previous run, so the first build only saves a baseline. Keep the .dino/snapshots folder between runs with a cache:
- uses: actions/cache@v4
with:
path: .dino/snapshots
key: dino-snapshots-${{ github.ref_name }}-${{ github.run_id }}
restore-keys: dino-snapshots-${{ github.ref_name }}-
- uses: Dino-HQ/dino/.github/actions/scan@v1
with:
api-url: ${{ secrets.API_URL }}
cli-version: 1.1.5
fail-on-breaking: trueAny other CI
The same three steps work anywhere with a shell: install a pinned version, run the scan, branch on the exit code.
bash -o pipefail -c 'curl -fsSL https://usedino.dev/install.sh | sh -s 1.1.5'
export PATH="$HOME/.local/bin:$PATH"
dino scan --endpoint "$API_URL" --fail-on-high --format json > dino-result.json
code=$? # capture it before any other command runs
echo "Dino exit code: $code"
exit "$code"- Credentials. Keep secrets in your CI's secret store and pass them at run time:
--token "$API_TOKEN"or--header "X-Api-Key: $API_KEY"on the command line, or commit a.dino.ymlwhoseauthnames the environment variables to read (valueEnv,clientIdEnv,clientSecretEnv). Dino never writes a secret to the file. See Configuration. - The result.
dino-result.jsonis theDinoResultdocument: keep it as a build artifact, or read the verdict withjq '.verdict'. Errors (exit2,4,5,70) put a JSON envelope on the last line of stderr instead. See Machine contracts. - No install. With Node.js 22+ on the runner you can skip installing:
npx -y @dino-hq/cli@1.1.5 scan …. - Breaking changes. Run
dino diff --endpoint "$API_URL" --fail-on-breakingbeforedino scanand keep.dino/snapshotsbetween runs with your CI's cache.diffcompares with the latest saved snapshot andscansaves a new one, so adiffafter thescanwould compare the API with itself. The GitHub Action already runs them in this order.