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
# 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:
scan—0on a successful scan even when impacts are found (a finding is a report, not a failure);2on usage errors. To gate a pipeline on findings, inspectimpacts.lengthin the JSON — see the gate example below.fix—0changes made or previewed,1nothing applicable,2usage error,3stale pack refused without--ack-stale. Treat1as success in automation (clean repo),3as a hard stop for human review.pr— never pushes without an explicit--pushflag, 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:
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.
# .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
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.
Recommended cadence
- 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 review —
mendapi fixis dry-run by default andmendapi prnever pushes without--push. Keep it that way in automation: generate the diff, let a human merge.