name: CI

on:
  pull_request:
    paths:
      - "**.go"
      - "go.mod"
      - "go.sum"
      - "frontend/**"
      - ".nvmrc"
  push:
    branches:
      - main
    paths:
      - "**.go"
      - "go.mod"
      - "go.sum"
      - "frontend/**"
      - ".nvmrc"

permissions:
  contents: read

jobs:
  go-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-go@v6
        with:
          go-version-file: go.mod
          cache: true
      - name: Stub internal/web/dist for go:embed
        run: mkdir -p internal/web/dist && touch internal/web/dist/.gitkeep
      - name: Test
        run: |
          go list ./... | grep -v '/frontend/node_modules/' > /tmp/go-packages.txt
          go test -shuffle=on -count=1 $(cat /tmp/go-packages.txt)

  codegen:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-go@v6
        with:
          go-version-file: go.mod
          cache: true
      - uses: actions/setup-node@v6
        with:
          node-version-file: .nvmrc
      - name: Regenerate schemas, examples and OpenAPI
        run: npm run gen
        working-directory: frontend
      - name: Fail if generated files are stale (run 'npm run gen' and commit)
        run: git diff --exit-code -- frontend/src/generated frontend/public/openapi.json

  govulncheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-go@v6
        with:
          go-version-file: go.mod
          cache: true
      - name: Stub internal/web/dist for go:embed
        run: mkdir -p internal/web/dist && touch internal/web/dist/.gitkeep
      - name: Install govulncheck
        run: go install golang.org/x/vuln/cmd/govulncheck@latest
      - name: Run govulncheck
        run: govulncheck ./...

  # Race + shuffle hygiene gate: data races and order-dependent tests fail the build.
  race:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-go@v6
        with:
          go-version-file: go.mod
          cache: true
      - name: Stub internal/web/dist for go:embed
        run: mkdir -p internal/web/dist && touch internal/web/dist/.gitkeep
      - name: Race + shuffle
        run: |
          go list ./... | grep -v '/frontend/node_modules/' > /tmp/go-packages.txt
          go test -race -shuffle=on -count=1 $(cat /tmp/go-packages.txt)

  # Brief native-fuzz smoke on the security-/parser-critical decoders. Each runs the
  # generated corpus plus 30s of exploration; a crash here is a real input-handling bug.
  fuzz-smoke:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-go@v6
        with:
          go-version-file: go.mod
          cache: true
      - name: Stub internal/web/dist for go:embed
        run: mkdir -p internal/web/dist && touch internal/web/dist/.gitkeep
      - name: Fuzz critical parsers (smoke)
        run: |
          go test -run '^$' -fuzz 'FuzzParseLink$' -fuzztime=30s ./internal/util/link/
          go test -run '^$' -fuzz 'FuzzDecodeCertPin$' -fuzztime=30s ./internal/web/runtime/

  frontend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v6
        with:
          node-version-file: .nvmrc
          cache: npm
          cache-dependency-path: frontend/package-lock.json
      - name: Install
        run: npm ci
        working-directory: frontend
      - name: Lint
        run: npm run lint
        working-directory: frontend
      - name: Typecheck
        run: npm run typecheck
        working-directory: frontend
      - name: Test
        run: npm test
        working-directory: frontend
      - name: Build
        run: npm run build
        working-directory: frontend
      - name: Audit
        run: npm audit --audit-level=high
        working-directory: frontend
