# GitHub Actions — Enterprise Level Deploy # Auto-generated by hmcmkit /pdca deploy # EKS + ArgoCD + Canary (Argo Rollouts) + Security Scan name: Enterprise Deploy on: push: branches: [main, develop, 'release/**'] pull_request: branches: [main] env: AWS_REGION: ap-northeast-2 ECR_REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.ap-northeast-2.amazonaws.com IMAGE_NAME: ${{ github.event.repository.name }} ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }} SENTRY_ORG: ${{ secrets.SENTRY_ORG }} SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} jobs: test-and-lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - run: npm run lint - run: npm test -- --coverage - name: Coverage gate (80%+) run: | COVERAGE=$(npx nyc report --reporter=text-summary | grep 'Statements' | awk '{print $3}' | tr -d '%') if (( $(echo "$COVERAGE < 80" | bc -l) )); then echo "Coverage $COVERAGE% is below 80% threshold" exit 1 fi security-scan: runs-on: ubuntu-latest needs: test-and-lint steps: - uses: actions/checkout@v4 - name: SAST — Semgrep uses: returntocorp/semgrep-action@v1 with: config: 'p/owasp-top-ten' - name: Container Scan — Trivy uses: aquasecurity/trivy-action@master with: scan-type: 'fs' severity: 'CRITICAL,HIGH' exit-code: '1' build-and-push: runs-on: ubuntu-latest needs: [test-and-lint, security-scan] permissions: id-token: write contents: read steps: - uses: actions/checkout@v4 - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: ${{ env.AWS_REGION }} - uses: aws-actions/amazon-ecr-login@v2 - uses: docker/build-push-action@v5 with: context: . push: true tags: | ${{ env.ECR_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} ${{ env.ECR_REGISTRY }}/${{ env.IMAGE_NAME }}:latest - name: Sentry Release — Create release & upload sourcemaps uses: getsentry/action-release@v1 env: SENTRY_AUTH_TOKEN: ${{ env.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ env.SENTRY_ORG }} SENTRY_PROJECT: ${{ env.SENTRY_PROJECT }} with: environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} version: ${{ github.sha }} sourcemaps: './apps/web/.next' set_commits: auto deploy-dev: if: github.ref == 'refs/heads/develop' runs-on: ubuntu-latest needs: build-and-push environment: development steps: - uses: actions/checkout@v4 - name: Deploy to DEV (Docker Compose on EC2) run: | echo "Deploying to DEV via Docker Compose" # ssh deploy@dev-server "cd /app && docker compose pull && docker compose up -d" deploy-staging: if: startsWith(github.ref, 'refs/heads/release/') runs-on: ubuntu-latest needs: build-and-push environment: staging steps: - uses: actions/checkout@v4 - name: Update Helm values for staging run: | sed -i "s|image.tag:.*|image.tag: ${{ github.sha }}|" infra/k8s/overlays/staging/values.yaml - name: Commit and push (ArgoCD auto-sync) run: | git config user.name "hmcmkit-deploy" git config user.email "deploy@hmcmkit.dev" git add infra/ git commit -m "chore(deploy): update staging image to ${{ github.sha }}" git push deploy-prod: if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest needs: build-and-push environment: name: production url: https://app.example.com steps: - uses: actions/checkout@v4 - name: Update Helm values for production (Canary) run: | sed -i "s|image.tag:.*|image.tag: ${{ github.sha }}|" infra/k8s/overlays/prod/values.yaml - name: Commit and push (ArgoCD + Argo Rollouts canary) run: | git config user.name "hmcmkit-deploy" git config user.email "deploy@hmcmkit.dev" git add infra/ git commit -m "chore(deploy): canary prod image to ${{ github.sha }}" git push post-deploy-monitoring: if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest needs: deploy-prod steps: - name: Wait for canary stabilization run: sleep 300 # 5min — canary 10% traffic period - name: Verify Sentry — no new critical issues run: | ISSUES=$(curl -s -H "Authorization: Bearer ${{ env.SENTRY_AUTH_TOKEN }}" \ "https://sentry.io/api/0/projects/${{ env.SENTRY_ORG }}/${{ env.SENTRY_PROJECT }}/issues/?query=is:unresolved+release:${{ github.sha }}&statsPeriod=5m" \ | jq 'length') echo "New unresolved issues: $ISSUES" if [ "$ISSUES" -gt "5" ]; then echo "::error::Too many new issues ($ISSUES) — triggering rollback" exit 1 fi - name: Verify Prometheus — error rate check run: | ERROR_RATE=$(curl -s "http://${{ secrets.PROMETHEUS_URL }}/api/v1/query?query=rate(http_requests_total{status=~\"5..\"}[5m])/rate(http_requests_total[5m])" \ | jq '.data.result[0].value[1]' -r 2>/dev/null || echo "0") echo "Current error rate: $ERROR_RATE" if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then echo "::error::Error rate $ERROR_RATE exceeds 1% threshold" exit 1 fi - name: On failure — trigger auto-rollback if: failure() run: | echo "::warning::Post-deploy checks failed — triggering Argo Rollouts abort" curl -X PUT "${{ env.ARGOCD_SERVER }}/api/v1/applications/${{ env.IMAGE_NAME }}/rollback" \ -H "Authorization: Bearer ${{ secrets.ARGOCD_TOKEN }}"