Home/Docs/CI & automation

CI & automation

The scanner is built for CI: zero configuration, zero network (after an initial sync), machine-readable JSON output, and deterministic exit behaviour.

Machine output

shell
# JSON to stdout, human summary suppressed
mendapi scan --json > impact.json

# or write the report directly
mendapi scan --out impact.json

# fixer JSON: one document per run, human summary moves to stderr
mendapi fix --from-report impact.json --json > fix.json

Every machine-readable report (scan --json, deps --json, fix --json, fix-report.json) carries a top-level schema_version integer (currently 1). It is bumped only on breaking JSON-shape changes — pin your parsers and agents to it instead of sniffing keys.

Exit codes

Automation should branch on exit codes where they carry signal, and on JSON where they do not:

  • scan0 on a successful scan even when impacts are found (a finding is a report, not a failure); 2 on usage errors. To gate a pipeline on findings, inspect impacts.length in the JSON — see the gate example below.
  • fix0 changes made or previewed, 1 nothing applicable, 2 usage error, 3 stale pack refused without --ack-stale. Treat 1 as success in automation (clean repo), 3 as a hard stop for human review.
  • pr — never pushes without an explicit --push flag, regardless of exit code.

Failing the build on impact

scan deliberately exits 0 when impacts are found. If you want a red build, gate on the JSON:

shell
npx mendapi scan --json > impact.json
node -e "
  const r = require('./impact.json');
  if (r.schema_version !== 1) throw new Error('unexpected schema_version');
  if (r.impacts.length > 0) {
    console.error(r.impacts.length + ' breaking-change impact(s) found');
    process.exit(1);
  }
"

Pre-commit hook

A local hook keeps API-impact drift out of the repo without waiting for CI. Zero network: scan reads the local change database, so run sync on your own schedule (cron, CI nightly), not inside the hook.

shell
# .git/hooks/pre-commit  (chmod +x)
#!/bin/sh
npx mendapi scan --json > /tmp/mendapi-impact.json || exit 0  # usage errors never block commits
node -e "
  const r = require('/tmp/mendapi-impact.json');
  const high = r.impacts.filter((i) => i.confidence === 'high');
  if (high.length > 0) {
    console.error('mendapi: ' + high.length + ' high-confidence API impact(s). Run: npx mendapi fix --from-report /tmp/mendapi-impact.json');
    process.exit(1);
  }
"

Gating only on high confidence keeps the hook quiet: medium findings surface in nightly CI where a human triages them, instead of blocking every commit.

GitHub Actions example

shell
name: api-impact
on:
  schedule:
    - cron: "0 6 * * *"   # nightly
  workflow_dispatch:

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - name: Fetch change feed
        run: npx mendapi sync
      - name: Scan for breaking-change impact
        run: npx mendapi scan --out impact.json
      - name: Preview fixes (dry-run, never writes to the repo)
        run: npx mendapi fix --from-report impact.json --out-dir fixes --json > fix.json || test $? -eq 1
      - name: Upload impact report and fix preview
        uses: actions/upload-artifact@v4
        with:
          name: mendapi-report
          path: |
            impact.json
            fix.json
            fixes/

The || test $? -eq 1 keeps the job green when nothing is applicable (fix exit 1 means "clean", not "failed") while still failing on usage errors (2) and stale packs (3). The uploaded fixes/ directory contains the unified diff a human can review and apply — or feed to mendapi pr on a trusted runner.

  • Nightly scheduled scan — upstream changes land on the provider's clock, not on your commit cadence. A snapshot at review time can be stale by deploy time.
  • Scan on dependency bumps — pair with Dependabot/Renovate PRs to see the API-level blast radius of an SDK version bump, not just the version delta.
  • Gate fixes behind reviewmendapi fix is dry-run by default and mendapi pr never pushes without --push. Keep it that way in automation: generate the diff, let a human merge.
Pre-release documentation, generated from the mendapi codebase. Command output and pack listings reflect the shipped CLI exactly.