Add the playbook that deploys one Site

The playbook pulls the tag from the registry, creates the data root owned by the
app user, runs the container with its config baked into the image, waits for
health, then verifies the data directory as the app user so a permissions
mistake surfaces at deploy time. Caddy is pointed at the app with a stable
marker, so a temporary hostname can be checked beside the live site and the
cutover is one invocation with the retire flags set.
This commit is contained in:
tanshu committed 2026-09-22 21:25:52 +05:30
1 parent bb9ddb437f
commit 1f06e32854
11 files changed
+359 -9

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
# menu-admin playbook
Deploys the menu app for one Site, beside the existing nginx container at first
and in place of it after cutover. Copy this directory to
`ansible-playbooks/menu-admin/` and run it from there, so `playbook_dir` and the
inventory match the other playbooks.
This playbook owns the `menu-app` container and the Site's snippet in the shared
Caddyfile. It does not touch the app's source, the image, or the other Sites'
containers.
## What lives where
| Thing | Where |
| --- | --- |
| Sections, brand, theme | `config/<site>.site.json` in the app repo, baked into the image |
| Menu content, Drafts, Originals | `/var/lib/menu-app.<site>/data` on the host |
| Admin password hash, session secret | `/var/lib/menu-app.<site>/site.env` (untracked, mode 600) |
| Which image tag runs | `-e tag=<tag>` |
Sections are deliberately not editable on the server: changing them is a commit
to the app repo, a new image, and a redeploy (ADR 0005).
## Deploying
```sh
# 1. Build and push from the app repo
./deploy.sh 2026-09-22 # -> registry.tanshu.com/menu-admin:2026-09-22
# 2. First run: a temporary hostname, so the live site keeps serving
ansible-playbook -l monoco -e site=hopsngrains -e tag=2026-09-22 playbook.yml
# 3. The admin needs a password before it can be used
ssh monoco scripts/provision-site-env.sh /var/lib/menu-app.hopsngrains.com
```
The first run creates `/var/lib/menu-app.hopsngrains.com`, starts the container
on the `menu-app.hopsngrains.com_net` network, adds a snippet for
`menu-new.hopsngrains.com` to the shared Caddyfile, and reloads Caddy. The
existing `menu.hopsngrains.com` vhost still points at the old nginx container.
`site.env` is deliberately not created by Ansible: the password is typed by a
human on the server. Until it exists the admin is disabled and the playbook says
so — the public menu still serves.
## Checking the parallel run
From a phone on restaurant wifi, not from the server:
- `https://menu-new.hopsngrains.com/` lists the Sections that have a current menu.
- A Section page scrolls, the pour line tracks, tapping a page opens it full width.
- The admin signs in, uploads a real photo, reorders, previews, publishes.
- After publishing, the phone shows the new menu and the old set appears under
"Older menus".
## Cutover
```sh
ansible-playbook -l monoco -e site=hopsngrains -e tag=2026-09-22 \
-e vhost=menu.hopsngrains.com \
-e retire_nginx_snippet=true \
-e retire_nginx_container=true \
playbook.yml
```
This repoints the real vhost at the app, removes the snippet the old playbook
wrote for that hostname, and stops the old container. The old container and
`/var/lib/menu.hopsngrains.com` are kept, not deleted: deleting 319 MB of the
only copy of the old menus is a human decision, made a week later, once the new
site has been trusted.
## Rolling back
```sh
docker start nginx-menu.hopsngrains.com
ansible-playbook -l monoco -e site=hopsngrains -e tag=<previous> \
-e vhost=menu.hopsngrains.com -e retire_nginx_snippet=false playbook.yml
```
Then delete the app's snippet from `/var/lib/caddy/conf/Caddyfile` by hand, or
set `vhost` back to the temporary name, and reload Caddy.