From e2661d66f5ecbfd72c98f37dfa59820027b68111 Mon Sep 17 00:00:00 2001 From: Guido X Jansen Date: Tue, 10 Mar 2026 19:46:29 +0100 Subject: [PATCH] feat(ci): add Docker smoke test to deploy-staging pipeline (#74) * chore: add .worktrees to .gitignore * feat(ci): add Docker smoke test to deploy-staging pipeline Split the monolithic build-and-deploy job into three stages: 1. build: builds and pushes images to GHCR 2. smoke-test: pulls edge images, starts full stack in CI, verifies all services become healthy and pass smoke-test.sh 3. deploy: only runs if smoke test passes, deploys to VPS This catches broken Docker images (missing env vars, broken startup, config issues) before they reach the staging VPS. Previously, broken images would be deployed and only caught by post-deploy health checks, requiring a rollback. Also adds missing barazo-plugins checkout needed by the API Dockerfile. Closes singi-labs/barazo-workspace#33 --- .github/workflows/deploy-staging.yml | 157 +++++++++++++++++++++++---- .gitignore | 1 + 2 files changed, 135 insertions(+), 23 deletions(-) diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index e334d2b..dff3d68 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -30,18 +30,22 @@ concurrency: cancel-in-progress: false jobs: - build-and-deploy: - name: Build & Deploy to Staging + # ====================================================================== + # Job 1: Build and push Docker images to GHCR + # ====================================================================== + build: + name: Build Images runs-on: ubuntu-latest permissions: contents: read packages: write - timeout-minutes: 20 + timeout-minutes: 15 + outputs: + api_ref: ${{ steps.refs.outputs.api_ref }} + web_ref: ${{ steps.refs.outputs.web_ref }} + trigger: ${{ steps.refs.outputs.trigger }} steps: - # ---------------------------------------------------------------------- - # Determine refs (from dispatch payload or manual input) - # ---------------------------------------------------------------------- - name: Determine checkout refs id: refs run: | @@ -61,11 +65,11 @@ jobs: echo "api_ref=$API_REF" >> "$GITHUB_OUTPUT" echo "web_ref=$WEB_REF" >> "$GITHUB_OUTPUT" echo "trigger=$TRIGGER" >> "$GITHUB_OUTPUT" - echo "::notice::Deploying api@$API_REF web@$WEB_REF (trigger: $TRIGGER)" + echo "::notice::Building api@$API_REF web@$WEB_REF (trigger: $TRIGGER)" - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ # Checkout all repos (public -- no PAT needed) - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ - name: Checkout barazo-deploy uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -111,9 +115,9 @@ jobs: ref: ${{ steps.refs.outputs.web_ref }} path: barazo-web - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ # Docker setup - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ - name: Set up Docker Buildx uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 @@ -124,9 +128,9 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ # Build and push images (amd64 only for staging) - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ - name: Build and push barazo-api uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 with: @@ -153,9 +157,116 @@ jobs: cache-to: type=gha,mode=max,scope=web platforms: linux/amd64 - # ---------------------------------------------------------------------- - # Deploy to staging VPS - # ---------------------------------------------------------------------- + # ====================================================================== + # Job 2: Smoke test -- verify images start and respond to health checks + # ====================================================================== + smoke-test: + name: Smoke Test + needs: build + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + timeout-minutes: 5 + + env: + POSTGRES_USER: barazo + POSTGRES_PASSWORD: ci_smoke_test + POSTGRES_DB: barazo + VALKEY_PASSWORD: ci_smoke_test + DATABASE_URL: postgresql://barazo:ci_smoke_test@postgres:5432/barazo + TAP_ADMIN_PASSWORD: ci_smoke_test + SESSION_SECRET: ci_session_secret_not_real_extend_to_32 + RELAY_URL: wss://bsky.network + COMMUNITY_DID: did:plc:ci-smoke-test + COMMUNITY_NAME: CI Smoke Test + COMMUNITY_DOMAIN: ":80" + COMMUNITY_MODE: single + OAUTH_CLIENT_ID: https://ci-smoke.barazo.forum/client-metadata.json + OAUTH_REDIRECT_URI: http://127.0.0.1/api/auth/callback + NEXT_PUBLIC_SITE_URL: http://127.0.0.1 + BARAZO_API_VERSION: edge + BARAZO_WEB_VERSION: edge + + steps: + - name: Checkout barazo-deploy + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Login to Container Registry + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create smoke test fixtures + run: | + # Empty plugin manifest (no plugins needed for smoke test) + echo '[]' > plugins.json + # No-op plugin install script + printf '#!/bin/sh\nexit 0\n' > scripts/install-plugins.sh + chmod +x scripts/install-plugins.sh + + - name: Start full stack + run: docker compose -f docker-compose.yml up -d + + - name: Wait for services to be healthy + run: | + echo "Waiting for services to become healthy..." + TIMEOUT=120 + ELAPSED=0 + INTERVAL=5 + + while [ $ELAPSED -lt $TIMEOUT ]; do + HEALTHY=$(docker compose -f docker-compose.yml ps --format json 2>/dev/null \ + | grep -c '"Health":"healthy"' || echo "0") + + # postgres, valkey, barazo-api, barazo-web, caddy = 5 services with healthchecks + if [ "$HEALTHY" -ge 5 ]; then + echo "All services healthy after ${ELAPSED}s" + break + fi + + echo " ${HEALTHY}/5 healthy (${ELAPSED}s elapsed)..." + sleep $INTERVAL + ELAPSED=$((ELAPSED + INTERVAL)) + done + + if [ $ELAPSED -ge $TIMEOUT ]; then + echo "::error::Timed out waiting for services to be healthy" + docker compose -f docker-compose.yml ps + docker compose -f docker-compose.yml logs --tail=50 + exit 1 + fi + + - name: Run smoke tests + run: ./scripts/smoke-test.sh + + - name: Dump logs on failure + if: failure() + run: | + echo "=== Service Status ===" + docker compose -f docker-compose.yml ps + echo "" + echo "=== Service Logs ===" + docker compose -f docker-compose.yml logs --tail=100 + + - name: Tear down + if: always() + run: docker compose -f docker-compose.yml down -v + + # ====================================================================== + # Job 3: Deploy to staging VPS (only if smoke test passes) + # ====================================================================== + deploy: + name: Deploy to Staging + needs: [build, smoke-test] + runs-on: ubuntu-latest + permissions: + contents: read + timeout-minutes: 10 + + steps: - name: Sync configuration files to VPS uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1 with: @@ -232,9 +343,9 @@ jobs: echo "Deploy complete at $(date -u '+%Y-%m-%dT%H:%M:%SZ')" - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ # Post-deploy health checks - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ - name: Wait for containers to start run: sleep 15 @@ -350,9 +461,9 @@ jobs: echo "Response: $RESPONSE" echo "This may indicate an OAuth configuration issue" - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ # Rollback on failure - # ---------------------------------------------------------------------- + # ------------------------------------------------------------------ - name: Rollback on failure if: failure() && (steps.deploy.outcome == 'failure' || steps.log-check.outcome == 'failure' || steps.health-check.outcome == 'failure') uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1 @@ -391,8 +502,8 @@ jobs: run: | echo "## Staging Deploy Complete" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" - echo "- **Trigger:** ${{ steps.refs.outputs.trigger }}" >> "$GITHUB_STEP_SUMMARY" - echo "- **API ref:** ${{ steps.refs.outputs.api_ref }}" >> "$GITHUB_STEP_SUMMARY" - echo "- **Web ref:** ${{ steps.refs.outputs.web_ref }}" >> "$GITHUB_STEP_SUMMARY" + echo "- **Trigger:** ${{ needs.build.outputs.trigger }}" >> "$GITHUB_STEP_SUMMARY" + echo "- **API ref:** ${{ needs.build.outputs.api_ref }}" >> "$GITHUB_STEP_SUMMARY" + echo "- **Web ref:** ${{ needs.build.outputs.web_ref }}" >> "$GITHUB_STEP_SUMMARY" echo "- **Run:** #${{ github.run_number }}" >> "$GITHUB_STEP_SUMMARY" echo "- **URL:** https://staging.barazo.forum" >> "$GITHUB_STEP_SUMMARY" diff --git a/.gitignore b/.gitignore index dc82d1e..c974669 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ Thumbs.db .idea/ *.swp *.swo +.worktrees/ -- 2.51.2