Rebuild Staging em Projeto Supabase NOVO
Cenário: você deletou o projeto staging antigo e criou um novo do zero no dashboard Supabase. Este runbook cobre o que
REBUILD_FROM_PROD.mdnão cobre (que assume staging existente e só reseta o schemapublic).Para staging existente: use
REBUILD_FROM_PROD.md.Última atualização: 2026-06-05
Por que precisa de runbook separado
pg_dump --schema=public traz tabelas, índices, funções, triggers e policies do schema public. NÃO traz:
| Item | Onde mora em prod | Como recriar em staging novo |
|---|---|---|
auth.users | schema auth (Supabase Auth) | Não recriar — login real não funciona. Dev users via seed |
| Storage objects (arquivos) | bucket S3 separado | Não recriar — paths antigos retornam 404 (aceitável em staging) |
| Storage buckets (metadata) | storage.buckets | Criar manualmente no dashboard Storage |
| Cron jobs | extensão pg_cron no schema cron | Reaplicar SQL de docs/operations/CRON_JOBS.md |
| Realtime publication | supabase_realtime publication | Reaplicar migration enable_realtime_* ou ALTER PUBLICATION |
| Edge function secrets | painel Supabase Functions → Secrets | Copiar de prod (ver supabase secrets list) |
| Edge functions (código) | deploy via supabase functions deploy | Loop deploy após rebuild |
Extensions (pg_cron, pgcrypto, etc.) | habilitadas no painel | Habilitar manualmente Database → Extensions |
Passo a passo
0. Backup paranoia de prod (recomendado)
pg_dump "$PGURL_PROD" -Fc --no-owner --no-acl \
-f /mnt/documents/prod-backup-$(date +%Y%m%d-%H%M).dump1. Delete projeto antigo + cria novo
No dashboard Supabase:
- Settings → General → Delete project (staging antigo)
- New project → escolher region/plan
- Aguardar provisionamento (~2 min)
2. Anote os novos valores
| Campo | Onde achar | Onde usar |
|---|---|---|
| Project ref | URL do dashboard (/project/<ref>) | PGURL_STAGING, secrets supabase functions deploy --project-ref |
| Connection string (postgres) | Settings → Database → Connection string (URI) | PGURL_STAGING |
| anon key | Settings → API → Project API keys → anon | .env, GitHub Secrets |
| service_role key | Settings → API → Project API keys → service_role | secrets Supabase functions |
| API URL | Settings → API → Project URL | .env, edge function secrets |
3. Habilita extensions necessárias
Dashboard → Database → Extensions → habilitar:
pg_cron(cron jobs)pg_net(HTTP do cron pra edge functions)pgcrypto(já vem em alguns planos)pg_trgm(se prod usar — checarSELECT extname FROM pg_extensionem prod)
Em SQL Editor:
SELECT extname FROM pg_extension ORDER BY extname;
-- comparar com prod, habilitar diferenças via dashboard4. Atualiza secrets locais / GitHub
Local (.env):
PGURL_STAGING="postgresql://postgres:<NEW_PWD>@db.<NEW_REF>.supabase.co:5432/postgres"GitHub repo secrets (Settings → Secrets and variables → Actions):
PGURL_STAGING← nova connection string- (
PGURL_PRODpermanece igual)
Supabase Functions secrets (no NOVO projeto, dashboard → Edge Functions → Secrets): Replicar TODOS os secrets do prod. Lista esperada (snapshot 2026-06-05):
CRON_SECRET, RESEND_API_KEY, RESEND_WEBHOOK_SECRET,
MP_ACCESS_TOKEN, OLP_PEPPER_SECRET, OLP_JWT_SECRET, OLP_PORTAL_SECRET,
TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER,
WASENDER_API_KEY, WASENDER_WEBHOOK_SECRET,
E2E_TEST_PASSWORD, NTFY_TOPIC,
SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, SUPABASE_URL, SUPABASE_DB_URL,
SUPABASE_JWKS, SUPABASE_PUBLISHABLE_KEY, SUPABASE_PUBLISHABLE_KEYS, SUPABASE_SECRET_KEYS,
LOVABLE_API_KEY⚠️ NÃO copiar valor de prod para todos — alguns DEVEM ser diferentes em staging:
OLP_JWT_SECRET,OLP_PORTAL_SECRET,OLP_PEPPER_SECRET: gerar novos (openssl rand -base64 64)SUPABASE_*: usar os valores DO PROJETO NOVO de stagingMP_ACCESS_TOKEN: usar token sandbox do MercadoPago, não o de prodNTFY_TOPIC: usar tópico de staging separado
5. Rebuild — duas opções
Opção A — CI (recomendado):
- GitHub → Actions → Staging Rebuild → Run workflow
- Marcar
fresh_project = true - Digitar
REBUILDno campo confirm - Aguardar approval no environment
staging-rebuild - Ver summary no fim do job
Opção B — Local:
bash scripts/staging/rebuild-from-prod.sh --fresh-project6. Pós-rebuild — recriar infra que não veio no dump
PGURL_STAGING="..." bash scripts/staging/post-rebuild-checklist.shO script lista o que falta. Para cada pendência:
Buckets ausentes: Dashboard → Storage → New bucket. Lista esperada:
banners-login(public),curso-thumbnails(public),tutoriais-thumbnails(public)mural-imagens(public),email-template-assets(public)feature-upsell-images(private)
Realtime publication vazia:
-- Em SQL Editor do staging, replicar migrations enable_realtime_*
-- Conferir tabelas em prod:
SELECT tablename FROM pg_publication_tables WHERE pubname='supabase_realtime';
-- Para cada uma faltando em staging:
ALTER PUBLICATION supabase_realtime ADD TABLE public.<tabela>;
ALTER TABLE public.<tabela> REPLICA IDENTITY FULL;Cron jobs ausentes: rodar SQL de docs/operations/CRON_JOBS.md no SQL Editor (substituindo URLs/keys pelo projeto novo).
7. Deploy edge functions
STAGING_REF="<novo-ref>"
for f in supabase/functions/*/; do
fname=$(basename "$f")
[[ "$fname" == "_shared" || "$fname" == "__tests__" ]] && continue
supabase functions deploy "$fname" --project-ref "$STAGING_REF"
done8. Deploy worker (Cloudflare)
cd workers/olp-msgqueue && wrangler deploy --env stagingAtualizar variáveis do worker (Cloudflare dashboard) com novas URLs/keys de staging.
9. Validação final
bun run staging:drift # BLOCKERS deve ser 0
bun run scripts/staging/post-rebuild-checklist.sh # Status: OK
bunx playwright test --grep @smokeRiscos conhecidos e aceitos
- Login real não funciona em staging —
auth.usersestá vazio. Usar Dev users. - Storage retorna 404 para arquivos antigos — paths apontam pro bucket prod. Aceitável.
- Webhooks de prod (Resend, MP, Wasender) chegam em prod, não em staging — staging precisa de webhooks separados configurados no painel de cada provider.
- Cookies antigos invalidados — secrets divergem, sessões precisam refazer login. Aceitável.
Referências
- Runbook reset destrutivo (staging existente):
REBUILD_FROM_PROD.md - Script de rebuild:
scripts/staging/rebuild-from-prod.sh --fresh-project - Workflow CI:
.github/workflows/staging-rebuild.yml - Validador pós-rebuild:
scripts/staging/post-rebuild-checklist.sh - Memória:
mem://infrastructure/staging/drift-detector-and-rebuild-protocol
Variante: projeto Supabase preservado com public vazio
Cenário comum: você não quer deletar o projeto Supabase (manter URL/secrets/buckets/auth providers), mas dropou todas as tabelas do public. O staging atual já está nesse estado.
Vantagens vs deletar e recriar:
- Não troca
SUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY,SUPABASE_JWKS,OLP_JWT_SECRET, etc. - Mantém os 6 storage buckets (
banners-login,curso-thumbnails,tutoriais-thumbnails,mural-imagens,email-template-assets,feature-upsell-images). - Edge function secrets, Cloudflare Worker,
.envlocal não mudam. - Auth providers config (Google, OTP) já configurada.
Passos:
# 1. Pré-check (valida public vazio, extensions, lista buckets/auth.users/cron)
bun run staging:pre-check
# 2. Rebuild reutilizando projeto (--fresh-project pula DROP, --reset-auth limpa auth.users)
PGURL_PROD=postgresql://... \
PGURL_STAGING=postgresql://... \
bun run staging:rebuild:reuse
# 3. Validação
bun run staging:post-check
bun run staging:driftVia GitHub Actions: dispare o workflow Staging Rebuild marcando fresh_project=true + reset_auth=true.
Secrets do workflow (PGURL_PROD, PGURL_STAGING):
- Cole apenas a connection string (Supabase → Settings → Database → Connection string), sem
PGURL_*=, sem aspas, sem quebra de linha. - Use a aba "Session pooler" (não "Direct connection") — o
db.<ref>.supabase.coé IPv6-only em projetos criados após jan/2024 e runners do GitHub Actions hosted (ubuntu-latest) não têm IPv6, então conexão direta falha comNetwork is unreachable. O Session pooler (Supavisor) é IPv4 e suportapg_dump/psqligual à conexão direta. - Formato esperado do Session pooler (porta 5432, NÃO 6543):Repare no usuário
postgresql://postgres.<project-ref>:<senha>@aws-<n>-<region>.pooler.supabase.com:5432/postgrespostgres.<ref>(com ponto). Porta 6543 = Transaction pooler — incompatível compg_dump, o script rejeita. - Direct connection (
postgresql://postgres:<senha>@db.<ref>.supabase.co:5432/postgres) só funciona localmente ou em runner com IPv6. - O step
Validate PGURL secretsfalha cedo se: formato inválido, refs iguais,PGURL_STAGINGapontando para o ref de produção, ouPGURL_PRODapontando para outro ref que não o de produção (anti-inversão). Imprime no summary o ref e o modo (directoupooler) detectado de cada secret. - O
pre-checktesta conectividade real (SELECT 1) antes de qualquer query — se falhar em mododirectcom "Network is unreachable", mostra dica explícita para trocar para Session pooler (não mais o falso positivo "public.escolas tem linhas").
O que ainda precisa de re-seed manual (não vem em pg_dump --schema=public):
| Item | Como recriar |
|---|---|
auth.users (Dev users) | psql -f docs/staging/seed_test_users.sql (chamado pelo rebuild) |
cron.job (pg_cron) | Rodar migrations de cron OU SQL manual conforme docs/operations/CRON_JOBS.md |
supabase_realtime publication tables | Migrations enable_realtime_* ou ALTER PUBLICATION supabase_realtime ADD TABLE ... |
| Storage objects (arquivos físicos) | Re-upload manual ou aceitar 404 em assets antigos |