Files
Sauci/.github/workflows/deploy-supabase.yml
T
jjbrunton 01bb8225ae chore(harness): make repository agent-ready
Add concise routed context, task-aware agent roles, focused skills, and structured learning promotion.\n\nUnify local development and workspace tooling, add layered test and E2E verification, harden MCP authentication, and gate deployment behind verification and explicit production promotion.
2026-08-27 20:58:48 +01:00

186 lines
7.2 KiB
YAML

name: Deploy Supabase to Production
on:
workflow_dispatch:
inputs:
deploy_production:
description: Promote the verified change to production after non-production
required: false
type: boolean
default: false
push:
branches:
- master
paths:
- 'apps/supabase/**'
- 'terraform/**'
- '.github/workflows/deploy-supabase.yml'
jobs:
quality:
name: Verify deployable source
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run verify:fast
- run: npm run build
deploy-database:
name: Deploy Migrations & Functions
runs-on: ubuntu-latest
needs: quality
environment: ${{ matrix.environment }}
strategy:
max-parallel: 1
matrix:
environment: ${{ fromJSON(github.event_name == 'workflow_dispatch' && inputs.deploy_production && '["non-prod","prod"]' || '["non-prod"]') }}
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
SUPABASE_PROJECT_REF: ${{ matrix.environment == 'prod' && 'ckjcrkjpmhqhiucifukx' || 'itbzhrvlgvdmzbnhzhyx' }}
ENVIRONMENT_NAME: ${{ matrix.environment }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest
- name: Link to ${{ matrix.environment }} Project
run: supabase link --project-ref $SUPABASE_PROJECT_REF --workdir apps
- name: Apply Database Migrations (${{ matrix.environment }})
run: supabase db push --workdir apps --include-all
- name: Sync Edge Function Secrets (${{ matrix.environment }})
env:
# Add secrets from GitHub Secrets
REVENUECAT_WEBHOOK_SECRET: ${{ secrets.REVENUECAT_WEBHOOK_SECRET }}
REVENUECAT_API_KEY: ${{ secrets.REVENUECAT_API_KEY }}
REVENUECAT_ENTITLEMENT_ID: ${{ secrets.REVENUECAT_ENTITLEMENT_ID }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
ADMIN_PRIVATE_KEY_JWK: ${{ secrets.ADMIN_PRIVATE_KEY_JWK }}
ADMIN_KEYS_JSON: ${{ secrets.ADMIN_KEYS_JSON }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: |
# Sync secrets to Supabase (only if they're set in GitHub)
secrets_to_set=""
[ -n "$REVENUECAT_WEBHOOK_SECRET" ] && secrets_to_set="$secrets_to_set REVENUECAT_WEBHOOK_SECRET=$REVENUECAT_WEBHOOK_SECRET"
[ -n "$REVENUECAT_API_KEY" ] && secrets_to_set="$secrets_to_set REVENUECAT_API_KEY=$REVENUECAT_API_KEY"
[ -n "$REVENUECAT_ENTITLEMENT_ID" ] && secrets_to_set="$secrets_to_set REVENUECAT_ENTITLEMENT_ID=$REVENUECAT_ENTITLEMENT_ID"
[ -n "$OPENROUTER_API_KEY" ] && secrets_to_set="$secrets_to_set OPENROUTER_API_KEY=$OPENROUTER_API_KEY"
[ -n "$ADMIN_PRIVATE_KEY_JWK" ] && secrets_to_set="$secrets_to_set ADMIN_PRIVATE_KEY_JWK=$ADMIN_PRIVATE_KEY_JWK"
[ -n "$ADMIN_KEYS_JSON" ] && secrets_to_set="$secrets_to_set ADMIN_KEYS_JSON=$ADMIN_KEYS_JSON"
[ -n "$DISCORD_WEBHOOK_URL" ] && secrets_to_set="$secrets_to_set DISCORD_WEBHOOK_URL=$DISCORD_WEBHOOK_URL"
if [ -n "$secrets_to_set" ]; then
echo "Syncing secrets to Supabase ($ENVIRONMENT_NAME)..."
supabase secrets set $secrets_to_set --project-ref $SUPABASE_PROJECT_REF
else
echo "No secrets to sync"
fi
- name: Deploy Edge Functions (${{ matrix.environment }})
run: |
# Deploy all edge functions (excluding _shared directory)
for dir in apps/supabase/functions/*/; do
function_name=$(basename "$dir")
# Skip _shared directory (it's a shared module, not a function)
if [ "$function_name" = "_shared" ]; then
echo "Skipping _shared (shared module)"
continue
fi
echo "Deploying function: $function_name to $ENVIRONMENT_NAME"
supabase functions deploy "$function_name" --no-verify-jwt --project-ref $SUPABASE_PROJECT_REF --workdir apps
done
- name: Remove Deleted Functions (${{ matrix.environment }})
run: |
# Get list of functions deployed on Supabase (skip header lines, extract slug column)
deployed=$(supabase functions list --project-ref $SUPABASE_PROJECT_REF | grep -E '^[a-z]' | awk '{print $1}')
# Get list of functions in the repo (excluding _shared)
local_functions=$(ls -d apps/supabase/functions/*/ 2>/dev/null | xargs -I {} basename {} | grep -v '^_shared$' || true)
# Delete any deployed functions that no longer exist in the repo
for func in $deployed; do
# Skip empty or invalid function names
[ -z "$func" ] && continue
[[ "$func" =~ ^[^a-z] ]] && continue
if ! echo "$local_functions" | grep -q "^${func}$"; then
echo "Deleting removed function from $ENVIRONMENT_NAME: $func"
supabase functions delete "$func" --project-ref $SUPABASE_PROJECT_REF
fi
done
deploy-infrastructure:
name: Deploy Infrastructure (Terraform)
runs-on: ubuntu-latest
needs: quality
env:
TF_VAR_supabase_access_token: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.6"
# Non-Production Environment
- name: Terraform Init (Non-Prod)
working-directory: terraform/environments/non-prod
run: terraform init
- name: Terraform Format Check
working-directory: terraform/modules/supabase
run: terraform fmt -check
- name: Terraform Validate (Non-Prod)
working-directory: terraform/environments/non-prod
run: terraform validate
- name: Terraform Plan (Non-Prod)
working-directory: terraform/environments/non-prod
run: terraform plan -out=tfplan
- name: Terraform Apply (Non-Prod)
working-directory: terraform/environments/non-prod
run: terraform apply -auto-approve tfplan
# Production Environment
- name: Terraform Init (Prod)
if: github.event_name == 'workflow_dispatch' && inputs.deploy_production
working-directory: terraform/environments/prod
run: terraform init
- name: Terraform Validate (Prod)
if: github.event_name == 'workflow_dispatch' && inputs.deploy_production
working-directory: terraform/environments/prod
run: terraform validate
- name: Terraform Plan (Prod)
if: github.event_name == 'workflow_dispatch' && inputs.deploy_production
working-directory: terraform/environments/prod
run: terraform plan -out=tfplan
- name: Terraform Apply (Prod)
if: github.event_name == 'workflow_dispatch' && inputs.deploy_production
working-directory: terraform/environments/prod
run: terraform apply -auto-approve tfplan