ci: gate prod deploy behind stage smoke test
Deploy marketing to Cloud Run / deploy (push) Has been cancelled

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_...
This commit is contained in:
Will Anderson
2026-05-02 19:02:17 -05:00
parent 88ee3d53de
commit 12ec770392
+71
View File
@@ -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 }}