chore(deps-dev): bump the lint-format group across 1 directory with 3 updates #661
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| # Cancel in-progress CI runs when a new commit is pushed to the same | |
| # PR / branch. Without this, force-pushes pile up overlapping runs | |
| # that all consume runner minutes pointlessly. | |
| # `cancel-in-progress: true` on PR-triggered runs keeps the queue | |
| # clean; we leave main-branch runs uncancelled so post-merge runs | |
| # always complete (they're rare and the result is load-bearing for | |
| # release dashboards). | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| permissions: | |
| contents: read | |
| jobs: | |
| npm-package: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22.x | |
| cache: npm | |
| cache-dependency-path: package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Verify lockfile is in sync | |
| run: | | |
| # npm ci already fails on package.json/lockfile mismatch, but this | |
| # catches subtler drift: e.g. someone ran `npm install` which silently | |
| # updated resolved URLs or integrity hashes without changing ranges. | |
| npm install --package-lock-only --ignore-scripts | |
| git diff --exit-code package-lock.json || { | |
| echo "::error::package-lock.json is out of sync with package.json. Run 'npm install' locally and commit the updated lockfile." | |
| exit 1 | |
| } | |
| - name: Verify npm package wrapper | |
| run: npm test | |
| # ── Go (analysis engine) ──────────────────────────────────── | |
| # Matrix-tested across the three OS families we ship binaries for. | |
| # ubuntu remains the canonical runner (race detector + extended smoke | |
| # suite); macos and windows run a reduced "does it build and pass unit | |
| # tests" check. The multi-OS matrix surfaces windows path-separator and | |
| # EOL regressions in PRs rather than after binaries ship. | |
| go-test: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| extended: true | |
| - os: macos-latest | |
| extended: false | |
| - os: windows-latest | |
| extended: false | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 # full history for fixture repos with git commits | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Verify go.mod is tidy | |
| if: matrix.extended | |
| run: | | |
| go mod tidy | |
| git diff --exit-code go.mod go.sum | |
| - name: Verify generated docs are up to date | |
| if: matrix.extended | |
| # Hard-fail gate: if you edit internal/signals/manifest.go, run | |
| # `make docs-gen` and commit docs/signals/manifest.json. | |
| run: make docs-verify | |
| - name: Run core Go verification | |
| if: matrix.extended | |
| run: make go-release-verify | |
| - name: Determinism gate | |
| if: matrix.extended | |
| # Three back-to-back runs against the same fixture must produce | |
| # byte-identical snapshots; this gates determinism in CI rather than | |
| # relying on a single-shot unit test. Failure here means a | |
| # non-deterministic data path crept in (typically an unsorted map or | |
| # a timestamp). | |
| run: make test-determinism | |
| - name: Run Go tests | |
| # The race detector adds significant runtime cost; on macos and | |
| # windows runners we omit it to keep PR feedback fast. ubuntu | |
| # remains the canonical environment for race-detection. | |
| run: | | |
| if [ "${{ matrix.extended }}" = "true" ]; then | |
| go test ./internal/... ./cmd/... -count=1 -race | |
| else | |
| go test ./internal/... ./cmd/... -count=1 | |
| fi | |
| shell: bash | |
| - name: Smoke-test terrain CLI | |
| if: matrix.extended | |
| run: | | |
| go build -o terrain ./cmd/terrain | |
| ./terrain version | |
| ./terrain version --json > /dev/null | |
| ./terrain --help > /dev/null | |
| ./terrain ai --help > /dev/null | |
| ./terrain migration --help > /dev/null | |
| ./terrain debug --help > /dev/null | |
| ./terrain export --help > /dev/null | |
| ./terrain analyze --root tests/fixtures/sample-repo --json > /dev/null | |
| ./terrain insights --root tests/fixtures/sample-repo --json > /dev/null | |
| - name: Smoke-test fixture matrix | |
| if: matrix.extended | |
| run: | | |
| for fixture in high-fanout weak-coverage legacy-mixed ai-eval-suite skipped-tests; do | |
| echo "--- fixture: $fixture ---" | |
| ./terrain analyze --root "tests/fixtures/$fixture" --json > /dev/null | |
| done | |
| - name: Benchmark smoke tests (4 canonical commands) | |
| if: matrix.extended | |
| run: | | |
| echo "=== benchmark: sample-repo ===" | |
| go build -o terrain ./cmd/terrain | |
| ./terrain analyze --root tests/fixtures/sample-repo --json | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['testsDetected']['testFileCount'] > 0, 'analyze: no test files'" | |
| ./terrain insights --root tests/fixtures/sample-repo --json | python3 -c "import sys,json; d=json.load(sys.stdin); assert 'healthGrade' in d, 'insights: no health grade'" | |
| echo "=== benchmark: ai-eval-suite ===" | |
| ./terrain analyze --root tests/fixtures/ai-eval-suite --json | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['testsDetected']['testFileCount'] > 0, 'analyze: no test files'" | |
| ./terrain ai list --root tests/fixtures/ai-eval-suite --json | python3 -c "import sys,json; d=json.load(sys.stdin); assert d['summary']['scenarios'] > 0, 'ai list: no scenarios'" | |
| echo "=== benchmark: backend-api ===" | |
| ./terrain analyze --root tests/fixtures/backend-api --json | python3 -c "import sys,json; d=json.load(sys.stdin); assert len(d.get('riskPosture',[])) == 5, 'analyze: expected 5 posture dimensions'" | |
| echo "All benchmark smoke tests passed." | |
| extension: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22.x | |
| cache: npm | |
| cache-dependency-path: extension/vscode/package-lock.json | |
| - name: Verify VS Code extension | |
| run: make extension-verify | |
| # ── Go benchmark comparison (PRs only) ────────────────────── | |
| go-bench-compare: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Install benchstat | |
| run: GOTOOLCHAIN=auto go install golang.org/x/perf/cmd/benchstat@latest | |
| - name: Benchmark current HEAD | |
| run: | | |
| go test ./internal/engine ./internal/analysis ./internal/scoring ./internal/testcase \ | |
| -run '^$' \ | |
| -bench 'BenchmarkRunPipeline|BenchmarkSignalDetection|BenchmarkBuildImportGraph|BenchmarkRiskScore|BenchmarkExtractTestCases' \ | |
| -count=5 > /tmp/bench_head.txt | |
| - name: Benchmark PR base | |
| run: | | |
| git checkout "${{ github.event.pull_request.base.sha }}" | |
| go test ./internal/engine ./internal/analysis ./internal/scoring ./internal/testcase \ | |
| -run '^$' \ | |
| -bench 'BenchmarkRunPipeline|BenchmarkSignalDetection|BenchmarkBuildImportGraph|BenchmarkRiskScore|BenchmarkExtractTestCases' \ | |
| -count=5 > /tmp/bench_base.txt | |
| - name: Compare benchmark results (base vs head) | |
| run: | | |
| git checkout "${{ github.sha }}" | |
| "$(go env GOPATH)/bin/benchstat" /tmp/bench_base.txt /tmp/bench_head.txt | tee /tmp/benchstat.txt | |
| - name: Regression gate (>10% fails the job) | |
| # The gate parses the same benchmark output benchstat consumed, | |
| # computes per-bench delta, and fails if any benchmark regressed | |
| # beyond the threshold. | |
| run: | | |
| go run ./cmd/internal/terrain-bench-gate \ | |
| --base /tmp/bench_base.txt \ | |
| --head /tmp/bench_head.txt \ | |
| --threshold 10 | tee /tmp/bench-gate.txt | |
| - name: Append summary | |
| if: always() | |
| run: | | |
| { | |
| echo '### Go Benchmark Comparison (base vs head)' | |
| echo '' | |
| echo '```' | |
| cat /tmp/benchstat.txt | |
| echo '```' | |
| echo '' | |
| echo '### Regression gate (>10% threshold)' | |
| echo '' | |
| echo '```' | |
| cat /tmp/bench-gate.txt 2>/dev/null || echo "(gate did not run)" | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # ── Docs and CLI surface audit ────────────────────────────── | |
| # Consistency gate between the shipped CLI surface, the docs, and the | |
| # tracked tree: dead links, placeholder copy, --help coverage, tracked | |
| # binaries, changelog discipline, clean discovery output. | |
| docs-and-surface-audit: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 # tags needed for the changelog discipline check | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Run docs and surface audit | |
| run: bash scripts/skeptic-audit.sh | |
| # ── Offline guarantee ─────────────────────────────────────── | |
| # The docs claim the analyze / gate / fix path needs no API key and | |
| # no network. This job enforces the claim: build the binary, then run | |
| # the core commands inside a network namespace with no interfaces — | |
| # no routes, no DNS, loopback down — against a fixture with a seeded | |
| # prompt↔schema drift, so each command demonstrably does real work | |
| # rather than short-circuiting. Any attempted egress fails the | |
| # command and the job. `terrain serve` is deliberately not covered | |
| # here: it binds loopback by design. | |
| offline-guarantee: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Setup Go | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| - name: Build terrain | |
| run: go build -o "$RUNNER_TEMP/terrain" ./cmd/terrain | |
| - name: Verify the namespace actually blocks egress | |
| # Negative control: a green offline run proves nothing if the | |
| # namespace quietly has network access. Guard commands first so | |
| # a broken sudo/unshare/curl can't masquerade as "blocked". | |
| run: | | |
| sudo unshare -n -- true | |
| command -v curl > /dev/null | |
| if sudo unshare -n -- curl --max-time 5 -sS -o /dev/null https://example.com/; then | |
| echo "::error::network namespace is not isolated — egress succeeded" | |
| exit 1 | |
| fi | |
| echo "egress blocked as expected" | |
| - name: Run discover / analyze / gate / fix with zero network access | |
| run: | | |
| work="$RUNNER_TEMP/offline" | |
| mkdir -p "$work" | |
| cp tests/fixtures/offline/*.py "$work/" | |
| sudo unshare -n -- bash tests/fixtures/offline/check.sh \ | |
| "$RUNNER_TEMP/terrain" "$work" "$PWD/tests/fixtures/offline" |