Wire into CI
OpenSIP CLI is a CLI that exits with a code. The GitHub Action is the shortest
path for pull request feedback; direct CLI commands remain available when you
want to own every baseline and artifact step yourself.
The minimal setup
# .github/workflows/opensip.yml
name: OpenSIP
on: [pull_request, push]
jobs:
opensip:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: opensip-ai/opensip-cli@v1
For a direct CLI workflow, the recommended public command is:
- run: opensip audit --json
The Action currently invokes the built-in review as `opensip suite run audit
--changed --json` internally. That explicit spelling is an Action implementation
detail, not a different analysis path: both forms use the same built-in suite
executor, Run ledger, review brief, and exit policy. This documentation change
does not change Action inputs or outputs. A supported repository gets
changed-code PR feedback without an opensip-cli.config.yml or an OpenSIP Cloud
account.
Do not add --open in CI. JSON, CI, non-TTY, and remote-shell execution suppress
browser launch. Archive the generated report separately if a human-readable CI
artifact is required; see the report reference.
That's the floor. The rest of this page is the polish: PR comments, SARIF upload,
failure policy, and the lower-level manual baseline flow.
Full GitHub Action setup
name: OpenSIP
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
pull-requests: write
security-events: write
jobs:
opensip:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: opensip-ai/opensip-cli@v1
id: opensip
with:
suite: audit
changed: true
annotations: true
comment: true
sarif: true
fail-on: new-errors
- uses: github/codeql-action/upload-sarif@v4
if: always() && steps.opensip.outputs.sarif != ''
with:
sarif_file: ${{ steps.opensip.outputs.sarif }}
category: opensip-cli
The action outputs:
| Output | Meaning |
|---|---|
| verdict | pass, warn, or fail from the review brief. |
| issues | Total issue count from the suite aggregate. |
| new-issues | Net-new count when baseline evidence is available. |
| brief | Path to the review brief JSON. |
| sarif | Path to action-generated SARIF when sarif: true. |
| degraded | Comma-separated degraded evidence reasons. |
fail-on: new-errors is the default. It fails on net-new error-rung risks when
baseline evidence is available and falls back to error-rung changed-code risks
when no baseline delta exists. Use fail-on: never for report-only adoption,
all-errors for a hard gate, or new-warnings to include warning-rung new
risks.
The action-generated SARIF is a bounded projection of the audit review brief. For
full source-tool SARIF and persistent baselines, use the manual flow below.
Example artifacts generated by the action renderer:
For regulated or mirror-first environments, pin both moving pieces: the GitHub
Action ref and the npm package version the action runs. The action does not
require an OpenSIP Cloud account; it produces local evidence, annotations, SARIF,
and step outputs from the repository checkout.
PR annotations via SARIF
opensip-cli exports the SARIF format that GitHub understands natively via the fit export --format baseline subcommand. The flow is two steps: run fit --gate-save (which records findings into the project SQLite store, then exits according to the failOnErrors/failOnWarnings thresholds — ADR-0020: the step itself is the gate, not a free pass), then fit export --format baseline --out fit.sarif to write the SARIF document. Uploaded findings appear inline in the PR's "Files changed" view.
- run: opensip fit --gate-save # record findings, then exit per fail thresholds
- run: opensip fit export --format baseline --out fit.sarif
if: always() # the save happened before the exit — export even when the gate failed
- uses: github/codeql-action/upload-sarif@v3
if: always() # upload even when a previous step failed
with:
sarif_file: fit.sarif
category: opensip-fit
The if: always() is important — fit --gate-save hard-fails the step when error-level findings breach the configured thresholds (set failOnErrors: 0 in the fitness: block for a ratchet-only adoption where only net-new Code Scanning alerts block PRs), and GitHub skips subsequent steps after a failure by default. The baseline is saved before the exit code is set, so the SARIF export + upload still have everything they need — they just have to actually run.
For GitLab, convert the exported SARIF to the Code Quality widget format with GitLab's converter, renaming the output to gl-code-quality-report.json. (There is no native GitLab code-quality emitter today — go through SARIF.)
Baseline-gate flow
If the codebase already has violations, gating on "all violations" blocks every PR until cleanup is done. Almost no team accepts that. The baseline-gate flow is the alternative: capture today's violations, gate only on new ones.
# Run once locally, on a clean main branch
opensip fit --gate-save
# This writes the baseline into opensip-cli/.runtime/datastore.sqlite
Then in CI:
- run: opensip fit --gate-compare
--gate-compare exits 0 if no new violations appeared since the baseline. Existing ones are tolerated. The baseline lives in SQLite (opensip-cli/.runtime/datastore.sqlite); since .runtime/ is gitignored, you'll want to publish + restore the baseline store as a CI artifact.
The artifact pattern: the gate baseline is a SQLite store, not a committed file. The standard flow is to run fit --gate-save on main-branch builds and upload opensip-cli/.runtime/datastore.sqlite as a workflow artifact; PR builds download that artifact into opensip-cli/.runtime/ before running fit --gate-compare. (For a human-readable export — e.g. to inspect the baseline or feed GitHub Code Scanning — use fit export --format baseline --out baseline.sarif, which reads the same store.) See output, gate, SARIF and the architecture-gate CI patterns for the full workflow.
Recommended full setup
The action setup above is the recommended default. Use the manual shape below
when you need explicit baseline artifact restore/publish behavior.
name: Fitness
on:
pull_request:
push:
branches: [main]
jobs:
fit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 24 }
- run: curl -fsSL https://opensip.ai/cli/install.sh | bash
# Restore the baseline store produced by the last main-branch build.
- uses: actions/download-artifact@v4
if: github.event_name == 'pull_request'
continue-on-error: true
with:
name: fit-baseline
path: opensip-cli/.runtime/
# On PRs: gate against new violations only.
# On main: refresh the baseline (so the next PR sees current state).
- name: Run fit
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
opensip fit --gate-compare
else
opensip fit --gate-save
fi
# Export the SARIF for PR annotations (reads the SQLite store).
- name: Export SARIF
if: always()
run: opensip fit export --format baseline --out fit.sarif
- name: Upload SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: fit.sarif
category: opensip-fit
# On main: publish the refreshed baseline store for the next PR.
- uses: actions/upload-artifact@v4
if: github.event_name != 'pull_request'
with:
name: fit-baseline
path: opensip-cli/.runtime/datastore.sqlite
This is the shape we recommend: PRs see "is this getting worse?", main updates the bar. Developers fix legacy violations at their own pace; CI is fast (no full-codebase pass on every PR).
Speed
The opensip-cli repository runs a synthetic performance SLO lane in CI:
pnpm bench:slo:ci -- --profile pr --require-memory --out slo-report.json. It
uploads slo-report.json as a workflow artifact with command timings, RSS
measurements, graph profile summaries, and any performance-slo:* signals. The
budgets are documented in Performance SLOs,
and the current published measurements are in
The CI SLO report is clean-wall evidence: the harness removes inherited
profiling and OpenTelemetry export variables. Contributors investigate a
regression locally with pnpm bench:profile, compare repeated before/after
reports with pnpm bench:compare, and use --cpu-profile only as a separate
experiment. The complete workflow and artifact-handling rules are in
If fit is slow on a large repo, the usual culprits:
- A specific check has an
O(n²)scan. Run with--verboselocally to see per-check timing. - Targets globs are too broad. The
targets:block inopensip-cli.config.ymlscopes which files each check matches; widening it fromsrc//.tsto/.tswill includenode_modules/if you're not careful. - A regex-shaped check has a catastrophic backtracking pattern. Switch to AST-driven analysis.
What opensip fit actually does in CI
For a deeper understanding of the gate flow itself — what the baseline contains, how new-vs-old violations are matched, what the exit codes mean — see output, gate, SARIF.
Where to go next
| You want to … | Go to … |
|---|---|
| Adopt incrementally on a large existing codebase | Adopt in a monorepo |
| Coexist with ESLint / migrate gradually | Migrate from ESLint |
| Understand the baseline format and diff logic | Output, gate, SARIF |
| See all CI-relevant CLI flags | CLI commands |