Ir al contenido

Despliegue

Al terminar esta página tendrás el sitio corriendo en Cloudflare contra la D1 y el R2 remotos, con el /admin accesible y el correo saliendo de verdad, y cada push desplegando solo.

cms deploy crea el repositorio en GitHub por ti, así que tu máquina necesita tres cosas. Las comprueba él antes de tocar nada, pero llegar preparado ahorra un viaje:

  • gh y git instalados. brew install gh (macOS), winget install --id GitHub.cli (Windows), o el resto en cli/cli.
  • Sesión de GitHub iniciada: gh auth login, y gh auth status para confirmarlo.
  • Lanzarlo sin GITHUB_TOKEN en el entorno. La instalación te hizo exportar esa variable para que npm bajara el paquete del registro privado, y gh la prefiere sobre su propio llavero: mientras esté puesta, gh no se autentica aunque tu sesión esté perfecta. De ahí el env -u GITHUB_TOKEN de abajo — o un GH_TOKEN con permiso repo, si prefieres. La salida larga está en Solución de problemas.
Ventana de terminal
env -u GITHUB_TOKEN pnpm cms deploy

En este orden, y el orden es media página:

  1. El diagnóstico de cms doctor como puerta. Los avisos no paran nada; un solo check en rojo sí, porque desplegar con él solo llevaría el problema a producción.

  2. Comprueba que ningún binding se haya quedado sin id —ausente, vacío o el marcador de posición—. Si alguno lo está, manda a cms resources:create y no toca nada.

  3. Comprueba el .npmrc, antes de tocar git. Es lectura de fichero pura: si falta el registro o si lleva un token escrito dentro, aborta sin haber lanzado ni un proceso.

  4. Comprueba que gh y git estén y que haya sesión de GitHub iniciada. También aquí, antes de migrar nada.

  5. Aplica las migraciones remotas (cms db:apply --remote), desde tu máquina.

  6. Crea el repositorio en GitHub con gh, commitea lo que haya suelto y empuja. Justo después del git init, y solo ahí, comprueba que el .npmrc no esté ignorado por git: es lo único que git no puede contestar antes de que el repositorio exista.

  7. Abre el dashboard y escribe en la terminal los cuatro clics que quedan.

Es lo que el comando imprime al terminar, y hay que hacerlo una sola vez por proyecto:

  1. Abre el dashboard y pulsa «Create» → «Import a repository». Elige el repositorio que se acaba de crear.

  2. Build command: pnpm buildDeploy command: npx wrangler deploy.

  3. En «Build variables and secrets» añade GITHUB_TOKEN como secret de build, con permiso read:packages: sin él el build falla con un 401 al instalar @kevolution-co/cms.

  4. El Worker en Cloudflare tiene que llamarse exactamente como el name de tu wrangler.jsonc. Con otro nombre, Cloudflare crea un Worker distinto y los bindings de ese fichero no apuntan a nada.

Conectar un repositorio con Workers Builds es una GitHub App con OAuth: no hay API ni CLI que lo haga, así que no se puede automatizar y no tiene sentido disimularlo. Está pedido en cloudflare/workers-sdk#12058. Lo que sí se puede hacer por ti —el repositorio, el push, las migraciones, el diagnóstico— lo hace cms deploy.

Este es el fichero que entra en el repositorio, al revés que casi todo lo que se le parece. El builder de Cloudflare instala @kevolution-co/cms desde un registro privado, en una máquina que no es la tuya, y sin ese fichero no sabe a dónde ir:

.npmrc
@kevolution-co:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

${GITHUB_TOKEN} es texto literal: lo expande npm al leer el fichero, y el valor sale del secret de build del paso 3. El token nunca se escribe ahí.

Y el fallo simétrico: si tu .gitignore ignora el .npmrc, nunca llegará al repositorio y el build caerá con un 401. cms doctor lo avisa con el check npmrc-ignored —en amarillo, así que no bloquea nada por su cuenta— y cms deploy para.

