diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index d7b5bd3..d0da2bb 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -353,49 +353,149 @@ jobs: run: docker builder prune -f --filter "until=1h" # ────────────────────────────────────────────────────────────────────────────── - # JOB 5: Health Check + # JOB 5: Post-Deploy Verification (Smoke Tests + Quality Gates) # ────────────────────────────────────────────────────────────────────────────── - healthcheck: - name: 🩺 Health Check + post_deploy_checks: + name: 🧪 Post-Deploy Verification needs: [prepare, deploy] - if: needs.deploy.result == 'success' + if: needs.deploy.result == 'success' && needs.prepare.outputs.target != 'branch' runs-on: docker container: image: catthehacker/ubuntu:act-latest steps: - - name: 🔍 Smoke Test + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 10 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: 🔐 Registry Auth run: | - URL="${{ needs.prepare.outputs.next_public_url }}" - echo "Checking health of $URL..." - for i in {1..12}; do - if curl -s -f -k -L "$URL" > /dev/null; then - echo "✅ Health check passed!" - exit 0 - fi - echo "Waiting for service to be ready... ($i/12)" - sleep 10 - done - echo "❌ Health check failed after 2 minutes." - exit 1 + echo "@mintel:registry=https://${{ vars.REGISTRY_HOST || 'npm.infra.mintel.me' }}" > .npmrc + echo "//${{ vars.REGISTRY_HOST || 'npm.infra.mintel.me' }}/:_authToken=${{ secrets.REGISTRY_PASS }}" >> .npmrc + - name: Install dependencies + id: deps + run: | + pnpm store prune + pnpm install --no-frozen-lockfile + - name: 📦 Cache APT Packages + uses: actions/cache@v4 + with: + path: /var/cache/apt/archives + key: apt-cache-${{ runner.os }}-${{ runner.arch }}-chromium + + - name: 💾 Cache Chromium + id: cache-chromium + uses: actions/cache@v4 + with: + path: /usr/bin/chromium + key: ${{ runner.os }}-chromium-native-${{ hashFiles('package.json') }} + + - name: 🔍 Install Chromium (Native & ARM64) + if: steps.cache-chromium.outputs.cache-hit != 'true' + run: | + rm -f /etc/apt/apt.conf.d/docker-clean + apt-get update + apt-get install -y gnupg wget ca-certificates + OS_ID=$(. /etc/os-release && echo $ID) + CODENAME=$(. /etc/os-release && echo $VERSION_CODENAME) + if [ "$OS_ID" = "debian" ]; then + apt-get install -y chromium + else + mkdir -p /etc/apt/keyrings + KEY_ID="82BB6851C64F6880" + wget -qO- "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x$KEY_ID" | gpg --dearmor > /etc/apt/keyrings/xtradeb.gpg + echo "deb [signed-by=/etc/apt/keyrings/xtradeb.gpg] http://ppa.launchpad.net/xtradeb/apps/ubuntu $CODENAME main" > /etc/apt/sources.list.d/xtradeb-ppa.list + printf "Package: *\nPin: release o=LP-PPA-xtradeb-apps\nPin-Priority: 1001\n" > /etc/apt/preferences.d/xtradeb + apt-get update + apt-get install -y --allow-downgrades chromium + fi + [ -f /usr/bin/chromium ] && ln -sf /usr/bin/chromium /usr/bin/google-chrome + [ -f /usr/bin/chromium ] && ln -sf /usr/bin/chromium /usr/bin/chromium-browser + + # ── Critical Smoke Tests (MUST pass) ────────────────────────────────── + - name: 🏥 CMS Deep Health Check + env: + DEPLOY_URL: ${{ needs.prepare.outputs.next_public_url }} + GK_PASS: ${{ secrets.GATEKEEPER_PASSWORD || vars.GATEKEEPER_PASSWORD }} + run: | + echo "Waiting 10s for app to fully start..." + sleep 10 + echo "Checking basic health..." + curl -sf "$DEPLOY_URL/health" || { echo "❌ Basic health check failed"; exit 1; } + echo "✅ Basic health OK" + + - name: 🌐 Core Smoke Tests (HTTP, API, Locale) + if: always() && steps.deps.outcome == 'success' + uses: https://git.infra.mintel.me/mmintel/at-mintel/.gitea/actions/core-smoke-tests@main + with: + TARGET_URL: ${{ needs.prepare.outputs.next_public_url }} + GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || vars.GATEKEEPER_PASSWORD }} + UMAMI_API_ENDPOINT: ${{ secrets.UMAMI_API_ENDPOINT || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN || vars.SENTRY_DSN }} + + - name: 📝 E2E Form Submission Test + if: always() && steps.deps.outcome == 'success' + env: + NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }} + GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || vars.GATEKEEPER_PASSWORD }} + PUPPETEER_EXECUTABLE_PATH: /usr/bin/chromium + run: pnpm test run # ────────────────────────────────────────────────────────────────────────────── # JOB 6: Notifications # ────────────────────────────────────────────────────────────────────────────── notifications: name: 🔔 Notify - needs: [prepare, deploy, healthcheck] + needs: [prepare, deploy, post_deploy_checks] if: always() runs-on: docker container: image: catthehacker/ubuntu:act-latest steps: - name: 🔔 Gotify + shell: bash run: | - STATUS="${{ needs.deploy.result }}" - TITLE="mb-grid-solutions.com: $STATUS" - [[ "$STATUS" == "success" ]] && PRIORITY=5 || PRIORITY=8 - + DEPLOY="${{ needs.deploy.result }}" + SMOKE="${{ needs.post_deploy_checks.result }}" + PERF="${{ needs.post_deploy_checks.result }}" + TARGET="${{ needs.prepare.outputs.target }}" + VERSION="${{ needs.prepare.outputs.image_tag }}" + URL="${{ needs.prepare.outputs.next_public_url }}" + + # Gotify priority scale: + # 1-3 = low (silent/info) + # 4-5 = normal + # 6-7 = high (warning) + # 8-10 = critical (alarm) + if [[ "$DEPLOY" != "success" ]]; then + PRIORITY=10 + EMOJI="🚨" + STATUS_LINE="DEPLOY FAILED" + elif [[ "$SMOKE" != "success" ]]; then + PRIORITY=8 + EMOJI="⚠️" + STATUS_LINE="Smoke tests failed" + elif [[ "$PERF" != "success" ]]; then + PRIORITY=5 + EMOJI="📉" + STATUS_LINE="Performance degraded" + else + PRIORITY=2 + EMOJI="✅" + STATUS_LINE="All checks passed" + fi + + TITLE="$EMOJI mb-grid-solutions.com $VERSION → $TARGET" + MESSAGE="$STATUS_LINE + Deploy: $DEPLOY | Smoke: $SMOKE | Perf: $PERF + $URL" + curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \ -F "title=$TITLE" \ - -F "message=Deploy to ${{ needs.prepare.outputs.target }} finished with status $STATUS.\nVersion: ${{ needs.prepare.outputs.image_tag }}" \ + -F "message=$MESSAGE" \ -F "priority=$PRIORITY" || true