From 12ec7703920801d60b556301dfd3addf09444bc7 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Sat, 2 May 2026 19:02:17 -0500 Subject: [PATCH] ci: gate prod deploy behind stage smoke test Nothing reaches prod without deploying to marketing-stage first and passing a 90s HTTP smoke test. Stage uses test Stripe keys (stripe-secret-key-stage) so checkout can be exercised safely. Set STRIPE_PUBLISHABLE_KEY on the stage service manually once: gcloud run services update marketing-stage --region us-central1 \ --project neuron-785695 \ --update-env-vars STRIPE_PUBLISHABLE_KEY=pk_test_... --- .gitea/workflows/deploy.yaml | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index f9e39ac..eb82088 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -1,5 +1,8 @@ name: Deploy marketing to Cloud Run +# Pipeline: build → stage → smoke test → prod +# Nothing reaches prod without passing the stage smoke test. + on: push: branches: [main] @@ -188,6 +191,74 @@ jobs: docker push "${LATEST%:*}:latest" echo "Fast asset build complete" + # ── Stage deployment ────────────────────────────────────────────────────── + # marketing-stage is a single-region Cloud Run service (us-central1). + # It uses stage-specific Stripe keys (sk_test_...) so checkout can be + # exercised safely. Prod deploy only runs after the smoke test below passes. + # + # Note: STRIPE_PUBLISHABLE_KEY (pk_test_...) is not set here — set it + # manually on the service once with: + # gcloud run services update marketing-stage --region us-central1 \ + # --project neuron-785695 \ + # --update-env-vars STRIPE_PUBLISHABLE_KEY=pk_test_... + # + # --update-env-vars / --update-secrets merges with existing config so + # manually-set values like STRIPE_PUBLISHABLE_KEY are preserved across + # pipeline deploys. + + - name: Deploy to marketing-stage + id: deploy-stage + env: + IMAGE: ${{ steps.tag.outputs.image }} + run: | + set -euo pipefail + + gcloud run deploy marketing-stage \ + --image "$IMAGE" \ + --region us-central1 \ + --project neuron-785695 \ + --update-env-vars "NODE_ENV=production,GCS_SHARE_BUCKET=neuron-shares-prod,SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9jb2pzZ2hhb25sdHVuaWRrenB3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzc2NDIxNjgsImV4cCI6MjA5MzIxODE2OH0.e0FVFw1aahnrBVvnkR5R8a-RxCx095U8o_gsk7Quq3E,NEURON_LLM_0_FORMAT=anthropic,NEURON_LLM_0_MODEL=claude-sonnet-4-5,NEURON_LLM_0_URL=https://api.anthropic.com/v1/messages" \ + --update-secrets "SUPABASE_SERVICE_KEY=supabase-service-key:latest,NEURON_LLM_0_KEY=anthropic-api-key:latest,ANTHROPIC_API_KEY=anthropic-api-key:latest,STRIPE_SECRET_KEY=stripe-secret-key-stage:latest,STRIPE_WEBHOOK_SECRET=stripe-webhook-secret-stage:latest,STRIPE_PRICE_PROFESSIONAL=stripe-price-professional-stage:latest,STRIPE_PRICE_FOUNDING=stripe-price-founding-stage:latest,STRIPE_PRICE_FAMILY_CHILD=stripe-price-family-child:latest,RESEND_API_KEY=resend-api-key:latest,DOCUSEAL_WEBHOOK_TOKEN=docuseal-webhook-token:latest" \ + --allow-unauthenticated \ + --quiet + + # Get the stable Cloud Run URL and ensure NEURON_ORIGIN is set correctly. + # --update-env-vars is idempotent: no new revision is created if the + # value is already correct (i.e. on every deploy after the first). + STAGE_URL=$(gcloud run services describe marketing-stage \ + --region us-central1 --project neuron-785695 \ + --format 'value(status.url)') + echo "stage_url=${STAGE_URL}" >> "$GITHUB_OUTPUT" + echo "Stage URL: ${STAGE_URL}" + + gcloud run services update marketing-stage \ + --region us-central1 --project neuron-785695 \ + --update-env-vars "NEURON_ORIGIN=${STAGE_URL}" \ + --quiet + + - name: Smoke test stage + run: | + set -euo pipefail + STAGE_URL="${{ steps.deploy-stage.outputs.stage_url }}" + echo "Smoke testing stage: ${STAGE_URL}" + + # Poll up to 90s — container cold-start + El page compilation can take ~20s + for i in $(seq 1 18); do + STATUS=$(curl -sSo /dev/null -w "%{http_code}" --max-time 15 "${STAGE_URL}/" || echo "000") + echo "Attempt ${i}/18: HTTP ${STATUS}" + if [ "$STATUS" = "200" ]; then + echo "Stage smoke test PASSED" + exit 0 + fi + sleep 5 + done + + echo "Stage smoke test FAILED — aborting prod deploy" + exit 1 + + # ── Prod deployment ─────────────────────────────────────────────────────── + # Only runs if the stage smoke test above passed. + - name: Deploy to all marketing prod regions in parallel env: IMAGE: ${{ steps.tag.outputs.image }}