# EN: Build & Deploy GoodGo Platform to K8s staging via Gitea Actions
# VI: Build & Deploy GoodGo Platform len K8s staging qua Gitea Actions
#
# Flow: Push → detect changes → batch Kaniko builds (parallel) → Harbor → kubectl deploy
# Runner: act_runner (host mode, in-cluster kubectl)
# Build: Kaniko Jobs clone from Gitea, build Dockerfiles, push to Harbor

name: Build & Deploy to K8s

on:
  push:
    branches: [master, main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Clone source
        run: |
          git clone --depth 2 --branch ${{ github.ref_name }} \
            https://admin:${{ secrets.REPO_PASSWORD }}@gitea.techbi.org/admin/pos-system.git .

      - name: Detect changed services
        run: |
          IMAGE_TAG=$(echo "${{ github.sha }}" | cut -c1-7)
          echo "IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_ENV

          CHANGED=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "ALL")

          SERVICES=""
          check_change() {
            if echo "$CHANGED" | grep -q "^${1}/" || echo "$CHANGED" | grep -q "ALL"; then
              SERVICES="${SERVICES} ${2}"
            fi
          }

          check_change "services/iam-service-net" "iam-service"
          check_change "services/merchant-service-net" "merchant-service"
          check_change "services/order-service-net" "order-service"
          check_change "services/fnb-engine-net" "fnb-engine"
          check_change "services/catalog-service-net" "catalog-service"
          check_change "services/inventory-service-net" "inventory-service"
          check_change "services/wallet-service-net" "wallet-service"
          check_change "services/storage-service-net" "storage-service"
          check_change "services/booking-service-net" "booking-service"
          check_change "services/chat-service-net" "chat-service"
          check_change "services/social-service-net" "social-service"
          check_change "services/promotion-service-net" "promotion-service"
          check_change "services/membership-service-net" "membership-service"
          check_change "services/mining-service-net" "mining-service"
          check_change "services/mission-service-net" "mission-service"
          check_change "services/ads-manager-service-net" "ads-manager-service"
          check_change "services/ads-serving-service-net" "ads-serving-service"
          check_change "services/ads-billing-service-net" "ads-billing-service"
          check_change "services/ads-tracking-service-net" "ads-tracking-service"
          check_change "services/ads-analytics-service-net" "ads-analytics-service"
          check_change "services/mkt-facebook-service-net" "mkt-facebook-service"
          check_change "services/mkt-whatsapp-service-net" "mkt-whatsapp-service"
          check_change "services/mkt-x-service-net" "mkt-x-service"
          check_change "services/mkt-zalo-service-net" "mkt-zalo-service"
          check_change "apps/web-client-tpos-net" "pos-web"

          K8S_CONFIG=""
          if echo "$CHANGED" | grep -q "^deployments/staging/"; then
            K8S_CONFIG="yes"
          fi

          echo "CHANGED_SERVICES=${SERVICES}" >> $GITHUB_ENV
          echo "K8S_CONFIG=${K8S_CONFIG}" >> $GITHUB_ENV
          echo "Changed services:${SERVICES:-none}"
          echo "K8s config changed: ${K8S_CONFIG:-no}"

      - name: Setup Kaniko registry secret
        run: |
          BUILD_SERVICES=""
          for svc in $CHANGED_SERVICES; do BUILD_SERVICES="${BUILD_SERVICES} ${svc}"; done
          if [ -z "$BUILD_SERVICES" ]; then echo "No services to build"; exit 0; fi

          kubectl create secret docker-registry kaniko-harbor-secret -n staging \
            --docker-server=harbor.techbi.org \
            --docker-username=${{ secrets.HARBOR_USERNAME }} \
            --docker-password=${{ secrets.HARBOR_PASSWORD }} \
            --docker-email=admin@techbi.org \
            --dry-run=client -o yaml | kubectl apply -f -

      - name: Build services via Kaniko (batched parallel)
        run: |
          if [ -z "$CHANGED_SERVICES" ]; then echo "No services to build"; exit 0; fi

          HARBOR="harbor.techbi.org"
          PROJECT="goodgo"
          GITEA_URL="https://admin:${{ secrets.REPO_PASSWORD }}@gitea.techbi.org/admin/pos-system.git"
          BRANCH="${{ github.ref_name }}"
          NS="staging"

          get_context() {
            case "$1" in
              iam-service) echo "services/iam-service-net" ;; merchant-service) echo "services/merchant-service-net" ;;
              order-service) echo "services/order-service-net" ;; fnb-engine) echo "services/fnb-engine-net" ;;
              catalog-service) echo "services/catalog-service-net" ;; inventory-service) echo "services/inventory-service-net" ;;
              wallet-service) echo "services/wallet-service-net" ;; storage-service) echo "services/storage-service-net" ;;
              booking-service) echo "services/booking-service-net" ;; chat-service) echo "services/chat-service-net" ;;
              social-service) echo "services/social-service-net" ;; promotion-service) echo "services/promotion-service-net" ;;
              membership-service) echo "services/membership-service-net" ;; mining-service) echo "services/mining-service-net" ;;
              mission-service) echo "services/mission-service-net" ;; ads-manager-service) echo "services/ads-manager-service-net" ;;
              ads-serving-service) echo "services/ads-serving-service-net" ;; ads-billing-service) echo "services/ads-billing-service-net" ;;
              ads-tracking-service) echo "services/ads-tracking-service-net" ;; ads-analytics-service) echo "services/ads-analytics-service-net" ;;
              mkt-facebook-service) echo "services/mkt-facebook-service-net" ;; mkt-whatsapp-service) echo "services/mkt-whatsapp-service-net" ;;
              mkt-x-service) echo "services/mkt-x-service-net" ;; mkt-zalo-service) echo "services/mkt-zalo-service-net" ;;
              pos-web) echo "apps/web-client-tpos-net" ;;
            esac
          }

          get_image() {
            case "$1" in pos-web) echo "web-client-tpos-net" ;; *) echo "${1}-net" ;; esac
          }

          create_kaniko_job() {
            local svc="$1"
            local ctx=$(get_context "$svc")
            local img=$(get_image "$svc")
            local full="${HARBOR}/${PROJECT}/${img}"
            local job="kaniko-${svc}-${IMAGE_TAG}"

            # Use initContainer to clone repo, then kaniko builds from local context
            cat <<JOBEOF | kubectl apply -f -
          apiVersion: batch/v1
          kind: Job
          metadata:
            name: ${job}
            namespace: ${NS}
            labels:
              build-batch: "${IMAGE_TAG}"
          spec:
            backoffLimit: 1
            ttlSecondsAfterFinished: 600
            template:
              spec:
                initContainers:
                - name: clone
                  image: alpine/git:latest
                  command: ["sh", "-c"]
                  args:
                  - |
                    git clone --depth 1 --branch ${BRANCH} ${GITEA_URL} /workspace/repo
                  volumeMounts:
                  - name: workspace
                    mountPath: /workspace
                containers:
                - name: kaniko
                  image: gcr.io/kaniko-project/executor:latest
                  args:
                  - "--dockerfile=Dockerfile"
                  - "--context=/workspace/repo/${ctx}"
                  - "--destination=${full}:${IMAGE_TAG}"
                  - "--destination=${full}:latest"
                  - "--cache=false"
                  - "--skip-tls-verify"
                  - "--push-retry=2"
                  volumeMounts:
                  - name: docker-config
                    mountPath: /kaniko/.docker
                  - name: workspace
                    mountPath: /workspace
                  resources:
                    requests:
                      cpu: 500m
                      memory: 1Gi
                    limits:
                      cpu: "2"
                      memory: 4Gi
                restartPolicy: Never
                volumes:
                - name: docker-config
                  secret:
                    secretName: kaniko-harbor-secret
                    items:
                    - key: .dockerconfigjson
                      path: config.json
                - name: workspace
                  emptyDir: {}
          JOBEOF
            echo "Created job ${job}"
          }

          # Convert to array and process in batches of 5
          set -- $CHANGED_SERVICES
          BATCH=0
          TOTAL_FAILED=0

          while [ $# -gt 0 ]; do
            BATCH=$((BATCH + 1))
            BATCH_JOBS=""
            COUNT=0

            # Create up to 5 Kaniko jobs
            while [ $# -gt 0 ] && [ $COUNT -lt 5 ]; do
              create_kaniko_job "$1"
              BATCH_JOBS="${BATCH_JOBS} kaniko-${1}-${IMAGE_TAG}"
              shift
              COUNT=$((COUNT + 1))
            done

            echo ""
            echo "=== Batch ${BATCH}: Waiting for ${COUNT} builds ==="

            # Wait for all jobs in this batch
            for job in $BATCH_JOBS; do
              echo "Waiting for ${job}..."
              if kubectl wait --for=condition=complete "job/${job}" -n ${NS} --timeout=600s 2>/dev/null; then
                echo "  ✅ ${job} succeeded"
              else
                echo "  ❌ ${job} FAILED"
                kubectl logs "job/${job}" -n ${NS} --tail=20 2>/dev/null
                TOTAL_FAILED=$((TOTAL_FAILED + 1))
              fi
            done
          done

          echo ""
          echo "=== Build Summary ==="
          echo "Total batches: ${BATCH}"
          echo "Failed: ${TOTAL_FAILED}"

          if [ $TOTAL_FAILED -gt 0 ]; then
            echo "ERROR: ${TOTAL_FAILED} builds failed"
            exit 1
          fi

      - name: Apply K8s configs
        run: |
          if [ -n "$K8S_CONFIG" ]; then
            echo "=== Applying K8s configs ==="
            kubectl apply -f deployments/staging/kubernetes/configmap.yaml
            kubectl apply -f deployments/staging/kubernetes/redis.yaml
            kubectl apply -f deployments/staging/kubernetes/rabbitmq.yaml
            kubectl apply -f deployments/staging/kubernetes/minio.yaml
            kubectl apply -f deployments/staging/kubernetes/ingress.yaml
            kubectl apply -f deployments/staging/kubernetes/network-policy.yaml
          fi

      - name: Deploy services
        run: |
          if [ -z "$CHANGED_SERVICES" ] && [ -z "$K8S_CONFIG" ]; then
            echo "Nothing to deploy"
            exit 0
          fi

          HARBOR="harbor.techbi.org"
          PROJECT="goodgo"
          NS="staging"

          deploy_svc() {
            local name="$1" img="$2" manifest="$3"
            echo "=== Deploying ${name} ==="
            kubectl apply -f "deployments/staging/kubernetes/${manifest}"
            kubectl set image "deployment/${name}" "${name}=${HARBOR}/${PROJECT}/${img}:${IMAGE_TAG}" -n "${NS}" 2>/dev/null
            kubectl patch deployment "${name}" -n "${NS}" \
              -p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"harbor-pull-secret"}],"containers":[{"name":"'"${name}"'","imagePullPolicy":"Always"}]}}}}' 2>/dev/null
          }

          for svc in $CHANGED_SERVICES; do
            case "$svc" in
              iam-service)          deploy_svc "iam-service" "iam-service-net" "iam-service.yaml" ;;
              merchant-service)     deploy_svc "merchant-service" "merchant-service-net" "merchant-service.yaml" ;;
              order-service)        deploy_svc "order-service" "order-service-net" "order-service.yaml" ;;
              fnb-engine)           deploy_svc "fnb-engine" "fnb-engine-net" "fnb-engine.yaml" ;;
              catalog-service)      deploy_svc "catalog-service" "catalog-service-net" "catalog-service.yaml" ;;
              inventory-service)    deploy_svc "inventory-service" "inventory-service-net" "inventory-service.yaml" ;;
              wallet-service)       deploy_svc "wallet-service" "wallet-service-net" "wallet-service.yaml" ;;
              storage-service)      deploy_svc "storage-service" "storage-service-net" "storage-service.yaml" ;;
              booking-service)      deploy_svc "booking-service" "booking-service-net" "booking-service.yaml" ;;
              chat-service)         deploy_svc "chat-service" "chat-service-net" "chat-service.yaml" ;;
              social-service)       deploy_svc "social-service" "social-service-net" "social-service.yaml" ;;
              promotion-service)    deploy_svc "promotion-service" "promotion-service-net" "promotion-service.yaml" ;;
              membership-service)   deploy_svc "membership-service" "membership-service-net" "membership-service.yaml" ;;
              mining-service)       deploy_svc "mining-service" "mining-service-net" "mining-service.yaml" ;;
              mission-service)      deploy_svc "mission-service" "mission-service-net" "mission-service.yaml" ;;
              ads-manager-service)  deploy_svc "ads-manager-service" "ads-manager-service-net" "ads-manager-service.yaml" ;;
              ads-serving-service)  deploy_svc "ads-serving-service" "ads-serving-service-net" "ads-serving-service.yaml" ;;
              ads-billing-service)  deploy_svc "ads-billing-service" "ads-billing-service-net" "ads-billing-service.yaml" ;;
              ads-tracking-service) deploy_svc "ads-tracking-service" "ads-tracking-service-net" "ads-tracking-service.yaml" ;;
              ads-analytics-service) deploy_svc "ads-analytics-service" "ads-analytics-service-net" "ads-analytics-service.yaml" ;;
              mkt-facebook-service) deploy_svc "mkt-facebook-service" "mkt-facebook-service-net" "mkt-facebook-service.yaml" ;;
              mkt-whatsapp-service) deploy_svc "mkt-whatsapp-service" "mkt-whatsapp-service-net" "mkt-whatsapp-service.yaml" ;;
              mkt-x-service)        deploy_svc "mkt-x-service" "mkt-x-service-net" "mkt-x-service.yaml" ;;
              mkt-zalo-service)     deploy_svc "mkt-zalo-service" "mkt-zalo-service-net" "mkt-zalo-service.yaml" ;;
              pos-web)              deploy_svc "pos-web" "web-client-tpos-net" "pos-web.yaml" ;;
            esac
          done

          echo ""
          echo "=== Waiting for rollouts ==="
          FAILED=0
          for svc in $CHANGED_SERVICES; do
            if ! kubectl rollout status "deployment/${svc}" -n "${NS}" --timeout=180s 2>/dev/null; then
              echo "⚠️ ${svc} rollout incomplete"
              FAILED=$((FAILED + 1))
            fi
          done

          echo ""
          echo "=== Final Status ==="
          kubectl get pods -n "${NS}" --sort-by=.metadata.name
          echo ""
          kubectl get svc -n "${NS}" | grep -v cm-acme

          if [ $FAILED -gt 0 ]; then
            echo "⚠️ ${FAILED} service(s) did not complete rollout"
          fi
# Fri 10 Apr 2026 21:48:29 +07
