Deploy in one run, with nothing older to preserve

There is no previous system kept serving, so the playbook no longer knows about
one: the retire flags, the temporary web address, the task that removed the old
nginx snippet and the task that stopped the old container are gone. One run
starts the container and points the Site's web address at it, and deploying an
earlier tag is the rollback. The vars lose vhost, retire_nginx_snippet,
retire_nginx_container and legacy_container; the Caddy role keeps only the
snippet upsert.

The deployment readme drops its staged-rollout sections and the glossary of words
that no longer exist, and the plan's sequence and the operating notes describe
the single run instead.
This commit is contained in:
tanshu committed 2026-09-22 23:27:17 +05:30
1 parent 041b8b62fe
commit f2d8058456
8 files changed
+152 -131

No files matched your search

+27 -73
View File
@@ -1,44 +1,20 @@
# The menu deployment
Lives here, beside the app it deploys, the way the other apps on this machine do.
It owns the `menu-app` container and the Site's snippet in the shared Caddyfile.
## Words used below
The deployment reads as jargon if you have not done one before, so:
- **web address** — one hostname the web server answers for. Caddy keeps a block
per address; `menu.hopsngrains.com` is one. Older writing calls this a
_vhost_, which just means "virtual host", and `vhost` is the variable name in
`vars/`.
- **parallel run** — putting the app on a second, temporary address
(`menu-new.hopsngrains.com`) while the old site keeps the real one, so the new
one can be checked on a phone without customers seeing it. It needs a DNS
record for the temporary name, because Caddy has to fetch a certificate for it.
- **cutover** — the moment the real address is pointed at the new app instead of
the old nginx container, and that container is stopped. Customers are on the
new app from then on. It is reversible.
| Stage | Address customers use | Address you check |
| ------------- | ---------------------------------- | ------------------------------------- |
| today | `menu.hopsngrains.com` → old nginx | — |
| parallel run | unchanged | `menu-new.hopsngrains.com` → this app |
| after cutover | `menu.hopsngrains.com` → this app | — |
Skipping the parallel run is a legitimate choice: cutover on its own is one
command, one Caddy reload, and a rollback if anything looks wrong. The parallel
run is for finding out that a container will not start, or a font will not load,
without anyone watching.
Lives here, beside the app it deploys. One run deploys a Site: it starts the
app's container and points the Site's web address at it. There is no older
system to keep serving, so there is no staged rollout and nothing to retire —
deploying an earlier tag is the rollback.
## What lives where
| Thing | Where |
| -------------------------------------- | --------------------------------------------------------------------------- |
| Name, tagline, icon, categories, menus | on the server, under `/var/lib/menu-app.<site>/data` — the admin edits them |
| Theme colours | `config/<site>.site.json` in the repo, baked into the image |
| Admin password hash, session secret | `/var/lib/menu-app.<site>/site.env` (untracked, mode 600) |
| Which image tag runs | `-e tag=<tag>` |
| Which Site is being deployed | the inventory entry in `hosts` |
| Thing | Where |
| -------------------------------------- | -------------------------------------------------------------------------- |
| Name, tagline, icon, categories, menus | on the server under `/var/lib/menu-app.<site>/data` — the admin edits them |
| Theme colours | `config/<site>.site.json` in the repo, baked into the image |
| Admin password hash, session secret | `/var/lib/menu-app.<site>/site.env` (untracked, mode 600) |
| Which image tag runs | `-e tag=<tag>` |
| Which Site is deployed | the inventory entry in `hosts` |
| The web address it serves | `host:` in `vars/<site>.yml` |
## Deploying
@@ -66,50 +42,27 @@ Every Site runs on the same box, so `hosts` has one entry per Site pointing at
the same `ansible_host` with its own `var_file`. `ansible_host` is written as the
resolvable name rather than an ssh alias, so the inventory works whatever a
machine's `~/.ssh/config` says. (`ssh` on some machines complains about
`/etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf` being owned by `nobody`; that
is a local file-permission bug with the fix `sudo chown -R root:root
`/etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf` being owned by `nobody`; that is
a local file-permission bug whose fix is `sudo chown -R root:root
/etc/ssh/ssh_config.d`, not something this playbook works around.)
## The first run, and cutover
Adding a Site is one line in `hosts` and a copy of `vars/hopsngrains.yml`.
The first run targets a temporary vhost, so the live site keeps serving:
## Checking it landed
```sh
ansible-playbook --inventory=hosts playbook.yml --limit hopsngrains \
-e tag=2026-09-22 -e vhost=menu-new.hopsngrains.com
npm run check-site -- https://menu.hopsngrains.com
```
`menu-new.hopsngrains.com` needs a DNS A record pointing at the same address as
the live site before this runs: Caddy fetches a certificate for the name, which
needs it to resolve publicly.
Then, from the outside (read-only, exits non-zero on failure):
```sh
npm run check-site -- https://menu-new.hopsngrains.com
```
And from a phone on restaurant wifi: the landing page, a Section page, the page
counter, tap-to-zoom, and signing into `/admin` to publish something.
Cutover is the same playbook with the real vhost and the two retire flags:
```sh
ansible-playbook --inventory=hosts playbook.yml --limit hopsngrains \
-e tag=2026-09-22 -e vhost=menu.hopsngrains.com \
-e retire_nginx_snippet=true -e retire_nginx_container=true
```
That repoints the real vhost at the app, removes the snippet the old `nginx-menu`
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 once the new site has been
trusted.
Read-only, exits non-zero on failure. Then from a phone: the landing page, a
Section page, the page counter, tap-to-zoom, and signing into `/admin` to
publish something.
## The admin password
The app starts with the admin disabled when `/var/lib/menu-app.<site>/site.env`
is missing, and the playbook says so. Create it once per Site:
The app starts with the admin disabled while `/var/lib/menu-app.<site>/site.env`
is missing, and the playbook says so rather than failing. Create it once per
Site:
```sh
scp scripts/provision-site-env.sh monoco:/tmp/
@@ -119,9 +72,10 @@ ssh monoco docker restart menu-app.hopsngrains.com
## Rolling back
Deploy the previous tag:
```sh
ssh monoco docker start nginx-menu.hopsngrains.com
./deploy.sh <previous-tag>
```
then point Caddy back at it — the cutover flags above with
`retire_nginx_snippet=false`.
`docker logs menu-app.<site>` is where a failed container says why.