Lint in CI/CD
- Applies to
- Gatewayruntime for API, AI, and MCP gateways
- Hosting
- Managed EdgeManaged DedicatedSelf-Hosted
zuplo lint exits with a non-zero code when it finds problems, so any CI system
can use it as a gate. This page shows a GitHub Actions workflow for each common
setup, then the pieces you need to adapt them to another CI system.
The examples assume your Zuplo project is at the root of the repository and
lists zuplo in package.json, as projects created with
create-zuplo-api do. zuplo lint doesn't need
an API key; it reads only the files in your repository.
Fail pull requests on lint errors
The simplest setup runs the linter and lets its exit code pass or fail the job:
.github/workflows/lint.yaml
The job fails when the linter finds an error. To fail on warnings too, change
the step to npx zuplo lint --fail-on warn, or set
failOn in zuplo.lint.jsonc so local
runs and CI agree.
To lint before deploying, add the same step before zuplo deploy in your
deployment workflow. See Custom CI/CD for deployment
workflows.
Show results in GitHub code scanning
With --format sarif, the linter writes results in the SARIF format that GitHub
code scanning reads. Code scanning shows each result as an annotation on the
line it applies to in the pull request diff, and tracks open results in the
repository's Security tab.
.github/workflows/lint.yaml
The if: ${{ !cancelled() }} condition uploads the results even when the lint
step fails, so the annotations appear on the pull requests that need them. The
lint step still fails the job.
Code scanning is available on public repositories, and on private repositories with GitHub Advanced Security. Without it, use the pass/fail workflow — the results appear in the job log.
Report breaking changes on pull requests
Breaking change detection compares your OpenAPI files with the version on another branch. In CI, point the baseline at the remote branch and make sure Git has fetched it.
zuplo.lint.jsonc
actions/checkout fetches a single commit by default, and the local main
branch doesn't exist in that checkout. Set fetch-depth: 0 to fetch the full
history, including origin/main:
.github/workflows/lint.yaml
If the baseline isn't in the checkout, the run fails with
baseline ref "origin/main" not found. Fetch the branch before linting.
origin/main also works on your machine after a git fetch, so the same
configuration file serves local runs and CI.
To report breaking changes without blocking the merge, set "severity": "warn".
The changes still appear in the output and in code scanning, and the job passes
unless failOn is "warn".
Use the JSON output in scripts
--format json prints a single JSON document with a summary, every result, and
any breaking changes or fixes. Use it to build your own report, such as a pull
request comment or a job summary:
.github/workflows/lint.yaml
--fail-on none keeps the lint step from failing before the summary is written;
the final jq -e '.passed' fails the job when the run didn't pass. The passed
field already takes failOn from your configuration file into account. See
Output formats for the full JSON shape.
Other CI systems
Any CI system that runs Node.js can run the linter. Each workflow above uses three things you need to carry over:
- Install the project's dependencies with
npm ci(or your package manager's equivalent), sonpx zuplouses the version in your lockfile. - Run
npx zuplo lintfrom the project root, or pass--dir. With--dir, file paths in the output are relative to that directory, so SARIF results won't line up with files in code scanning unless the project is at the repository root. - Read the exit code.
0passes,1means the linter found problems, and2means it couldn't run. Treat2as a broken pipeline rather than a lint result — check the error message in the log.
For breaking change detection, also fetch the baseline branch before linting,
for example with git fetch origin main.
Troubleshooting
The linter binary for your platform isn't installed
The linter ships as a separate, platform-specific package that npm installs
alongside the Zuplo CLI as an optional dependency. If optional dependencies are
skipped — for example with npm install --omit=optional — zuplo lint stops
with:
Code
Install the dependencies again without skipping optional dependencies. The error
message names the exact package for your platform, such as
@zuplo/lint-linux-x64, if you need to install it directly.
Invalid directory
Code
The command needs a config/ directory containing at least one file that ends
in .oas.json. Run it from the project root, or pass --dir to point at the
project. This error exits with 2.
Next steps
- Lint configuration — presets, severities, and breaking change settings.
- Testing your API — run tests against a live gateway in the same pipeline.
- Custom CI/CD — deploy from your own CI system.