Files
Oier Bravo Urtasun ae4fd45b99 deploy(prod): pre-deploy DB backup + paired code/DB rollback skill
Every `just deploy` now takes a pg_dumpall to
/opt/colectivo/backups/predeploy-<ts>-<sha>.sql.gz BEFORE rebuilding the
app or applying migrations, aborts the deploy if the dump is < 1 KiB,
and (on success) appends `<iso-ts> \t <sha> \t <backup-file>` to
/opt/colectivo/.deploys.log on ambrosio. Keeps the newest 10 backups
(prunes older predeploy-* files).

New `infra/scripts/rollback-erosi.sh` reads .deploys.log and pairs a
code rollback with a DB restore atomically:

  just rollback-list          # show recent deploys
  just rollback               # roll back to N-1
  just rollback-to <sha>      # roll back to a specific deploy
  just rollback-code          # roll back code only, keep current DB

Rollback safety:
- 5-second abort window.
- Verifies the target SHA exists locally + the backup is still on prod
  (warns if it's been pruned past the 10-deploy window).
- Uses a temporary git worktree so the user's working tree isn't
  disturbed.
- Stops app/auth/rest/realtime/storage before the gunzip|psql restore.
- Rebuilds the app image at the rolled-back SHA with the same GIT_SHA
  build-arg path the deploy uses (so __APP_COMMIT__ in the bundle
  matches the running code).
- Does NOT write a new .deploys.log entry — rollback is intentionally
  not a deploy event; the next `just deploy` is from current HEAD.
- Storage volume (/var/lib/storage user uploads) is NOT rolled back.

Justfile `deploy` recipe repointed from the stale `deploy.sh` (which
referenced GHCR pull) to `deploy-erosi.sh` (the active prod path).

New project-scoped skill: `.claude/skills/deploy/SKILL.md` documents the
flow + safety boundaries for Claude-assisted invocations. `.gitignore`
keeps `.claude/settings.local.json` ignored but tracks `.claude/skills/`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 14:57:25 +02:00

4.3 KiB

name, description
name description
deploy Ship the colectivo stack to ambrosio (https://erosi.limonia.net) with pre-deploy DB backup, or roll back code + DB to a previous deploy. Trigger when the user asks to deploy, ship, push to prod, roll back, or undo a deploy.

Deploy + rollback skill

Deploys and rollbacks for the production stack on ambrosio. Every deploy takes a paired DB backup first, every successful deploy is logged with (timestamp, git-sha, backup-filename), and rollback uses that log to restore both code and DB atomically.

Trigger

Use this skill when the user says any of:

  • "deploy", "deploy to ambrosio", "deploy to prod", "ship it"
  • "roll back", "rollback", "undo the deploy", "revert prod"
  • "list deploys", "what was the last deploy"

What the skill does

Forward (deploy)

  1. just deploy runs infra/scripts/deploy-erosi.sh:
    • Captures the local short SHA (git rev-parse --short HEAD).
    • rsyncs the working tree to ambrosio:/opt/colectivo/ (excludes .git, node_modules, **/.env, etc.).
    • On a fresh .env, generates secrets and bails — operator fills PUBLIC_KEYCLOAK_URL + KEYCLOAK_CLIENT_SECRET and re-runs.
    • Pre-deploy DB backup: pg_dumpall from the prod db container → backups/predeploy-<ts>-<sha>.sql.gz on ambrosio. Aborts the deploy if the dump is < 1 KiB. Keeps the newest 10 backups.
    • Rebuilds the app image with GIT_SHA build-arg (so __APP_COMMIT__ lands in the bundle, Fase 14.3).
    • docker compose up -d brings the stack to the new image.
    • Applies any pending DB migrations.
    • On success, appends <iso-ts>\t<sha>\t<backup-file> to /opt/colectivo/.deploys.log.
  2. Site smoke: curl -I https://erosi.limonia.net/ should return 200.

Reverse (rollback)

  1. just rollback-list prints recent rows from .deploys.log so the user can pick a target.
  2. just rollback rolls back code + DB to the previous deploy (last but one line of .deploys.log).
  3. just rollback-to <sha> rolls back to a specific deploy.
  4. just rollback-code rolls back code only, leaves the DB at current state — useful when the regression is UI-only and the DB schema is compatible.

infra/scripts/rollback-erosi.sh enforces:

  • Confirms with a 5-second abort window before doing anything destructive.
  • Verifies the target SHA is reachable locally (git cat-file -e) and that the backup file still exists on ambrosio.
  • Materialises the target commit into a temporary git worktree, rsyncs from there, never disturbs the user's working tree.
  • Stops app / auth / rest / realtime / storage before the DB restore so they don't observe inconsistent state.
  • gunzip | psql -v ON_ERROR_STOP=1 restores the dump.
  • Rebuilds the app image at the rolled-back SHA and brings the stack up.
  • Does not write a new .deploys.log line — rollback is intentionally not a deploy event.

Safety boundaries

  • The stack lives on ambrosio. The external proxy + TLS terminator are off-stack (docs/deployment.md). Neither this skill nor the scripts ever touch host TLS or the external proxy.
  • infra/scripts/deploy-erosi.sh never runs sudo.
  • --no-verify / --amend are never used. Pre-commit hooks must pass.
  • Always confirm with the user before invoking just rollback* — DB restore is destructive and irreversible.
  • Storage volume (/var/lib/storage, user uploads) is not part of the rollback. Treated as append-only.

What to tell the user

Before running anything:

  • Show the local short SHA + branch + remote.
  • For deploy: confirm with the user. Mention "this will take a fresh DB backup before deploying."
  • For rollback: show just rollback-list output, ask which SHA, confirm destructive nature.

After running:

  • Print the new .deploys.log tail (or the rollback target).
  • Curl-probe the site and report.

Files this skill touches

  • infra/scripts/deploy-erosi.sh — deploy, with pre-deploy backup + .deploys.log writing.
  • infra/scripts/rollback-erosi.sh — rollback, paired code + DB.
  • Justfiledeploy, rollback, rollback-list, rollback-to, rollback-code recipes.
  • docs/deployment.md — runbook for both flows.

Anti-trigger

If the user is just asking what would happen on deploy (status, dry-run-style questions), DO NOT run anything. Answer from the script content.