chore(deps): bump @radix-ui/react-slot from 1.2.4 to 1.2.5 in /web #462
Workflow file for this run
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] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # detect-changes emits per-area output flags so downstream jobs can | |
| # decide whether they actually need to run. Only Go-touching PRs | |
| # need the slow race+coverage gate; npm/docs PRs (incl. dependabot | |
| # patch bumps) skip it entirely. | |
| detect-changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| go: ${{ steps.filter.outputs.go }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| # Pinned to a commit SHA per the SonarCloud hotspot guidance — | |
| # third-party actions can have their tags rewritten to point at | |
| # a malicious commit, so consume the action by immutable hash. | |
| # Tag at the time of pinning: v3 (commit d1c1ffe). | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: filter | |
| with: | |
| filters: | | |
| go: | |
| - '**/*.go' | |
| - 'go.mod' | |
| - 'go.sum' | |
| - '.github/workflows/ci.yml' | |
| # PR fast gate: no -race, no coverage. Runs on every pull request so | |
| # docs/CI/web-only PRs still get a Go-build sanity check (~45-60s). | |
| # Skipped on push events because main pushes are post-merge — the | |
| # PR-side run already proved the same tree, and go-test-race below | |
| # re-validates with race+coverage. | |
| go-test: | |
| name: Go Tests | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| # //go:embed cannot reference paths outside the package, so the | |
| # canonical deploy/install-agent.sh is mirrored into the server | |
| # package via `go generate`. Mirror Makefile's gen-install-script | |
| # so every Go-touching CI step sees the embedded file. | |
| - name: Mirror install-agent.sh for //go:embed | |
| run: go generate ./internal/controlplane/server/... | |
| - run: go test -count=1 ./... | |
| # Loadtest correctness gate: the Test* variants of the realistic | |
| # concurrency scenarios (agent burst, login storm, job fan-out) | |
| # assert no errors / no audit drops / no missed lockouts under | |
| # contention. Cheap (<30s) and runs on every PR. The full | |
| # throughput sweep is separate (see load-bench job). | |
| - name: Loadtest correctness assertions | |
| run: go test -count=1 ./internal/loadtest/... | |
| # Agent binaries ship for arm64 (RPi / ARM VPS fleets); until now no | |
| # test ever executed on that architecture. Smoke-run the agent | |
| # packages natively on GitHub's arm64 runners (audit F4). | |
| go-test-arm64: | |
| name: Go Tests (agent, arm64) | |
| runs-on: ubuntu-24.04-arm | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| - name: Mirror install-agent.sh for //go:embed | |
| run: go generate ./internal/controlplane/server/... | |
| - name: Agent test suite (native arm64) | |
| run: go test -count=1 ./internal/agent/... ./cmd/agent/... | |
| # Slow gate: race detector + coverage. Runs on: | |
| # - PRs that touch Go files / go.mod / go.sum (real merge gate | |
| # under branch protection — required check evaluates here) | |
| # - pushes to main (covers the merge commit and uploads coverage) | |
| # Skipped on PRs that do not touch Go (dependabot npm bumps, doc | |
| # tweaks) so the long pole moves off the dependabot critical path. | |
| go-test-race: | |
| name: Go Tests (race + coverage) | |
| needs: detect-changes | |
| if: | | |
| (github.event_name == 'pull_request' && needs.detect-changes.outputs.go == 'true') || | |
| (github.event_name == 'push' && github.ref == 'refs/heads/main') | |
| runs-on: ubuntu-latest | |
| # Safety net: -race over the whole tree historically lands around | |
| # 6–8 min; loadtest no-race adds ~30 s. A runaway leaks memory and | |
| # the runner thrashes for an hour before GitHub's default 6 h kill — | |
| # cap explicitly so a regression fails fast. | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| - name: Mirror install-agent.sh for //go:embed | |
| run: go generate ./internal/controlplane/server/... | |
| # The internal/loadtest scenarios spawn ~100 concurrent Argon2id | |
| # verifies (~96 MiB each) and ~200-agent enrolls; under -race the | |
| # working set climbs past 20 GB RSS and the runner spends 25+ min | |
| # thrashing the page cache. Mirror the local pre-push hook split: | |
| # everything else gets -race + coverage; loadtest gets a separate | |
| # no-race pass below. The fast-PR gate (go-test) already covers | |
| # loadtest correctness on every PR. | |
| - name: Go race + coverage (loadtest excluded) | |
| run: | | |
| PKGS=$(go list ./... | grep -v '^github.com/lost-coder/panvex/internal/loadtest$') | |
| go test -race -coverprofile=coverage.out -covermode=atomic $PKGS | |
| - name: Go tests (loadtest, no -race) | |
| run: go test -count=1 ./internal/loadtest/... | |
| # Coverage floor: total backend coverage must stay above the | |
| # baseline. Fail loud here instead of relying on Codecov's | |
| # asynchronous diff-coverage (which is informational, not a gate). | |
| # The threshold is intentionally below the current measured | |
| # number so a small regression flags rather than every PR — bump | |
| # in lockstep with new test waves. | |
| # Raised 45 -> 48 (audit F4). Measured total at the time was ~50.7% | |
| # (the audit's "~61%" estimate was stale); 48 leaves ~2.7pp headroom | |
| # for fluctuation while catching real regressions. Bump toward 55 as | |
| # coverage grows with new test waves. | |
| - name: Enforce coverage floor (>=48%) | |
| run: | | |
| set -eu | |
| total=$(go tool cover -func=coverage.out | awk '/^total:/ {print $3}' | tr -d '%') | |
| echo "total coverage: ${total}%" | |
| awk -v t="$total" 'BEGIN { if (t+0 < 48.0) exit 1 }' | |
| - name: Upload coverage artifact | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: go-coverage | |
| path: coverage.out | |
| - name: Upload coverage to Codecov | |
| if: always() | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| files: coverage.out | |
| flags: backend | |
| fail_ci_if_error: false | |
| # Canary build against the next Go version. Non-blocking — surfaces | |
| # toolchain regressions before we lock the next version into the | |
| # required matrix but does not gate merges if upstream has a | |
| # transient bug. | |
| go-test-next: | |
| name: Go Tests (next version, canary) | |
| needs: detect-changes | |
| # Non-blocking canary (continue-on-error): surfaces toolchain | |
| # regressions on the latest Go without gating merges. We track | |
| # `stable` + check-latest rather than pinning a specific RC — an | |
| # explicit RC tag (e.g. 1.27.0-rc.1) is not reliably resolvable by | |
| # actions/setup-go and left the job permanently red. `stable` always | |
| # resolves and automatically rolls onto the next Go minor the day it | |
| # GAs, so this keeps giving signal without manual version bumps. | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "stable" | |
| check-latest: true | |
| - name: Mirror install-agent.sh for //go:embed | |
| run: go generate ./internal/controlplane/server/... | |
| - run: go test -count=1 ./... | |
| schema-sync: | |
| # C9 + A6: verify the PostgreSQL and SQLite migration bundles stay in | |
| # sync AND run the full storage tree (contract suites, EXPLAIN plan | |
| # gates, prune tests) against a live Postgres. Without the DSN the | |
| # postgres-bound tests skip silently — this job is the only place | |
| # the postgres backend is actually executed. | |
| name: Schema Sync (PG vs SQLite) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_USER: panvex | |
| POSTGRES_PASSWORD: panvex | |
| POSTGRES_DB: panvex_test | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| env: | |
| PANVEX_POSTGRES_TEST_DSN: postgres://panvex:panvex@127.0.0.1:5432/panvex_test?sslmode=disable | |
| PANVEX_ALLOW_INSECURE_DB: "1" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| - name: Run storage tree against Postgres (contract + schema-sync + EXPLAIN) | |
| # -p 1: the migrate schema-sync test DROPs/recreates the public | |
| # schema while the postgres contract tests TRUNCATE the same | |
| # database — package-level parallelism would race on the shared | |
| # DSN. Expected duration ~3-5 min. | |
| run: go test -p 1 -count=1 ./internal/controlplane/storage/... | |
| panel-build: | |
| name: Build Binaries | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| - name: Mirror install-agent.sh for //go:embed | |
| run: go generate ./internal/controlplane/server/... | |
| - run: go build -o /dev/null ./cmd/control-plane | |
| - run: go build -o /dev/null ./cmd/agent | |
| go-lint: | |
| name: Go Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| - name: Mirror install-agent.sh for //go:embed | |
| run: go generate ./internal/controlplane/server/... | |
| - uses: golangci/golangci-lint-action@v9 | |
| with: | |
| # Pin to a known-good golangci-lint version. Bumping is a deliberate | |
| # operator action so a silent upstream regression cannot break CI. | |
| version: v2.11.4 | |
| go-vuln: | |
| name: Go Vulnerability Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| - name: Install govulncheck | |
| # Pinned: a govulncheck regression should not silently break CI. | |
| run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.4 | |
| - name: Run govulncheck | |
| run: govulncheck ./... | |
| # Drift guard for the OpenAPI codegen pipeline (Wave 3.3): ensure | |
| # openapi/panvex.gen.go and web/src/shared/api/openapi.gen.ts match | |
| # the canonical openapi/panvex.yaml. Mirrors the gen-settings drift | |
| # guard pattern. | |
| openapi-drift: | |
| name: OpenAPI Codegen Drift | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22" | |
| cache: 'npm' | |
| cache-dependency-path: web/package-lock.json | |
| - name: Install oapi-codegen | |
| run: go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.7.0 | |
| - name: Verify generated files in sync with spec | |
| run: make verify-openapi | |
| # Phase 4 inlined @lost-coder/panvex-ui into web/src/ui/, so the | |
| # frontend jobs no longer need to clone + build a side-repo UI-kit | |
| # or symlink ../ui. They just npm ci + run the target script. | |
| frontend-build: | |
| name: Frontend Build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22" | |
| cache: 'npm' | |
| cache-dependency-path: web/package-lock.json | |
| - name: Install and build frontend | |
| working-directory: web | |
| run: npm ci && npm run build | |
| # Phase-3 §3.3: size-limit gates the bundle so a careless | |
| # `npm install` of a heavy dep, or a forgotten manualChunks | |
| # override, fails CI instead of silently bloating first-paint. | |
| # Budgets live in web/package.json under "size-limit". | |
| - name: Check bundle size budgets | |
| working-directory: web | |
| run: npm run size | |
| frontend-test: | |
| name: Frontend Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22" | |
| cache: 'npm' | |
| cache-dependency-path: web/package-lock.json | |
| # P2-TEST-01: vitest suite. npm run test = vitest run which exits | |
| # non-zero on any failure; coverage is gated by thresholds in | |
| # vitest.config.ts so a regression under 40% also fails CI. | |
| - name: Run frontend tests | |
| working-directory: web | |
| run: npm ci && npm run test | |
| - name: Run coverage with thresholds | |
| working-directory: web | |
| run: npm run test:coverage | |
| - name: Run node:test state suites | |
| working-directory: web | |
| run: npm run test:state | |
| frontend-lint: | |
| name: Frontend Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22" | |
| cache: 'npm' | |
| cache-dependency-path: web/package-lock.json | |
| - name: Lint frontend | |
| working-directory: web | |
| run: npm ci && npm run lint | |
| - name: i18n parity + hardcoded-Cyrillic guard | |
| working-directory: web | |
| run: npm run check:i18n | |
| frontend-e2e: | |
| name: Frontend E2E (Playwright) | |
| runs-on: ubuntu-latest | |
| # The smoke suite stubs the API per-test via page.route(), so it | |
| # boots Vite and a hermetic browser without touching a backend. | |
| # Running on every PR keeps regressions in the navigation / | |
| # auth-shell flows from sneaking past type checks. | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22" | |
| cache: 'npm' | |
| cache-dependency-path: web/package-lock.json | |
| - name: Install npm deps | |
| working-directory: web | |
| run: npm ci | |
| - name: Install Playwright browsers | |
| working-directory: web | |
| run: npx playwright install --with-deps chromium | |
| - name: Run Playwright smoke suite | |
| working-directory: web | |
| run: npx playwright test --project=chromium | |
| - name: Upload Playwright report | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-report | |
| path: web/playwright-report/ | |
| retention-days: 7 | |
| frontend-e2e-integration: | |
| name: Frontend E2E (integration, real backend) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| # Unlike frontend-e2e (hermetic, page.route() stubs), this job runs | |
| # the suite in web/tests/e2e/integration/ against a REAL | |
| # control-plane binary with the embedded UI and a SQLite store — | |
| # the auth middleware, session store, and embedded-asset serving | |
| # are exercised end-to-end (audit F2). | |
| env: | |
| PANVEX_ENCRYPTION_KEY: ci-e2e-integration-throwaway-key | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22" | |
| cache: 'npm' | |
| cache-dependency-path: web/package-lock.json | |
| - name: Mirror install-agent.sh for //go:embed | |
| run: go generate ./internal/controlplane/server/... | |
| - name: Install npm deps | |
| working-directory: web | |
| run: npm ci | |
| - name: Build embedded UI bundle | |
| working-directory: web | |
| run: npm run build:embed | |
| - name: Build control-plane (embedded UI) | |
| run: | | |
| mkdir -p .tmp/e2e | |
| go build -tags embeddedui -o .tmp/e2e/panvex-control-plane ./cmd/control-plane | |
| - name: Bootstrap admin account (admin/e2e-secret per login.int.spec.ts) | |
| run: | | |
| PANVEX_BOOTSTRAP_ALLOW_INSECURE_FLAG=1 ./.tmp/e2e/panvex-control-plane bootstrap-admin \ | |
| -username admin \ | |
| -password e2e-secret \ | |
| -storage-driver sqlite \ | |
| -storage-dsn .tmp/e2e/panvex.db | |
| - name: Start control-plane on :18080 | |
| run: | | |
| PANVEX_HTTP_ADDR=:18080 \ | |
| PANVEX_GRPC_ADDR=:18443 \ | |
| PANVEX_STORAGE_DRIVER=sqlite \ | |
| PANVEX_STORAGE_DSN=.tmp/e2e/panvex.db \ | |
| ./.tmp/e2e/panvex-control-plane > .tmp/e2e/panel.log 2>&1 & | |
| for i in $(seq 1 60); do | |
| if curl -fsS http://127.0.0.1:18080/readyz >/dev/null 2>&1; then | |
| echo "control-plane ready after ${i}s" | |
| exit 0 | |
| fi | |
| sleep 1 | |
| done | |
| echo "::error::control-plane never became ready on :18080" | |
| cat .tmp/e2e/panel.log | |
| exit 1 | |
| - name: Install Playwright browsers | |
| working-directory: web | |
| run: npx playwright install --with-deps chromium | |
| - name: Run integration suite | |
| working-directory: web | |
| run: npx playwright test -c playwright.integration.config.ts | |
| - name: Show panel log on failure | |
| if: failure() | |
| run: tail -n 200 .tmp/e2e/panel.log | |
| - name: Upload traces and report | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-integration-report | |
| path: | | |
| web/playwright-report/ | |
| web/test-results/ | |
| retention-days: 7 | |
| frontend-audit: | |
| name: Frontend npm audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22" | |
| cache: 'npm' | |
| cache-dependency-path: web/package-lock.json | |
| - name: Audit web dependencies | |
| working-directory: web | |
| # `--audit-level=moderate` matches the bar the security audit | |
| # asks us to enforce. Anything moderate or worse fails CI; lower | |
| # severities are still listed by `npm audit` but do not gate. | |
| run: npm audit --audit-level=moderate | |
| buf-lint: | |
| name: Protobuf Lint | |
| runs-on: ubuntu-latest | |
| # bufbuild/buf-action posts a status comment on the PR by default. | |
| # Without pull-requests: write the post fails with 'Resource not | |
| # accessible by integration' even when the lint itself passed — | |
| # which used to surface as a red CI on every PR. Disable the | |
| # comment instead of broadening token scope; GitHub annotations | |
| # already surface buf errors inline on the diff. | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: bufbuild/buf-action@v1 | |
| with: | |
| lint: true | |
| pr_comment: false | |
| sqlc-check: | |
| name: SQLc Verify | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: sqlc-dev/setup-sqlc@v5 | |
| with: | |
| sqlc-version: '1.30.0' | |
| - run: sqlc diff | |
| load-bench: | |
| name: Load harness (smoke) | |
| runs-on: ubuntu-latest | |
| # The login-storm bench fans out 100 concurrent Argon2id verifies at | |
| # 96 MiB each (~10 GiB peak). On a runner that's also under memory | |
| # pressure this thrashes the page cache for tens of minutes before | |
| # the GitHub default 6 h kill. Cap explicitly — the documented | |
| # wall-clock budget is <30 s, so anything past 10 min is a runaway. | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.26" | |
| # Phase-3 §3.6: smoke-run the perf benchmarks at -benchtime=1x | |
| # so a regression that broke compilation, panicked under load, | |
| # or blew up O(N²) shows up in CI without paying for a full | |
| # statistical run. Operators wanting throughput numbers run | |
| # `go test -bench . -benchtime=10s ./internal/loadtest/...` | |
| # locally or in a nightly job. | |
| - name: Run load harness benches (1x) | |
| run: go test -run '^$' -bench . -benchtime=1x ./internal/loadtest/... | |
| trivy-fs: | |
| name: Trivy filesystem scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Trivy scan (fs) | |
| # Catches vulnerable Go modules (go.sum), npm packages | |
| # (web/package-lock.json), and known-bad config patterns. Severity | |
| # gate matches the npm-audit bar so behaviour is consistent across | |
| # ecosystems. | |
| uses: aquasecurity/trivy-action@v0.36.0 | |
| with: | |
| scan-type: fs | |
| scan-ref: . | |
| severity: HIGH,CRITICAL | |
| ignore-unfixed: true | |
| exit-code: '1' | |
| format: table | |
| trivy-dockerfile: | |
| name: Trivy Dockerfile scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Trivy config scan (Dockerfile) | |
| uses: aquasecurity/trivy-action@v0.36.0 | |
| with: | |
| scan-type: config | |
| scan-ref: Dockerfile | |
| severity: HIGH,CRITICAL | |
| exit-code: '1' | |
| format: table | |
| # Scans the *built* image — trivy-fs and trivy-dockerfile cover source | |
| # deps and Dockerfile misconfig, but neither catches a vulnerable OS | |
| # package baked into the final runtime layer (distroless/alpine CVEs, | |
| # transitively pulled libs). Build the image locally (no push) and run | |
| # Trivy in `image` mode with the same HIGH,CRITICAL + ignore-unfixed | |
| # gate the other Trivy jobs use, so behaviour stays consistent. | |
| trivy-image: | |
| name: Trivy image scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Build image (load into local docker) | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| push: false | |
| load: true | |
| tags: panvex:ci | |
| - name: Trivy scan (image) | |
| uses: aquasecurity/trivy-action@v0.36.0 | |
| with: | |
| scan-type: image | |
| image-ref: panvex:ci | |
| severity: HIGH,CRITICAL | |
| ignore-unfixed: true | |
| exit-code: '1' | |
| format: table | |
| # Plan 1 (S-21 security bootstrap) regression gate. Pure grep checks — | |
| # fast (no compile, no deps) — that fail CI if a future refactor undoes | |
| # any of the security fixes landed in S-1..S-9 / T-4 / T-5. Runs in | |
| # parallel with the slower test/lint jobs so it surfaces a regression | |
| # within seconds of the PR opening. | |
| security-gate: | |
| name: Security regression gate (Plan 1) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Plan 1 regression checks | |
| run: | | |
| set -eu | |
| # S-2: SPKI pin must remain wired at the bootstrap dialer. | |
| if ! grep -q 'newBootstrapTLSConfig\|VerifyPeerCertificate' cmd/control-plane/serve.go cmd/control-plane/bootstrap_tls.go; then | |
| echo "S-2 regression: SPKI pin gone from bootstrap dialer" >&2 | |
| exit 1 | |
| fi | |
| # S-1: bootstrap-admin CLI must validate the password source. | |
| if ! grep -q 'validatePasswordSource' cmd/control-plane/bootstrap_admin.go; then | |
| echo "S-1 regression: validatePasswordSource gone from bootstrap-admin" >&2 | |
| exit 1 | |
| fi | |
| # T-5: install-agent.sh must self-check its own hash before exec. | |
| # Canonical script lives at deploy/install-agent.sh after ba6aadc; | |
| # the server-package mirror is .gitignored and only materialised | |
| # by `go generate`, so grep the source-of-truth path here. | |
| if ! grep -q 'panvex_self_check_hash' deploy/install-agent.sh; then | |
| echo "T-5 regression: install-agent.sh self-check gone" >&2 | |
| exit 1 | |
| fi | |
| # T-4: install command issuance must bind the script hash. | |
| if ! grep -q 'ScriptHash\b' internal/controlplane/bootstrap/install.go; then | |
| echo "T-4 regression: ScriptHash binding gone from install command" >&2 | |
| exit 1 | |
| fi | |
| # S-8: deploy/install.sh must not reintroduce unconditional `curl -k`. | |
| # The CURL_INSECURE_FLAG path is the sanctioned (loopback-only) escape. | |
| if grep -nE 'curl[[:space:]]+[^|]*-k\b' deploy/install.sh | grep -v 'CURL_INSECURE_FLAG'; then | |
| echo "S-8 regression: unconditional curl -k reintroduced in deploy/install.sh" >&2 | |
| exit 1 | |
| fi | |
| # S-7: agent listener must keep TLS 1.3 floor. | |
| if ! grep -q 'tls.VersionTLS13' internal/agent/transport/listen.go; then | |
| echo "S-7 regression: TLS 1.3 minimum gone from agent listener" >&2 | |
| exit 1 | |
| fi | |
| # S-9: install-command TTL must stay minute-scale; reject hour-scale. | |
| if grep -nE 'installCommandTTL[[:space:]]*=[[:space:]]*[0-9]+[[:space:]]*\*[[:space:]]*time\.Hour' internal/controlplane/bootstrap/install.go; then | |
| echo "S-9 regression: installCommandTTL bumped to hour-scale" >&2 | |
| exit 1 | |
| fi | |
| echo "All Plan 1 security gates pass." | |
| shellcheck: | |
| name: ShellCheck | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| # CodeQL covers Go + JS but not shell. install-agent.sh, deploy/ | |
| # install.sh and the dev fleet scripts each touch host state on | |
| # operator machines, so a shell-injection bug there is high blast | |
| # radius. ludeeus/action-shellcheck enumerates *.sh by default | |
| # and respects shellcheckdirective comments inline. | |
| - uses: ludeeus/action-shellcheck@2.0.0 | |
| env: | |
| SHELLCHECK_OPTS: -e SC1091 | |
| with: | |
| severity: warning | |
| ignore_paths: >- | |
| web/node_modules | |
| node_modules | |
| # Per-PR supply-chain check: refuses any new dependency with a known | |
| # vuln (>= moderate) or a license outside the allow-list. CodeQL + | |
| # govulncheck + npm audit + Trivy already gate post-merge / on-tip, | |
| # but they don't surface a new transitive coming in via package-lock | |
| # diff during review. dependency-review-action does exactly that. | |
| dependency-review: | |
| name: Dependency Review | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/dependency-review-action@v5 | |
| with: | |
| fail-on-severity: moderate | |
| comment-summary-in-pr: on-failure | |
| gitleaks: | |
| name: gitleaks (secret scan) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| # Full history so gitleaks scans every commit on the branch, | |
| # not just the tip — leaks often slip in mid-branch and get | |
| # "fixed" by a follow-up commit. | |
| fetch-depth: 0 | |
| - uses: gitleaks/gitleaks-action@v3 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} | |
| # Tokenless reporting: action still fails the job on findings | |
| # but does not post back to the gitleaks dashboard. | |
| GITLEAKS_NOTIFY_USER_LIST: '' |