Commit Graph
7 Commits
Author SHA1 Message Date
tanshu 537a4f1e46 Match the deploy system to hops and greatbear
menu-admin was the odd one out: it built from the working directory with a date
tag and a multi-arch manifest, deployed with `-e tag=`, and kept the Docker-era
container name. The other two apps build from `git archive HEAD` and tag with the
commit, so what runs on the host is exactly what git knows about; build only for
the architecture emily actually runs; and use one slug for the container, its
network and its data directory.

The build is now their three lines: `podman build --platform linux/arm64` from
`git archive HEAD`, tagged `:<commit>` and `:latest`, pushed with `podman push`
rather than a manifest. `deploy.sh` pushes the commit first, then refuses a dirty
tracked tree or a HEAD that is not origin/main — which is what makes the commit
tag trustworthy — takes the same `--force` and `--no-push`, and ends in the same
`-u tanshu ... -e image_tag=<commit>`. Its own lint/typecheck/test gate stays,
but behind `--checks`, so a plain deploy behaves as theirs does.

The registry credential moves into vars/default.yml and the role logs in, pulls
and logs out around it, as their roles do. The image is only pulled at deploy
time, so the unit keeps podman's default `Pull=missing` and a reboot never needs
the registry. This is the same plaintext credential the other two repos carry.

One real bug goes with it: the healthcheck gate ran before the handler that
restarts a changed unit, so a run that only changed the unit checked the stale
container. `meta: flush_handlers` before the gate is the fix the sibling repos
already made.

The container, network and data directory are all menu-<site> now. The dotted
Docker-era name and the NetworkAlias it needed are gone, so `podman logs
menu-<site>` and `systemctl --user status menu-<site>` are the same two commands
across all three apps. The Dockerfile keeps its name for the same reason: podman
reads it as readily as a Containerfile, and the siblings use that name too.
2026-10-16 11:10:59 +05:30
tanshu e68f8ab00d Run the Sites as rootless Podman Quadlets, and build with podman
The fleet on emily is rootless Podman with Quadlet units under
~/.config/containers/systemd and state in ~/podman-data, all behind one shared
Caddy unit. This brings the menus in line instead of leaving them the last
Docker deployment. The build moves with them: podman reads a Containerfile as
readily as a Dockerfile, so the name now says which toolchain is real.

The app is one .container and one .network file. Two details carry it.
UserNS=keep-id maps the image's own node account onto the user that owns the
units, so data/ and site.env stay owned by tanshu: plain tar backups, no
subuids. provision-site-env.sh writes site.env with the same mapping rather
than --user, which under rootless Podman would have produced a subuid-owned
file the app could not read. And the Site registers its own network with Caddy
by writing a drop-in beside caddy.container, so Caddy never lists its upstreams
and never changes when a Site is added. The unit keeps the Docker-era container
name for the scripts and muscle memory that look it up, while the Caddyfile
proxies to a dotless NetworkAlias, because a container name with dots in it is
a liability in DNS.

The unit keeps podman's Pull=missing, so a restart or a reboot never depends on
the registry being reachable, while the play pulls explicitly at deploy time.
That is also where a missing `podman login` for the private registry now fails,
with the instruction, rather than as a unit that will not start.

copy-site-data.sh moves a Site's data across, and is not a plain rsync:
/var/lib/menu.* on monoco belongs to uid 1000 while the ssh user there is 1001,
so the read needs sudo; monoco cannot ssh to emily, so the workstation relays
the tar; and the extraction runs as tanshu on emily, which is what makes the
files land owned by the user keep-id maps the app onto. It proves the copy by
comparing a SHA-256 of the whole tree against the source and checking every
file's owner.

Not yet done: the copies themselves and the DNS move, which need a human.
2026-10-16 09:54:52 +05:30
tanshu 11f60c619f No minimum password length, and say how to reset it
The script refused anything under ten characters. That is a policy the owner
should hold, not one the tooling invents; the only rule left is that the password
cannot be empty, since an empty one would let anyone in. Resetting is the same
script with --force, which overwrites the hash and generates a fresh session
secret, signing out anyone who was signed in - now written down in the operating
notes and the deployment readme, including the form that prompts on your own
machine instead of the server.
2026-09-23 00:02:35 +05:30
tanshu a81d96668c Refuse to hang at the password prompt
ssh host bash script gives no terminal, so read -s could neither show the prompt
nor read it, and the script sat there looking frozen. It reads from /dev/tty now
and, when there is no terminal, says so and prints the command that works:
ssh -t <host> bash bash <data-root>. The deployment readme shows the -t form and
uses the real paths.
2026-09-22 23:55:34 +05:30
tanshu 3eb6be6c7d Borrow the running app's image for the password step
The script defaulted to menu-admin:latest and refused to run on monoco, where the
image is registry.tanshu.com/menu-admin:<tag>. It now reads the image off the
container the app is already running, deriving the container name the way the
playbook does, and only asks for an explicit image when there is no container to
borrow one from. The failure message names the exact command either way.
2026-09-22 23:51:46 +05:30
tanshu 8af52a0f7d Provision site.env as the app user, not the SSH user
The data root belongs to the app's uid, so the SSH user usually cannot write
into it, and a site.env they did write would be unreadable by the app — the
admin would stay disabled after provisioning. The script now writes the file
through a container running as the app user, so ownership is right the first
time and no playbook re-run is needed. Also checks the data root exists and
tells you to run the playbook if it does not.
2026-09-22 21:34:04 +05:30
tanshu bb9ddb437f Ship a repair command and a container
verify reports integrity problems and repair rebuilds derivatives from the
Originals, which is what makes the file layout trustworthy without a database.
The image runs TypeScript on Node 24 with no build step, writes only under
MENU_ROOT, and the provision script writes the untracked site.env using the
image's own hasher so the server needs no Node.
2026-09-22 21:24:47 +05:30