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. @v1 takes backward-compatible fixes to the action and never a breaking change (that would be v2). cli-version pins 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-token is the action's input for a bearer token. Pass it from a repository secret; the action sends it as Authorization: Bearer … and never writes it to disk.
  • The result. The report is uploaded as the dino-scan-report artifact, and the step's exit-code output carries Dino's exit code.
InputDefaultWhat it does
api-urlrequiredThe endpoint to verify
protocolgraphqlgraphql or rest
spec-urlOpenAPI URL or path, required for rest
api-tokenBearer token for the target API
fail-on-highfalseFail (exit 3) on any HIGH or CRITICAL finding
accept-partialfalseTreat partial coverage as success instead of exit 6
fail-on-breakingfalseAlso fail (exit 3) on breaking schema changes; see Breaking changes
formatmarkdownReport format: markdown or json
cli-versionlatestCLI version to install

Failing a build

The exit code is the signal. Nothing needs to parse the report.

ExitMeaning in CI
0Clean, or findings below your gate: pass
3A gate failed (--fail-on-high, --fail-on-breaking): do not ship
6Partial coverage: some checks couldn't complete, so this is not a pass
4Transient (network, rate limit): retry
2, 5Usage or config error: fix the input
70Dino 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: true

Any 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.yml whose auth names the environment variables to read (valueEnv, clientIdEnv, clientSecretEnv). Dino never writes a secret to the file. See Configuration.
  • The result. dino-result.json is the DinoResult document: keep it as a build artifact, or read the verdict with jq '.verdict'. Errors (exit 2, 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-breaking before dino scan and keep .dino/snapshots between runs with your CI's cache. diff compares with the latest saved snapshot and scan saves a new one, so a diff after the scan would compare the API with itself. The GitHub Action already runs them in this order.