Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,12 @@ jobs:
runs-on: ubuntu-latest
continue-on-error: true
steps:
# fetch-depth: 0 — the OpenAPI breaking-change gate (oasdiff) reads the base
# spec via `git show <base_ref>:docs/reference/openapi.yaml`; a shallow clone
# would lack the base ref and the gate would self-skip (base-unresolved).
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: ${{ env.CI_NODE_VERSION }}
Expand Down Expand Up @@ -195,20 +200,34 @@ jobs:
bash <(curl -fsSL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) latest "$HOME/.local/bin"
# zizmor — PyPI (pipx preferred, pip --user fallback); lands in ~/.local/bin
pipx install zizmor || pip install --user zizmor
# oasdiff — download latest linux amd64 tarball via gh (authed), extract binary
rm -rf /tmp/oasd && mkdir -p /tmp/oasd
gh release download --repo oasdiff/oasdiff --pattern '*linux_amd64.tar.gz' --dir /tmp/oasd
tar -xzf /tmp/oasd/*linux_amd64.tar.gz -C "$HOME/.local/bin" oasdiff
# ALWAYS export the bin dir (even if any step above failed)
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
# diagnostics — prove what installed on the next CI run
ls -la "$HOME/.local/bin"
"$HOME/.local/bin/gitleaks" version || true
"$HOME/.local/bin/actionlint" -version || true
"$HOME/.local/bin/osv-scanner" --version || true
"$HOME/.local/bin/oasdiff" --version || true
zizmor --version || true
- name: Secret scan (gitleaks; skips if absent)
run: npm run check:secrets
- name: Vulnerability ratchet (osv-scanner; skips if absent)
run: npm run check:vuln-ratchet
- name: Workflow lint (actionlint+zizmor; skips if absent)
run: npm run check:workflows
# OpenAPI breaking-change detection (oasdiff). Diffs the PR's public API
# contract (docs/reference/openapi.yaml) against the base branch's spec.
# ADVISORY: reports `openapiBreaking=N` and self-skips when oasdiff is absent
# or the base spec can't be resolved. BASE_REF is read by the script from the
# env (never interpolated into a shell body) — workflow-injection-safe.
- name: OpenAPI breaking-change (oasdiff; advisory)
env:
BASE_REF: ${{ github.base_ref }}
run: npm run check:openapi-breaking

docs-sync-strict:
name: Docs Sync (Strict)
Expand Down
15 changes: 15 additions & 0 deletions docs/architecture/QUALITY_GATES.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,21 @@ Runs after `test-coverage`. Blocks merge on failure.
| `check:duplication` | Code duplication (jscpd@4) does not exceed baseline in `quality-baseline.json` | Yes |
| `check:complexity` | File-level cyclomatic complexity does not exceed the cap | Yes |

### Job: `quality-extended`

Entire job is advisory (`continue-on-error: true`). The npm-based ratchets run for
real; the external scanners install via `gh release download` and self-skip (exit 0)
when a binary is still absent.

| Script | Validates | Blocking |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ------------ |
| `check:circular-deps` | No circular dependencies (dpdm) | **Advisory** |
| `check:bundle-size` | Bundle size does not exceed the cap | **Advisory** |
| `check:secrets` | Secret scanning (gitleaks) — skips if binary absent | **Advisory** |
| `check:vuln-ratchet` | Dependency vulnerabilities (osv-scanner) do not regress — skips if binary absent | **Advisory** |
| `check:workflows` | Workflow lint (actionlint + zizmor) — skips if binaries absent | **Advisory** |
| `check:openapi-breaking` | Breaking changes to the public API contract (`openapi.yaml`) vs the base branch (oasdiff) — emits `openapiBreaking=N`; skips if oasdiff absent or base spec unresolvable | **Advisory** |

### Job: `docs-sync-strict`

Runs on every PR to `main`. Blocks merge on failure.
Expand Down
33 changes: 33 additions & 0 deletions docs/reference/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5007,6 +5007,39 @@ components:
application/json:
schema:
$ref: "#/components/schemas/ValidationErrorResponse"
BadRequest:
description: The request was malformed or failed validation
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
example:
error:
message: Invalid request
type: invalid_request_error
requestId: 8c2b1d44-7a3e-4c91-9b0f-1e2d3c4b5a60
NotFound:
description: The requested resource was not found
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
example:
error:
message: Resource not found
type: not_found_error
requestId: 4d5e6f70-1a2b-3c4d-5e6f-7a8b9c0d1e2f
InternalError:
description: An unexpected server error occurred
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
example:
error:
message: Internal server error
type: api_error
requestId: 0a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d

schemas:
PlaygroundPreset:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
"check:provider-consistency": "node --import tsx scripts/check/check-provider-consistency.ts",
"check:fetch-targets": "node scripts/check/check-fetch-targets.mjs",
"check:openapi-routes": "node scripts/check/check-openapi-routes.mjs",
"check:openapi-breaking": "node scripts/check/check-openapi-breaking.mjs",
"check:deps": "node scripts/check/check-deps.mjs",
"check:file-size": "node scripts/check/check-file-size.mjs",
"check:duplication": "node scripts/check/check-duplication.mjs",
Expand Down
Loading