Version the deploy skill with the repo

.claude/ was ignored wholesale, which meant the deploy skill lived only
on one machine. Narrow the rule so .claude/skills/ is tracked while
settings.local.json stays local.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
JP
2026-08-08 17:02:51 +00:00
parent 45d9bd12d7
commit 2beb2d5d43
2 changed files with 72 additions and 1 deletions

View File

@@ -0,0 +1,69 @@
---
name: deploy-drinktracker
description: Deploy drinktracker to the production LXC and verify it came back healthy. Use when asked to deploy, ship, release, push to prod, or update the running app - and after merging changes that need to go live. Also covers rollback and diagnosing a deploy that did not take effect.
---
# Deploying drinktracker
Run `./deploy/deploy.sh` from the repo root. It typechecks, pushes to Gitea, rebuilds the
image on the LXC, restarts the stack and polls until the app answers. Use `--yes` to skip
the confirmation prompt when the user has already approved the deploy.
`deploy/README.md` has the full topology table, the one-time sudoers setup, and rollback
commands. Read it before improvising — the layout is unusual in ways that waste time.
## Facts that are not discoverable from the repo
- **The LXC is `192.168.2.169`.** `drinktracker.tenseconddelay.net` (and `NEXTAUTH_URL`)
resolve to the reverse proxy at `192.168.2.172`. SSHing there fails with what looks like
a rejected key but is simply the wrong machine.
- **The live stack is `/root/drinktracker`.** The checkouts under `/home/drinkadmin/`
(`drinktracker`, `drinktracker-latest`) are stale June copies. Confirm with
`docker inspect drinktracker-app-1 --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'`.
- **There is no CI.** No Gitea Actions, no GitHub workflows. `git push` updates the repo
and nothing else; the image must be built and the stack restarted separately. If someone
reports "I pushed but nothing changed", this is why.
- **Access:** `ssh -i ~/.ssh/drinktracker_ed25519 drinkadmin@192.168.2.169`. Root SSH is
disabled. `drinkadmin` needs the sudoers drop-in from `deploy/README.md` for the script
to work unattended.
## Verifying
An unauthenticated request to port 3000 returns **307** (redirect to `/login`) — that is a
healthy response, not a failure. The script accepts 200, 302 and 307.
Each AI call emits one line:
```
sudo docker logs drinktracker-app-1 2>&1 | grep '\[switchboard\]'
```
`FAILOVER` lines are expected and harmless: the gateway's `:batch` model variants fail on
every request and fall back. `CONTEXT OVERFLOW` is worth investigating — it means a request
was too large for any model's window and may have been truncated.
## Before deploying schema changes
The `migrate` service runs `prisma db push --skip-generate --accept-data-loss` on every
start. Removing a field from `schema.prisma` therefore **drops the column and its data** on
the next deploy, in both directions — so a rollback past a schema change is destructive too.
Dump the database first when the schema changed:
```
sudo docker exec drinktracker-db-1 pg_dump -U drinktracker drinktracker > ~/dt-$(date +%F).sql
```
This is also why `UserPreference.defaultProvider` is still in the schema despite being
unused: deleting it would drop the column and break restoring older backups.
## If the app does not come back
```
sudo docker compose -f /root/drinktracker/docker-compose.prod.yml ps
sudo docker logs drinktracker-app-1 --tail 100
```
Most likely causes, in order: the build failed and compose restarted the previous image;
`.env.production` is missing a variable the new code reads (compare against `.env.example`);
or the `migrate` service exited non-zero and `app` never started, which `ps` will show as
`migrate` in a non-zero exited state.

4
.gitignore vendored
View File

@@ -32,7 +32,9 @@ yarn-error.log*
.env*.local
# claude
.claude/
.claude/*
# Skills are project documentation and worth versioning; local settings are not.
!.claude/skills/
# vercel
.vercel