# API Pipeline - Mosaic Stack
# Quality gates, build, and Docker publish for @mosaic/api
#
# Triggers on: apps/api/**, packages/**, root configs
# Security chain: source audit + Trivy container scan

when:
  - event: [push, pull_request, manual]
    path:
      include:
        - "apps/api/**"
        - "packages/**"
        - "pnpm-lock.yaml"
        - "pnpm-workspace.yaml"
        - "turbo.json"
        - "package.json"
        - ".woodpecker/api.yml"

variables:
  - &node_image "node:24-alpine"
  - &install_deps |
    corepack enable
    pnpm install --frozen-lockfile
  - &use_deps |
    corepack enable
  - &kaniko_setup |
    mkdir -p /kaniko/.docker
    echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$GITEA_USER\",\"password\":\"$GITEA_TOKEN\"}}}" > /kaniko/.docker/config.json

services:
  postgres:
    image: postgres:17.7-alpine3.22
    environment:
      POSTGRES_DB: test_db
      POSTGRES_USER: test_user
      POSTGRES_PASSWORD: test_password

steps:
  # === Quality Gates ===

  install:
    image: *node_image
    commands:
      - *install_deps

  security-audit:
    image: *node_image
    commands:
      - *use_deps
      - pnpm audit --audit-level=high
    depends_on:
      - install

  lint:
    image: *node_image
    environment:
      SKIP_ENV_VALIDATION: "true"
    commands:
      - *use_deps
      - pnpm --filter "@mosaic/api" lint
    depends_on:
      - prisma-generate
      - build-shared

  prisma-generate:
    image: *node_image
    environment:
      SKIP_ENV_VALIDATION: "true"
    commands:
      - *use_deps
      - pnpm --filter "@mosaic/api" prisma:generate
    depends_on:
      - install

  build-shared:
    image: *node_image
    environment:
      SKIP_ENV_VALIDATION: "true"
    commands:
      - *use_deps
      - pnpm --filter "@mosaic/shared" build
    depends_on:
      - install

  typecheck:
    image: *node_image
    environment:
      SKIP_ENV_VALIDATION: "true"
    commands:
      - *use_deps
      - pnpm --filter "@mosaic/api" typecheck
    depends_on:
      - prisma-generate
      - build-shared

  prisma-migrate:
    image: *node_image
    environment:
      SKIP_ENV_VALIDATION: "true"
      DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/test_db?schema=public"
    commands:
      - *use_deps
      - pnpm --filter "@mosaic/api" prisma migrate deploy
    depends_on:
      - prisma-generate

  test:
    image: *node_image
    environment:
      SKIP_ENV_VALIDATION: "true"
      DATABASE_URL: "postgresql://test_user:test_password@postgres:5432/test_db?schema=public"
      ENCRYPTION_KEY: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    commands:
      - *use_deps
      - pnpm --filter "@mosaic/api" exec vitest run --exclude 'src/auth/auth-rls.integration.spec.ts' --exclude 'src/credentials/user-credential.model.spec.ts' --exclude 'src/job-events/job-events.performance.spec.ts' --exclude 'src/knowledge/services/fulltext-search.spec.ts' --exclude 'src/mosaic-telemetry/mosaic-telemetry.module.spec.ts'
    depends_on:
      - prisma-migrate

  # === Build ===

  build:
    image: *node_image
    environment:
      SKIP_ENV_VALIDATION: "true"
      NODE_ENV: "production"
    commands:
      - *use_deps
      - pnpm turbo build --filter=@mosaic/api
    depends_on:
      - lint
      - typecheck
      - test
      - security-audit

  # === Docker Build & Push ===

  docker-build-api:
    image: gcr.io/kaniko-project/executor:debug
    environment:
      GITEA_USER:
        from_secret: gitea_username
      GITEA_TOKEN:
        from_secret: gitea_token
      CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
      CI_COMMIT_TAG: ${CI_COMMIT_TAG}
    commands:
      - *kaniko_setup
      - |
        DESTINATIONS=""
        if [ -n "$CI_COMMIT_TAG" ]; then
          DESTINATIONS="--destination git.mosaicstack.dev/mosaic/stack-api:$CI_COMMIT_TAG"
        elif [ "$CI_COMMIT_BRANCH" = "main" ]; then
          DESTINATIONS="--destination git.mosaicstack.dev/mosaic/stack-api:latest"
        elif [ "$CI_COMMIT_BRANCH" = "develop" ]; then
          DESTINATIONS="--destination git.mosaicstack.dev/mosaic/stack-api:dev"
        fi
        /kaniko/executor --context . --dockerfile apps/api/Dockerfile --snapshot-mode=redo $DESTINATIONS
    when:
      - branch: [main, develop]
        event: [push, manual, tag]
    depends_on:
      - build

  # === Container Security Scan ===

  security-trivy-api:
    image: aquasec/trivy:latest
    environment:
      GITEA_USER:
        from_secret: gitea_username
      GITEA_TOKEN:
        from_secret: gitea_token
      CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
      CI_COMMIT_TAG: ${CI_COMMIT_TAG}
    commands:
      - |
        if [ -n "$$CI_COMMIT_TAG" ]; then
          SCAN_TAG="$$CI_COMMIT_TAG"
        elif [ "$$CI_COMMIT_BRANCH" = "main" ]; then
          SCAN_TAG="latest"
        else
          SCAN_TAG="dev"
        fi
        mkdir -p ~/.docker
        echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$$GITEA_USER\",\"password\":\"$$GITEA_TOKEN\"}}}" > ~/.docker/config.json
        trivy image --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed \
          --ignorefile .trivyignore \
          git.mosaicstack.dev/mosaic/stack-api:$$SCAN_TAG
    when:
      - branch: [main, develop]
        event: [push, manual, tag]
    depends_on:
      - docker-build-api

  # === Package Linking ===

  link-packages:
    image: alpine:3
    environment:
      GITEA_TOKEN:
        from_secret: gitea_token
    commands:
      - apk add --no-cache curl
      - sleep 10
      - |
        set -e
        link_package() {
          PKG="$$1"
          echo "Linking $$PKG..."
          for attempt in 1 2 3; do
            STATUS=$$(curl -s -o /tmp/link-response.txt -w "%{http_code}" -X POST \
              -H "Authorization: token $$GITEA_TOKEN" \
              "https://git.mosaicstack.dev/api/v1/packages/mosaic/container/$$PKG/-/link/stack")
            if [ "$$STATUS" = "201" ] || [ "$$STATUS" = "204" ]; then
              echo "  Linked $$PKG"
              return 0
            elif [ "$$STATUS" = "400" ]; then
              echo "  $$PKG already linked"
              return 0
            elif [ "$$STATUS" = "404" ] && [ $$attempt -lt 3 ]; then
              echo "  $$PKG not found yet, retrying in 5s (attempt $$attempt/3)..."
              sleep 5
            else
              echo "  FAILED: $$PKG status $$STATUS"
              cat /tmp/link-response.txt
              return 1
            fi
          done
        }
        link_package "stack-api"
    when:
      - branch: [main, develop]
        event: [push, manual, tag]
    depends_on:
      - security-trivy-api
