Reescribe el script de despliegue con lo que funciona de verdad en el servidor

Lo escribí suponiendo, lo probé a mano contra la máquina y las suposiciones
fallaron en dos sitios:

- `rsync` no está en la máquina de desarrollo. Git Bash no lo trae. Se sube con
  `tar` por SSH, que sí está en los dos lados.
- `npm ci` no funciona en el servidor. Tiene salida a internet, pero su DNS
  resuelve unos dominios y otros no: `google.com` sí, `registry.npmjs.org` no.
  Las dependencias se resuelven aquí y se suben instaladas. Los tres paquetes de
  producción son JavaScript puro, así que viajan tal cual; el script comprueba
  que sigan siéndolo y se para si aparece un binario de plataforma.

Comprobado en el servidor y no solo en el papel: la web arranca, las siete rutas
que probé devuelven 200, la muestra de audio sale entera y la ficha lee las
valoraciones de Postgres por el bucle local.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
master
dev 1 month ago
parent c87cbcd3ab
commit 2213368d5b

@ -994,6 +994,13 @@ hace que abrir la web no despierte al proceso de Node para devolver un CSS. En
Lo que hace falta en el servidor: **Node 22**, Nginx y PostgreSQL. Nada más; ni
Vite ni el compilador, porque el build se hace en la máquina de desarrollo.
El script sube con `tar` por SSH y no con `rsync` —la máquina de desarrollo es
Windows y Git Bash no lo trae—, y **sube las dependencias ya instaladas** en vez
de resolverlas allí: el servidor tiene salida a internet pero su DNS no resuelve
`registry.npmjs.org`, así que `npm ci` falla. Los tres paquetes de producción
son JavaScript puro y viajan tal cual; si algún día entra uno con binarios de
plataforma, el script se para y avisa en vez de dejar el servicio caído.
Cuatro cosas que muerden si se olvidan:
1. **`ORIGIN` con el dominio público.** Sin él, SvelteKit rechaza todos los

@ -8,12 +8,14 @@
# porque el servidor no necesita las herramientas de desarrollo —ni Vite, ni
# Playwright, ni el compilador— para servir la web: solo Node y tres paquetes.
#
# Lo que se sube:
# Dos decisiones que salen de haberlo hecho una vez a mano, no de suponer:
#
# build/ el servidor ya compilado
# package.json para instalar solo las dependencias de ejecución
# media/audio/ los másteres, que no van en git ni los genera el build
# drizzle/ las migraciones, para poder repasarlas allí
# - **Se sube con `tar` por SSH, no con `rsync`.** La máquina de desarrollo es
# Windows y Git Bash no trae rsync. Tar sí, y está en los dos lados.
# - **Las dependencias se resuelven aquí y se suben ya instaladas.** El servidor
# tiene salida a internet pero su DNS no resuelve `registry.npmjs.org`, así
# que `npm ci` allí falla. Da igual: los tres paquetes de producción son
# JavaScript puro, sin binarios de plataforma, y viajan tal cual.
#
# Lo que NO se sube: `.env`. Las claves se ponen a mano en el servidor una vez
# y se quedan ahí. Subirlas en cada despliegue es cómo acaban en un historial.
@ -22,12 +24,17 @@ set -euo pipefail
DESTINO="${1:-}"
RUTA="${2:-/var/www/senzapaura}"
CLAVE="${SSH_CLAVE:-$HOME/.ssh/senzapaura_deploy}"
if [[ -z "$DESTINO" ]]; then
echo "uso: $0 usuario@servidor [ruta]" >&2
exit 1
fi
SSH=(ssh -i "$CLAVE" -o BatchMode=yes)
TEMPORAL="$(mktemp -d)"
trap 'rm -rf "$TEMPORAL"' EXIT
echo "==> Construyendo"
node scripts/construir.mjs
@ -36,38 +43,45 @@ if [[ ! -f build/index.js ]]; then
exit 1
fi
echo "==> Resolviendo dependencias de producción"
cp package.json package-lock.json "$TEMPORAL/"
(cd "$TEMPORAL" && npm ci --omit=dev --silent)
# Un binario compilado para Windows no arrancaría en el servidor. Hoy no hay
# ninguno, pero si algún día se añade una dependencia que lo traiga, es mejor
# enterarse aquí que con el servicio caído.
if find "$TEMPORAL/node_modules" -name '*.node' | grep -q .; then
echo "hay binarios de plataforma en node_modules: instálalos en el servidor" >&2
find "$TEMPORAL/node_modules" -name '*.node' | head -5 >&2
exit 1
fi
echo "==> Subiendo la aplicación"
# `--delete` en build/ para que no se queden restos de versiones anteriores:
# los nombres llevan hash y se acumularían sin fin.
rsync -az --delete build/ "$DESTINO:$RUTA/build/"
rsync -az package.json package-lock.json drizzle.config.ts "$DESTINO:$RUTA/"
rsync -az --delete drizzle/ "$DESTINO:$RUTA/drizzle/"
# `build` se borra antes: los nombres llevan hash y si no se acumularían sin fin.
tar -czf - build package.json package-lock.json drizzle.config.ts drizzle |
"${SSH[@]}" "$DESTINO" "rm -rf '$RUTA/build' '$RUTA/drizzle' && tar -xzf - -C '$RUTA'"
echo "==> Subiendo dependencias"
tar -czf - -C "$TEMPORAL" node_modules |
"${SSH[@]}" "$DESTINO" "rm -rf '$RUTA/node_modules' && tar -xzf - -C '$RUTA'"
echo "==> Subiendo el audio privado"
# Sin `--delete`: aquí puede haber material que no esté en esta máquina, y
# borrarlo por descuido sería irreversible.
rsync -az media/audio/ "$DESTINO:$RUTA/media/audio/"
# Sin borrar lo que hubiera: aquí puede haber material que no esté en esta
# máquina, y perderlo sería irreversible.
tar -czf - media/audio | "${SSH[@]}" "$DESTINO" "tar -xzf - -C '$RUTA'"
echo "==> Migrando la base de datos"
# Desde aquí y no en el servidor: `drizzle-kit` es una herramienta de
# desarrollo y en producción se instala con `--omit=dev`, así que allí no
# existe. Como la base es la misma, da igual desde dónde se lance.
# Desde aquí y no en el servidor: `drizzle-kit` es una herramienta de desarrollo
# y allí no se instala. Como la base es la misma, da igual desde dónde se lance.
npm run db:migrate
echo "==> Instalando dependencias"
ssh "$DESTINO" bash -euo pipefail <<REMOTO
cd "$RUTA"
# Solo lo que hace falta para servir: pg, drizzle-orm y stripe.
npm ci --omit=dev
REMOTO
echo "==> Reiniciando"
# Requiere que el usuario pueda reiniciar este servicio sin contraseña. En
# /etc/sudoers.d/senzapaura:
# senzapaura ALL=(root) NOPASSWD: /bin/systemctl restart senzapaura
ssh "$DESTINO" "sudo systemctl restart senzapaura && sleep 2 && systemctl is-active senzapaura"
"${SSH[@]}" "$DESTINO" "sudo systemctl restart senzapaura && sleep 3 && systemctl is-active senzapaura"
echo "==> Comprobando que responde"
ssh "$DESTINO" "curl -sf -o /dev/null -w 'portada -> %{http_code}\n' http://127.0.0.1:3000/"
"${SSH[@]}" "$DESTINO" "curl -sf -o /dev/null -w 'portada -> %{http_code}\n' http://127.0.0.1:3000/"
echo "Listo."

Loading…
Cancel
Save

Powered by TurnKey Linux.