# CI/CD Pipeline for persona-community-5
# Components will add their build steps below the marker
#
# TODO: Templatize registry URL — replace hardcoded registry.threesix.ai with
#       {{REGISTRY_URL}} so the registry is configurable per environment.

clone:
  git:
    image: woodpeckerci/plugin-git
    settings:
      depth: 1

steps:
  deps:
    depends_on: []
    image: golang:1.25
    commands:
      - go work sync
      - |
        for dir in services/*/; do
          if [ -f "$dir/go.mod" ]; then
            (cd "$dir" && go mod tidy)
          fi
        done
      - |
        for dir in workers/*/; do
          if [ -f "$dir/go.mod" ]; then
            (cd "$dir" && go mod tidy)
          fi
        done
      - |
        for dir in cli/*/; do
          if [ -f "$dir/go.mod" ]; then
            (cd "$dir" && go mod tidy)
          fi
        done
    when:
      branch: main
      event: push

  # Pre-flight registry health check before builds
  preflight:
    depends_on: [deps]
    image: alpine/curl
    commands:
      - |
        echo "==> Checking registry health before builds"
        HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --insecure --connect-timeout 10 https://registry.threesix.ai/v2/)
        if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "401" ]; then
          echo "ERROR: Registry unhealthy (HTTP $HTTP_CODE), aborting build"
          echo "Registry must return 200 or 401 (auth required) to proceed"
          exit 1
        fi
        echo "==> Registry healthy (HTTP $HTTP_CODE)"
    when:
      branch: main
      event: push

  # COMPONENT_STEPS_BELOW

  # Woodpecker CI step for creator-ui React app
  # Add this step to your .woodpecker.yml
  
  build-creator-ui:
    depends_on: [preflight]
    image: woodpeckerci/plugin-kaniko
    settings:
      registry: registry.threesix.ai
      repo: persona-community-5/creator-ui
      tags:
        - latest
        - ${CI_COMMIT_SHA:0:8}
      context: .
      dockerfile: apps/creator-ui/Dockerfile
      cache: true
      skip-tls-verify: true
    when:
      branch: main
      event: push
  
  verify-creator-ui:
    depends_on: [build-creator-ui]
    image: alpine/curl
    failure: ignore
    commands:
      - |
        TAG="${CI_COMMIT_SHA:0:8}"
        REPO="persona-community-5/creator-ui"
        REGISTRY="registry.threesix.ai"
        echo "==> Verifying image $REGISTRY/$REPO:$TAG exists in registry"
        HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
          --insecure \
          --connect-timeout 10 \
          --max-time 15 \
          "https://$REGISTRY/v2/$REPO/manifests/$TAG" \
          -H "Accept: application/vnd.docker.distribution.manifest.v2+json")
        if [ "$HTTP_CODE" = "200" ]; then
          echo "==> Image verified: $REGISTRY/$REPO:$TAG"
          exit 0
        elif [ "$HTTP_CODE" = "404" ]; then
          echo "==> WARNING: Image $REGISTRY/$REPO:$TAG not found in registry"
          echo "    Build may have failed. Deploy will be skipped."
          exit 1
        else
          echo "==> WARNING: Registry check returned HTTP $HTTP_CODE"
          exit 0
        fi
    when:
      branch: main
      event: push
  
  deploy-creator-ui:
    depends_on: [verify-creator-ui]
    image: bitnami/kubectl:latest
    commands:
      - echo "==> Deploying creator-ui with image tag ${CI_COMMIT_SHA:0:8}"
      - kubectl set image deployment/persona-community-5-creator-ui persona-community-5-creator-ui=registry.threesix.ai/persona-community-5/creator-ui:${CI_COMMIT_SHA:0:8} -n projects
      - kubectl patch deployment/persona-community-5-creator-ui -n projects -p '{"spec":{"replicas":1}}'
      - |
        echo "==> Verifying deployment persona-community-5-creator-ui"
        ACTUAL_IMAGE=$(kubectl get deployment/persona-community-5-creator-ui -n projects -o jsonpath='{.spec.template.spec.containers[0].image}')
        EXPECTED_IMAGE="registry.threesix.ai/persona-community-5/creator-ui:${CI_COMMIT_SHA:0:8}"
        if [ "$ACTUAL_IMAGE" != "$EXPECTED_IMAGE" ]; then
          echo "FATAL: Image mismatch after deploy"
          echo "  expected: $EXPECTED_IMAGE"
          echo "  actual:   $ACTUAL_IMAGE"
          exit 1
        fi
        echo "==> Image confirmed: $ACTUAL_IMAGE"
        echo "==> Waiting for rollout (timeout 120s)..."
        kubectl rollout status deployment/persona-community-5-creator-ui -n projects --timeout=120s
    when:
      branch: main
      event: push

  # Woodpecker CI step for media-worker worker
  # Add this step to your .woodpecker.yml
  
  build-media-worker:
    depends_on: [preflight]
    image: woodpeckerci/plugin-kaniko
    settings:
      registry: registry.threesix.ai
      repo: persona-community-5/media-worker
      tags:
        - latest
        - ${CI_COMMIT_SHA:0:8}
      context: .
      dockerfile: workers/media-worker/Dockerfile
      cache: true
      skip-tls-verify: true
    when:
      branch: main
      event: push
  
  verify-media-worker:
    depends_on: [build-media-worker]
    image: alpine/curl
    failure: ignore
    commands:
      - |
        TAG="${CI_COMMIT_SHA:0:8}"
        REPO="persona-community-5/media-worker"
        REGISTRY="registry.threesix.ai"
        echo "==> Verifying image $REGISTRY/$REPO:$TAG exists in registry"
        HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
          --insecure \
          --connect-timeout 10 \
          --max-time 15 \
          "https://$REGISTRY/v2/$REPO/manifests/$TAG" \
          -H "Accept: application/vnd.docker.distribution.manifest.v2+json")
        if [ "$HTTP_CODE" = "200" ]; then
          echo "==> Image verified: $REGISTRY/$REPO:$TAG"
          exit 0
        elif [ "$HTTP_CODE" = "404" ]; then
          echo "==> WARNING: Image $REGISTRY/$REPO:$TAG not found in registry"
          echo "    Build may have failed. Deploy will be skipped."
          exit 1
        else
          echo "==> WARNING: Registry check returned HTTP $HTTP_CODE"
          exit 0
        fi
    when:
      branch: main
      event: push
  
  deploy-media-worker:
    depends_on: [verify-media-worker]
    image: bitnami/kubectl:latest
    commands:
      - echo "==> Deploying media-worker with image tag ${CI_COMMIT_SHA:0:8}"
      - kubectl set image deployment/persona-community-5-media-worker persona-community-5-media-worker=registry.threesix.ai/persona-community-5/media-worker:${CI_COMMIT_SHA:0:8} -n projects
      - kubectl patch deployment/persona-community-5-media-worker -n projects -p '{"spec":{"replicas":1}}'
      - |
        echo "==> Verifying deployment persona-community-5-media-worker"
        ACTUAL_IMAGE=$(kubectl get deployment/persona-community-5-media-worker -n projects -o jsonpath='{.spec.template.spec.containers[0].image}')
        EXPECTED_IMAGE="registry.threesix.ai/persona-community-5/media-worker:${CI_COMMIT_SHA:0:8}"
        if [ "$ACTUAL_IMAGE" != "$EXPECTED_IMAGE" ]; then
          echo "FATAL: Image mismatch after deploy"
          echo "  expected: $EXPECTED_IMAGE"
          echo "  actual:   $ACTUAL_IMAGE"
          exit 1
        fi
        echo "==> Image confirmed: $ACTUAL_IMAGE"
        echo "==> Waiting for rollout (timeout 120s)..."
        kubectl rollout status deployment/persona-community-5-media-worker -n projects --timeout=120s
    when:
      branch: main
      event: push

  # Woodpecker CI step for persona-api service
  # Add this step to your .woodpecker.yml
  # NOTE: verify step is replicated in all component templates (service, app-react,
  # app-astro, app-nextjs, worker). Update all 5 if changing the verify logic.
  
  build-persona-api:
    depends_on: [preflight]
    image: woodpeckerci/plugin-kaniko
    settings:
      registry: registry.threesix.ai
      repo: persona-community-5/persona-api
      tags:
        - latest
        - ${CI_COMMIT_SHA:0:8}
      context: .
      dockerfile: services/persona-api/Dockerfile
      cache: true
      skip-tls-verify: true
    when:
      branch: main
      event: push
  
  verify-persona-api:
    depends_on: [build-persona-api]
    image: alpine/curl
    failure: ignore
    commands:
      - |
        TAG="${CI_COMMIT_SHA:0:8}"
        REPO="persona-community-5/persona-api"
        REGISTRY="registry.threesix.ai"
        echo "==> Verifying image $REGISTRY/$REPO:$TAG exists in registry"
        HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
          --insecure \
          --connect-timeout 10 \
          --max-time 15 \
          "https://$REGISTRY/v2/$REPO/manifests/$TAG" \
          -H "Accept: application/vnd.docker.distribution.manifest.v2+json")
        if [ "$HTTP_CODE" = "200" ]; then
          echo "==> Image verified: $REGISTRY/$REPO:$TAG"
          exit 0
        elif [ "$HTTP_CODE" = "404" ]; then
          echo "==> WARNING: Image $REGISTRY/$REPO:$TAG not found in registry"
          echo "    Build may have failed. Deploy will be skipped."
          exit 1
        else
          echo "==> WARNING: Registry check returned HTTP $HTTP_CODE"
          exit 0
        fi
    when:
      branch: main
      event: push
  
  deploy-persona-api:
    depends_on: [verify-persona-api]
    image: bitnami/kubectl:latest
    commands:
      - echo "==> Deploying persona-api with image tag ${CI_COMMIT_SHA:0:8}"
      - kubectl set image deployment/persona-community-5-persona-api persona-community-5-persona-api=registry.threesix.ai/persona-community-5/persona-api:${CI_COMMIT_SHA:0:8} -n projects
      - kubectl patch deployment/persona-community-5-persona-api -n projects -p '{"spec":{"replicas":1}}'
      - |
        echo "==> Verifying deployment persona-community-5-persona-api"
        ACTUAL_IMAGE=$(kubectl get deployment/persona-community-5-persona-api -n projects -o jsonpath='{.spec.template.spec.containers[0].image}')
        EXPECTED_IMAGE="registry.threesix.ai/persona-community-5/persona-api:${CI_COMMIT_SHA:0:8}"
        if [ "$ACTUAL_IMAGE" != "$EXPECTED_IMAGE" ]; then
          echo "FATAL: Image mismatch after deploy"
          echo "  expected: $EXPECTED_IMAGE"
          echo "  actual:   $ACTUAL_IMAGE"
          exit 1
        fi
        echo "==> Image confirmed: $ACTUAL_IMAGE"
        echo "==> Waiting for rollout (timeout 120s)..."
        kubectl rollout status deployment/persona-community-5-persona-api -n projects --timeout=120s
    when:
      branch: main
      event: push
  # Do not remove the marker above - component steps are inserted here

  # Sync point after all component builds/deploys complete
  # depends_on is updated dynamically when components are added
  build-complete:
    depends_on: [preflight, deploy-persona-api, deploy-media-worker, deploy-creator-ui] # BUILD_COMPLETE_DEPS
    image: alpine:3.19
    commands:
      - echo "All component builds complete"
    when:
      branch: main
      event: push

  # Services deployed sync point - fires after all deployments complete
  # Use this to detect when services are ready (before docs generation)
  # This allows wait_pipeline to succeed before docs steps run
  services-deployed:
    depends_on: [build-complete]
    image: alpine:3.19
    commands:
      - echo "==> All services deployed successfully"
      - echo "    Pipeline is now considered successful for service deployment"
      - echo "    Documentation generation continues independently"
    when:
      branch: main
      event: push

  # Export OpenAPI specs from built services
  # Runs after services-deployed to ensure all services are ready
  # Uses failure:ignore so doc failures don't block pipeline success
  export-openapi:
    depends_on: [services-deployed]
    failure: ignore
    image: golang:1.25
    commands:
      - |
        echo "==> Exporting OpenAPI specs from services"
        for svc_dir in services/*/; do
          [ -d "$svc_dir" ] || continue
          svc=$(basename "$svc_dir")
          [ "$svc" = ".gitkeep" ] && continue

          if [ -f "$svc_dir/cmd/server/main.go" ]; then
            echo "    Exporting spec for $svc"
            (cd "$svc_dir" && go run ./cmd/server --export-openapi > openapi.json) || {
              echo "    WARNING: Failed to export spec for $svc"
              continue
            }
            echo "    Generated $svc_dir/openapi.json"
          fi
        done
    when:
      branch: main
      event: push

  # Generate API documentation from OpenAPI specs
  generate-docs:
    image: node:20-slim
    depends_on: [export-openapi]
    failure: ignore
    commands:
      - npm install -g widdershins
      - |
        echo "==> Generating Slate markdown from OpenAPI specs"
        mkdir -p docs/source/includes
        found_specs=0
        for spec in services/*/openapi.json; do
          if [ -f "$spec" ]; then
            svc=$(dirname "$spec" | xargs basename)
            echo "    Converting $svc"
            widdershins \
              --language_tabs 'shell:curl' 'go:Go' \
              --summary \
              --omitHeader \
              --resolve \
              --shallowSchemas \
              "$spec" \
              -o "docs/source/includes/_${svc}.md"
            found_specs=$((found_specs + 1))
          fi
        done
        echo "==> Converted $found_specs service specs"
    when:
      branch: main
      event: push

  # Build Slate static documentation (skipped if no docs infrastructure)
  build-docs:
    image: ruby:3.2-slim
    depends_on: [generate-docs]
    failure: ignore
    commands:
      - |
        if [ ! -d "docs" ] || [ ! -f "docs/Gemfile" ]; then
          echo "==> No docs/ directory or Gemfile found, skipping Slate build"
          exit 0
        fi
      - apt-get update && apt-get install -y build-essential nodejs
      - cd docs && bundle install --jobs 4
      - cd docs && bundle exec middleman build --clean
      - echo "==> Docs built to docs/build/"
    when:
      branch: main
      event: push

  # Build and push docs-nginx image (skipped if no docs build output)
  # failure: ignore allows pipeline to continue if docs weren't built
  build-docs-image:
    depends_on: [build-docs]
    image: woodpeckerci/plugin-kaniko
    failure: ignore
    settings:
      registry: registry.threesix.ai
      repo: persona-community-5-docs
      tags:
        - latest
        - ${CI_COMMIT_SHA:0:8}
      context: docs
      dockerfile: docs/Dockerfile.nginx
      cache: true
      skip-tls-verify: true
    when:
      branch: main
      event: push

  # Verify docs image exists in registry before deploying
  # Prevents ImagePullBackOff errors from missing/failed image builds
  verify-docs-image:
    image: alpine/curl
    depends_on: [build-docs-image]
    failure: ignore
    commands:
      - |
        TAG="${CI_COMMIT_SHA:0:8}"
        REPO="persona-community-5-docs"
        REGISTRY="registry.threesix.ai"

        # Check if docs were built (same check as deploy-docs)
        if [ ! -d "docs/build" ]; then
          echo "==> No docs build output, skipping verification"
          exit 0
        fi

        echo "==> Verifying image $REGISTRY/$REPO:$TAG exists in registry"

        # Query registry v2 API to check if manifest exists
        # Returns 200 if image exists, 404 if not
        HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
          --insecure \
          "https://$REGISTRY/v2/$REPO/manifests/$TAG" \
          -H "Accept: application/vnd.docker.distribution.manifest.v2+json")

        if [ "$HTTP_CODE" = "200" ]; then
          echo "==> Image verified: $REGISTRY/$REPO:$TAG"
          # Create marker file for deploy-docs to check
          touch /tmp/image-verified
          exit 0
        elif [ "$HTTP_CODE" = "404" ]; then
          echo "==> WARNING: Image $REGISTRY/$REPO:$TAG not found in registry"
          echo "    This may indicate the build step failed or is still pushing"
          echo "    Deploy step will be skipped to prevent ImagePullBackOff"
          exit 1
        else
          echo "==> WARNING: Registry check returned HTTP $HTTP_CODE"
          echo "    Proceeding cautiously - deploy may fail if image missing"
          exit 0
        fi
    when:
      branch: main
      event: push

  # Deploy docs to docs.me4yrdax.threesix.ai (skipped if no docs image was built or verified)
  deploy-docs:
    image: bitnami/kubectl:latest
    depends_on: [verify-docs-image]
    failure: ignore
    commands:
      - |
        # Check if docs image exists by trying to describe the deployment
        # If this is the first build, the deployment won't exist yet
        if [ ! -d "docs/build" ]; then
          echo "==> No docs build output, skipping deployment"
          exit 0
        fi
      - |
        cat <<EOF | kubectl apply -f -
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: persona-community-5-docs
          namespace: projects
          labels:
            app: persona-community-5-docs
            project: persona-community-5
        spec:
          replicas: 1
          selector:
            matchLabels:
              app: persona-community-5-docs
          template:
            metadata:
              labels:
                app: persona-community-5-docs
                project: persona-community-5
            spec:
              containers:
              - name: nginx
                image: registry.threesix.ai/persona-community-5-docs:${CI_COMMIT_SHA:0:8}
                ports:
                - containerPort: 80
                resources:
                  requests:
                    cpu: 10m
                    memory: 16Mi
                  limits:
                    cpu: 100m
                    memory: 64Mi
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: persona-community-5-docs
          namespace: projects
          labels:
            app: persona-community-5-docs
            project: persona-community-5
        spec:
          selector:
            app: persona-community-5-docs
          ports:
          - port: 80
            targetPort: 80
        ---
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        metadata:
          name: persona-community-5-docs
          namespace: projects
          labels:
            app: persona-community-5-docs
            project: persona-community-5
          annotations:
            cert-manager.io/cluster-issuer: letsencrypt-prod
            traefik.ingress.kubernetes.io/router.entrypoints: websecure
            traefik.ingress.kubernetes.io/router.tls: "true"
        spec:
          ingressClassName: traefik
          tls:
          - hosts:
            - docs.me4yrdax.threesix.ai
            secretName: docs-me4yrdax.threesix.ai-tls
          rules:
          - host: docs.me4yrdax.threesix.ai
            http:
              paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: persona-community-5-docs
                    port:
                      number: 80
        EOF
      - kubectl rollout restart deployment/persona-community-5-docs -n projects
      - kubectl rollout status deployment/persona-community-5-docs -n projects --timeout=120s
    when:
      branch: main
      event: push

  verify:
    depends_on: [services-deployed]
    image: bitnami/kubectl:latest
    commands:
      - echo "Pipeline complete for persona-community-5"
      - kubectl get deployments -n projects -l project=persona-community-5 --no-headers || true
    when:
      branch: main
      event: push
