Skip to content

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.md não cobre (que assume staging existente e só reseta o schema public).

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:

ItemOnde mora em prodComo recriar em staging novo
auth.usersschema auth (Supabase Auth)Não recriar — login real não funciona. Dev users via seed
Storage objects (arquivos)bucket S3 separadoNão recriar — paths antigos retornam 404 (aceitável em staging)
Storage buckets (metadata)storage.bucketsCriar manualmente no dashboard Storage
Cron jobsextensão pg_cron no schema cronReaplicar SQL de docs/operations/CRON_JOBS.md
Realtime publicationsupabase_realtime publicationReaplicar migration enable_realtime_* ou ALTER PUBLICATION
Edge function secretspainel Supabase Functions → SecretsCopiar de prod (ver supabase secrets list)
Edge functions (código)deploy via supabase functions deployLoop deploy após rebuild
Extensions (pg_cron, pgcrypto, etc.)habilitadas no painelHabilitar manualmente Database → Extensions

Passo a passo

0. Backup paranoia de prod (recomendado)

bash
pg_dump "$PGURL_PROD" -Fc --no-owner --no-acl \
  -f /mnt/documents/prod-backup-$(date +%Y%m%d-%H%M).dump

1. Delete projeto antigo + cria novo

No dashboard Supabase:

  1. Settings → General → Delete project (staging antigo)
  2. New project → escolher region/plan
  3. Aguardar provisionamento (~2 min)

2. Anote os novos valores

CampoOnde acharOnde usar
Project refURL do dashboard (/project/<ref>)PGURL_STAGING, secrets supabase functions deploy --project-ref
Connection string (postgres)Settings → Database → Connection string (URI)PGURL_STAGING
anon keySettings → API → Project API keys → anon.env, GitHub Secrets
service_role keySettings → API → Project API keys → service_rolesecrets Supabase functions
API URLSettings → 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 — checar SELECT extname FROM pg_extension em prod)

Em SQL Editor:

sql
SELECT extname FROM pg_extension ORDER BY extname;
-- comparar com prod, habilitar diferenças via dashboard

4. Atualiza secrets locais / GitHub

Local (.env):

bash
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_PROD permanece 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 staging
  • MP_ACCESS_TOKEN: usar token sandbox do MercadoPago, não o de prod
  • NTFY_TOPIC: usar tópico de staging separado

5. Rebuild — duas opções

Opção A — CI (recomendado):

  1. GitHub → Actions → Staging Rebuild → Run workflow
  2. Marcar fresh_project = true
  3. Digitar REBUILD no campo confirm
  4. Aguardar approval no environment staging-rebuild
  5. Ver summary no fim do job

Opção B — Local:

bash
bash scripts/staging/rebuild-from-prod.sh --fresh-project

6. Pós-rebuild — recriar infra que não veio no dump

bash
PGURL_STAGING="..." bash scripts/staging/post-rebuild-checklist.sh

O 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:

sql
-- 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

bash
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"
done

8. Deploy worker (Cloudflare)

bash
cd workers/olp-msgqueue && wrangler deploy --env staging

Atualizar variáveis do worker (Cloudflare dashboard) com novas URLs/keys de staging.

9. Validação final

bash
bun run staging:drift            # BLOCKERS deve ser 0
bun run scripts/staging/post-rebuild-checklist.sh   # Status: OK
bunx playwright test --grep @smoke

Riscos conhecidos e aceitos

  • Login real não funciona em stagingauth.users está 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, .env local não mudam.
  • Auth providers config (Google, OTP) já configurada.

Passos:

bash
# 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:drift

Via 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 com Network is unreachable. O Session pooler (Supavisor) é IPv4 e suporta pg_dump/psql igual à conexão direta.
  • Formato esperado do Session pooler (porta 5432, NÃO 6543):
    postgresql://postgres.<project-ref>:<senha>@aws-<n>-<region>.pooler.supabase.com:5432/postgres
    Repare no usuário postgres.<ref> (com ponto). Porta 6543 = Transaction pooler — incompatível com pg_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 secrets falha cedo se: formato inválido, refs iguais, PGURL_STAGING apontando para o ref de produção, ou PGURL_PROD apontando para outro ref que não o de produção (anti-inversão). Imprime no summary o ref e o modo (direct ou pooler) detectado de cada secret.
  • O pre-check testa conectividade real (SELECT 1) antes de qualquer query — se falhar em modo direct com "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):

ItemComo 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 tablesMigrations enable_realtime_* ou ALTER PUBLICATION supabase_realtime ADD TABLE ...
Storage objects (arquivos físicos)Re-upload manual ou aceitar 404 em assets antigos