name: Veza CI on: push: branches: ["main", "remediation/*", "feature/mvp-complete"] pull_request: branches: ["main", "feature/mvp-complete"] workflow_dispatch: env: GIT_SSL_NO_VERIFY: "true" NODE_TLS_REJECT_UNAUTHORIZED: "0" jobs: # =========================================================================== # Backend (Go) — build, test, lint, security # =========================================================================== backend: name: Backend (Go) runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Go uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version: "1.25" cache: true # go.mod/go.sum live under veza-backend-api, not repo root. # Without this, setup-go warns "Dependencies file is not # found" and skips the mod cache → adds ~60-90s per run. cache-dependency-path: veza-backend-api/go.sum - name: Cache Go tool binaries id: go-tools-cache uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: ~/go/bin key: ${{ runner.os }}-go-tools-govulncheck-golangci-lint-v2 # Save the cache even when later steps (Lint, Test, etc.) # fail so the next run benefits from the installed tools. save-always: true - name: Install Go tools # NOTE: golangci-lint v2 lives under the /v2/ module path. # The old /cmd/ path still resolves to v1.64.x, which rejects # v2-format .golangci.yml with "please use golangci-lint v2". # Pinned versions so the cache key stays stable. if: steps.go-tools-cache.outputs.cache-hit != 'true' run: | go install golang.org/x/vuln/cmd/govulncheck@latest go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest - name: Add ~/go/bin to PATH run: echo "$HOME/go/bin" >> $GITHUB_PATH - name: Build run: go build ./... working-directory: veza-backend-api - name: Test # -short + VEZA_SKIP_INTEGRATION=1 so testcontainers-go (which # needs a Docker socket) is not invoked on the Forgejo runner. # Integration tests run in a dedicated nightly job with DinD. run: go test ./... -short -count=1 -timeout 300s -coverprofile=coverage.out env: VEZA_SKIP_INTEGRATION: "1" working-directory: veza-backend-api - name: Lint run: golangci-lint run ./... --timeout 5m working-directory: veza-backend-api - name: Vet run: go vet ./... working-directory: veza-backend-api - name: Vulnerability check run: govulncheck ./... working-directory: veza-backend-api - name: Coverage summary run: | COVERAGE=$(go tool cover -func=coverage.out | grep total | awk '{print $3}') echo "## Backend Coverage: $COVERAGE" >> $GITHUB_STEP_SUMMARY working-directory: veza-backend-api # =========================================================================== # Frontend (Web) — lint, typecheck, build, unit tests # =========================================================================== frontend: name: Frontend (Web) runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Use Node.js uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 with: node-version: "20" cache: "npm" cache-dependency-path: package-lock.json - name: Install dependencies run: npm ci # Sprint 2 design-system migrated to Style Dictionary; the # generated tokens live in packages/design-system/dist/ which # is gitignored. apps/web imports `@veza/design-system/tokens-generated`, # so dist/ MUST exist before tsc/vitest/build runs. # `prepare` in the package would normally cover npm ci, but # this explicit step makes the dependency loud and runnable # standalone for local debugging. - name: Build design tokens run: npm run build:tokens --workspace=@veza/design-system # Prevents drift between veza-backend-api/openapi.yaml and # apps/web/src/types/generated/. Regenerates then fails if # git diff is non-empty. - name: Check OpenAPI types in sync run: bash scripts/check-types-sync.sh working-directory: apps/web - name: Lint # ESLint warning baseline freeze (v1.0.10 polish): # 1204 is the current count of legacy warnings (mostly the # custom no-restricted-syntax 721, plus @typescript-eslint # no-non-null-assertion 139, no-unused-vars 134, # no-explicit-any 115, react-hooks/exhaustive-deps 47). # CI fails on ANY new warning. Lower this number as warnings # are resorbed by feature work; never raise it. run: npx eslint --max-warnings=1204 . working-directory: apps/web - name: Typecheck run: npx tsc --noEmit working-directory: apps/web - name: Build run: npm run build working-directory: apps/web - name: Bundle size gate run: node scripts/check-bundle-size.mjs working-directory: apps/web - name: Audit dependencies run: npm audit --audit-level=critical - name: Unit tests run: npx vitest run --reporter=verbose working-directory: apps/web # =========================================================================== # Rust (Stream Server) — build, test, lint, audit # =========================================================================== rust: name: Rust (Stream Server) runs-on: ubuntu-latest timeout-minutes: 20 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Cache rustup toolchain id: rustup-cache uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: | ~/.rustup ~/.cargo/bin key: ${{ runner.os }}-rustup-stable-rustfmt-clippy-audit-tarpaulin save-always: true - name: Set up Rust if: steps.rustup-cache.outputs.cache-hit != 'true' run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --component rustfmt,clippy - name: Add ~/.cargo/bin to PATH run: echo "$HOME/.cargo/bin" >> $GITHUB_PATH - name: Cache Cargo deps and target uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: | ~/.cargo/registry ~/.cargo/git veza-stream-server/target key: ${{ runner.os }}-cargo-${{ hashFiles('veza-stream-server/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo- save-always: true - name: Build run: cargo build working-directory: veza-stream-server - name: Test run: cargo test --workspace working-directory: veza-stream-server - name: Clippy # NOTE: -D warnings temporarily lifted while the team resorbs # the Rust clippy backlog (~20 warnings: unused imports, # missing Default impls, manual clamp/contains, etc.). # Re-enable once the backlog is cleared. run: cargo clippy --all-targets working-directory: veza-stream-server - name: Format check run: cargo fmt -- --check working-directory: veza-stream-server - name: Security audit # cargo-audit is cached with the rustup toolchain (~/.cargo/bin), # so the install is a no-op on warm cache. run: | command -v cargo-audit >/dev/null || cargo install cargo-audit --locked cargo audit working-directory: veza-stream-server # Rust coverage via cargo-tarpaulin is disabled in ci.yml because # tarpaulin needs CAP_SYS_PTRACE to disable ASLR, which the Docker # container running the Forgejo act runner doesn't grant: # "ERROR cargo_tarpaulin: Failed to run tests: # ASLR disable failed: EPERM: Operation not permitted" # Either (a) add `privileged: true` to the runner's container # config to grant ptrace, or (b) switch to `cargo llvm-cov` # which uses source-based coverage and doesn't need ptrace. # Until then, run coverage locally or in a dedicated nightly job. # =========================================================================== # Notify on failure # =========================================================================== notify-failure: name: Notify on failure needs: [backend, frontend, rust] if: failure() runs-on: ubuntu-latest steps: - name: Summary run: echo "## ❌ CI Failed" >> $GITHUB_STEP_SUMMARY