name: CI
description: Runs Go tests, race detection, vulnerability scanning, and frontend quality checks.
on:
  push:
    branches:
      - main
      - develop
  pull_request:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  POSTGRES_USER: myuser
  POSTGRES_PASSWORD: mypassword
  POSTGRES_DB: mydb

jobs:
  vet:
    name: Build & Vet
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: sh
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: "1.26"
          cache: false

      - name: Cache Go modules
        uses: actions/cache@v4
        with:
          path: |
            ~/go/pkg/mod
            ~/.cache/go-build
          key: ${{ runner.os }}-go-${{ hashFiles('backend/go.sum') }}-vet
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Fix node toolcache path for Post-step cleanup
        run: |
          mkdir -p /opt/hostedtoolcache/node/22.23.1/x64/bin
          ln -sf /usr/local/bin/node /opt/hostedtoolcache/node/22.23.1/x64/bin/node

      - name: Build
        run: go build ./...
        working-directory: backend

      - name: Vet (dev tags)
        run: go vet -tags "test,dev" ./...
        working-directory: backend

      - name: Vet (prod tags)
        run: go vet -tags "test,!dev" ./...
        working-directory: backend

  test:
    name: Tests (${{ matrix.label }})
    needs: [vet, vulns]
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: sh
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: myuser
          POSTGRES_PASSWORD: mypassword
          POSTGRES_DB: mydb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    strategy:
      matrix:
        include:
          - label: dev
            gotags: test,dev
            verbose: -v
          - label: prod
            gotags: test,!dev
            verbose: ""

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: "1.26"
          cache: false

      - name: Cache Go modules
        uses: actions/cache@v4
        with:
          path: |
            ~/go/pkg/mod
            ~/.cache/go-build
          key: ${{ runner.os }}-go-${{ hashFiles('backend/go.sum') }}-test-${{ matrix.label }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Fix node toolcache path for Post-step cleanup
        run: |
          mkdir -p /opt/hostedtoolcache/node/22.23.1/x64/bin
          ln -sf /usr/local/bin/node /opt/hostedtoolcache/node/22.23.1/x64/bin/node

      - name: Install psql client
        run: apk add --no-cache postgresql-client

      - name: Wait for Postgres
        run: |
          for i in $(seq 1 30); do
            pg_isready -h postgres -U myuser && break
            sleep 1
          done

      - name: Create test database
        run: PGPASSWORD=mypassword psql -h postgres -U myuser -d mydb -c "CREATE DATABASE crussell_test_db;" 2>/dev/null || true

      - name: Run tests
        working-directory: backend
        run: go test -tags "${{ matrix.gotags }}" -count=1 ${{ matrix.verbose }} ./...
        env:
          POSTGRES_HOST: postgres
          TEST_DB_HOST: postgres

  race:
    name: Race (${{ matrix.label }})
    needs: [vet, vulns]
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: sh
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: myuser
          POSTGRES_PASSWORD: mypassword
          POSTGRES_DB: mydb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    strategy:
      matrix:
        include:
          - label: dev
            gotags: test,dev
          - label: prod
            gotags: test,!dev

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: "1.26"
          cache: false

      - name: Cache Go modules
        uses: actions/cache@v4
        with:
          path: |
            ~/go/pkg/mod
            ~/.cache/go-build
          key: ${{ runner.os }}-go-${{ hashFiles('backend/go.sum') }}-race-${{ matrix.label }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Fix node toolcache path for Post-step cleanup
        run: |
          mkdir -p /opt/hostedtoolcache/node/22.23.1/x64/bin
          ln -sf /usr/local/bin/node /opt/hostedtoolcache/node/22.23.1/x64/bin/node

      - name: Install psql + build-base
        run: apk add --no-cache postgresql-client build-base

      - name: Wait for Postgres
        run: |
          for i in $(seq 1 30); do
            pg_isready -h postgres -U myuser && break
            sleep 1
          done

      - name: Create test database
        run: PGPASSWORD=mypassword psql -h postgres -U myuser -d mydb -c "CREATE DATABASE crussell_test_db;" 2>/dev/null || true

      - name: Run race detector
        working-directory: backend
        run: go test -tags "${{ matrix.gotags }}" -race -count=1 ./...
        env:
          POSTGRES_HOST: postgres
          TEST_DB_HOST: postgres
          CGO_ENABLED: "1"

  vulns:
    name: Go vulnerabilities
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: sh
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: "1.26"
          cache: false

      - name: Cache Go modules
        uses: actions/cache@v4
        with:
          path: |
            ~/go/pkg/mod
            ~/.cache/go-build
          key: ${{ runner.os }}-go-${{ hashFiles('backend/go.sum') }}-vulns
          restore-keys: |
            ${{ runner.os }}-go-

      - name: Fix node toolcache path for Post-step cleanup
        run: |
          mkdir -p /opt/hostedtoolcache/node/22.23.1/x64/bin
          ln -sf /usr/local/bin/node /opt/hostedtoolcache/node/22.23.1/x64/bin/node

      - name: Go vulnerability scan
        working-directory: backend
        run: |
          go install golang.org/x/vuln/cmd/govulncheck@latest
          govulncheck ./...

  frontend-build:
    name: Frontend build (gate)
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: sh
    steps:
      - uses: actions/checkout@v4

      - name: Fix node toolcache path for Post-step cleanup
        run: |
          mkdir -p /opt/hostedtoolcache/node/22.23.1/x64/bin
          ln -sf /usr/local/bin/node /opt/hostedtoolcache/node/22.23.1/x64/bin/node

      - name: Cache npm dependencies
        uses: actions/cache@v4
        with:
          path: |
            ~/.npm
            frontend/node_modules
          key: ${{ runner.os }}-npm-${{ hashFiles('frontend/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-

      - name: Install dependencies
        run: cd frontend && npm ci

      - name: Build
        run: cd frontend && npm run build

  frontend-qc:
    name: Frontend QC (${{ matrix.task }})
    needs: [frontend-build]
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: sh
    strategy:
      matrix:
        include:
          - task: typecheck
            cmd: npm run check
          - task: lint
            cmd: npm run lint
          - task: audit
            cmd: npm audit --audit-level=info
    steps:
      - uses: actions/checkout@v4

      - name: Fix node toolcache path for Post-step cleanup
        run: |
          mkdir -p /opt/hostedtoolcache/node/22.23.1/x64/bin
          ln -sf /usr/local/bin/node /opt/hostedtoolcache/node/22.23.1/x64/bin/node

      - name: Restore npm dependencies
        uses: actions/cache@v4
        with:
          path: |
            ~/.npm
            frontend/node_modules
          key: ${{ runner.os }}-npm-${{ hashFiles('frontend/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-

      - name: Install dependencies
        run: cd frontend && npm ci

      - name: Run QC task
        run: cd frontend && ${{ matrix.cmd }}