Pero para más tarde que los otros dos casos, y conviene saberlo: git no sabe contestar check-ignore hasta que el repositorio existe, así que esa comprobación vive después del git init, con las migraciones remotas ya aplicadas. Si te sale este error, tu D1 de producción ya está migrada y tienes un .git/ recién creado en el directorio; quita la línea .npmrc del .gitignore y vuelve a ejecutar cms deploy, que a partir de ahí es idempotente.

.dev.vars es solo local: wrangler no lo sube en el deploy y el fichero está en tu .gitignore. En producción, BETTER_AUTH_SECRET se guarda como secreto del Worker:

Ventana de terminal
pnpm cms secret:set --remote

Genera uno nuevo con randomBytes(32) y se lo entrega a wrangler por la entrada estándar, así que no queda en la tabla de procesos ni en el historial del shell, y el valor no se imprime. Lo cifra la cuenta de Cloudflare; no queda en ningún fichero del repositorio.

Rotarlo invalida todas las sesiones abiertas —hay que volver a entrar en /admin—, que es exactamente lo que quieres si sospechas que se filtró.

Sin ese secreto el Worker despliega, pero la autenticación no funciona: /admin responde 500. Los detalles, en Autenticación.

cms db:apply --remote desde tu máquina, antes de empujar — que es justo lo que cms deploy hace por ti— y nunca en el build command de Cloudflare.

Con Workers Builds el despliegue lo dispara el push, así que no hay un «antes del deploy» que ejecutar desde el builder: lo que hay es un antes del push. Y meterlo en el build command por comodidad tiene tres problemas, cada uno suficiente:

  • No está verificado que el token que Cloudflare inyecta en el builder pueda editar D1.
  • Un fallo a mitad de build deja el Worker desplegado contra un esquema que no existe, y cada lectura de esa colección responde 500 hasta que alguien se da cuenta.
  • El build es exactamente el sitio donde una confirmación sin TTY se acepta sin que nadie mire: db:apply --remote no pregunta cuando no hay terminal. Ver CLI.

La consecuencia operativa es la que se olvida: cada cambio de esquema es un cms db:apply --remote antes del push siguiente, o un cms deploy otra vez.

  1. D1 y R2 son otros. La base y el bucket con los que trabaja wrangler dev son copias locales que viven en .wrangler/, no los recursos que cms init --cloud —o cms resources:create— creó en tu cuenta. El contenido que escribiste probando no viaja: la instalación remota arranca vacía y /admin te enseña otra vez el asistente de configuración, con su primera cuenta y sus ajustes del sitio.

    Por eso db:apply tiene dos modos: sin flag toca la copia local, con --remote la de verdad.

  2. El correo sale de verdad. En local, wrangler dev simula el envío y vuelca el mensaje a un fichero temporal cuya ruta imprime; no hace falta dominio ni plan de pago. Desplegado no hay simulación que valga, así que los tres requisitos de plataforma tienen que estar cumplidos —plan Workers de pago, dominio con DNS de Cloudflare dado de alta en Email Sending, y un from de ese dominio— o el envío falla.

    El flag "remote": true del binding send_email no es para producción: es lo que hace que tu máquina deje de simular y mande correo real desde wrangler dev, con esos mismos tres requisitos ya cumplidos. Sirve para ver cómo queda el mensaje en clientes reales; déjalo fuera mientras desarrolles.

  3. La URL pública cambia. El ajuste siteUrl que guardaste desde local apunta a localhost:4321, y es de ahí de donde salen los enlaces de verificación y restablecimiento de los correos. En la instalación remota lo pone el asistente; si lo cambias después, se edita desde /admin/settings sin desplegar. Si te deja fuera del panel, cms settings:reset-site-url --remote lo borra.

Los fallos de un primer despliegue —el 401 del builder, el Worker que no es el tuyo, /admin respondiendo 500, nodejs_compat que falta, migraciones que wrangler no encuentra— están recogidos por síntoma en Solución de problemas.