Skip to content

feat(cli): provision the site on the control-plane during migration (D7) - #551

Open
JonasJesus42 wants to merge 1 commit into
mainfrom
feat/migrate-provision-control-plane
Open

JonasJesus42 wants to merge 1 commit into
mainfrom
feat/migrate-provision-control-plane

Conversation

@JonasJesus42

@JonasJesus42 JonasJesus42 commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Problema

Um site migrado saía não-deployável. A pipeline era wiring manual, um clique no dashboard da Cloudflare por worker (D6.3), e nada dava ao site o namespace de KV que o fast-deploy precisa.

D7 — deploy passa para o control-plane

Registro D7 (neste PR, em MIGRATION_TOOLING_PLAN.md + a regra always-loaded) — é o "follow-up D-record once shipped" que o D6.3 prometeu. O Cloudflare Workers Builds sai; entra decocms/control-plane (SITE_CREATE → repo, admin sites, org ownership, KV/R2/D1 próprios do site, env de produção, Deco CR) + operator + cfworkers-builder.

Três coisas que o CF Builds estruturalmente não dava:

  1. Isolamento de KV por site. index:live é uma chave por namespace — dois sites dividindo um se atropelam. ensureSiteKvNamespace cria deco-kv-<site> e o builder re-força o id do DECO_KV a partir de CF_KV_NAMESPACE_ID a cada build, então repo de tenant não consegue bindar KV de outro site.
  2. Seed antes de ativar. O builder semeia decofile:<sha> antes do wrangler deploy e vira index:live depois — código novo nunca lê conteúdo velho. Essa ordem é exatamente o que torna seguro stubar o blocks.gen do bundle servidor (feat(tanstack): stub blocks.gen by default when the pipeline seeds KV #549), o corte de memória. Site no CF Builds nunca consegue esse ganho.
  3. Zero clique por worker, e o builder tira routes/account_id controlados pelo tenant.

Inalterado do D6.3: storefront continua sem deploy.yml/preview.yml/sync-secrets.yml scaffoldados — a pipeline é do control-plane, não do repo do site.

Mudanças

  • provisionSite no migrate.ts, logo após o bootstrap: tools/call SITE_CREATE no /mcp, grava o kvNamespaceId retornado no wrangler.jsonc. Inerte sem DECO_CONTROL_PLANE_TOKEN + DECO_ORG — imprime a chamada equivalente e segue, mesmo padrão do provisionAnalytics. Migração não pode falhar porque alguém rodou sem token, e o id nem é load-bearing (o builder re-força a cada build).
  • setKvNamespaceIdInJsonc em lib/wrangler-config.ts, ao lado do leitor. Edita como texto em vez de parse-and-restringify, porque o wrangler.jsonc scaffoldado carrega os comentários explicando por que cada binding existe.
  • Bonus fix: provisionAnalytics interpolava package.json.name direto numa string SQL enviada à Supabase Management API. Essa API não aceita bind parameters, então a correção honesta é recusar o que não for um slug de site.
  • --help documenta as env vars novas.

Testes

5 casos para setKvNamespaceIdInJsonc: escreve o id preservando comentários, não encosta em outro binding, aceita binding/id em qualquer ordem e entrada sem id, e devolve a fonte intacta quando o binding não existe (não é erro — o builder re-força mesmo assim).

Pilha: #548#549 (+ infra#333) → #550 → este.

🤖 Generated with Claude Code


Summary by cubic

Migrated sites previously required manual Cloudflare wiring and lacked the KV namespace required for fast deploys. Migration now calls the control plane’s SITE_CREATE tool to provision deployment resources and writes the site’s KV namespace ID to wrangler.jsonc; storefront repos still receive no deploy workflow scaffolding.

  • The provisioning step is enabled by DECO_CONTROL_PLANE_TOKEN and DECO_ORG; without them, migration prints an equivalent curl command and continues.
  • The config update preserves JSONC comments and leaves unrelated KV bindings unchanged.
  • Analytics provisioning now rejects site names that are not valid slugs before interpolating them into SQL.
  • Adds coverage for KV config updates, including reversed fields, missing IDs, and absent bindings.

Written for commit d0a651c. Summary will update on new commits.

Review in cubic

A migrated site came out undeployable: the pipeline had to be wired by
hand, one Cloudflare dashboard click per worker (D6.3), and nothing gave
the site the KV namespace fast-deploy needs.

D7 (this commit, superseding D6.3) moves deploy to our own pipeline, and
the migration now calls its front door. `provisionSite` posts SITE_CREATE
to the control-plane's MCP endpoint and writes the KV namespace id it
returns into the scaffolded wrangler.jsonc. SITE_CREATE is idempotent and
create-if-absent at every step: admin `sites` row, org ownership, the
site's OWN `deco-kv-<site>` namespace, the production environment, and the
Deco CR that makes it deploy.

The site's own namespace is the point, not a detail: `index:live` is one
key per namespace, so two sites sharing one clobber each other's live
pointer. And our builder seeds `decofile:<sha>` before activating the new
version, which is the guarantee that makes stubbing `blocks.gen` out of
the server bundle safe — a site on CF Workers Builds can never take that
memory win.

Inert without `DECO_CONTROL_PLANE_TOKEN` + `DECO_ORG`: prints the
equivalent call and continues, like `provisionAnalytics`. A migration must
not fail because an operator ran it without a token, and the id is not
load-bearing anyway — the builder re-forces it from `CF_KV_NAMESPACE_ID`
on every build.

`setKvNamespaceIdInJsonc` edits the config as text rather than
parse-and-restringify, because the scaffolded wrangler.jsonc carries the
comments explaining why each binding exists.

Also hardens `provisionAnalytics`, which interpolated `package.json`'s
name straight into SQL sent to the Supabase Management API. That API takes
no bind parameters, so the honest fix is to refuse anything that is not a
plain site slug.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant