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.
This commit is contained in:
tanshu committed 2026-10-16 11:10:59 +05:30
1 parent e68f8ab00d
commit 537a4f1e46
21 files changed
+407 -307

No files matched your search

+11 -36
View File
@@ -1,50 +1,25 @@
---
# Append this Site's block to the shared Caddyfile.
#
# Caddy is its own playbook (ansible-playbooks/caddy/playbook.yml) and a rootless
# Quadlet unit there, so this role does not manage the container — it only points
# it at the app. The Caddyfile is a bind mount into that container, so Caddy sees
# a written change as soon as the file lands; the handler only asks Caddy to
# re-read it. That is a reload, not a restart, so the other hosted sites are
# untouched.
# The Caddyfile is a bind mount into the Caddy container, so Caddy sees the
# change as soon as the file is written; the handler only asks Caddy to re-read
# it. That is a reload, not a restart, so the other hosted sites are untouched.
#
# blockinfile is idempotent on its own via the marker, so there is no need to
# slurp the file and compare first — which also matters because slurp *fails*
# when the Caddyfile does not exist yet, on a host where the Caddy playbook has
# when the Caddyfile does not exist yet, on a host where caddy/playbook.yml has
# not run.
#
# The marker is the same one the retired nginx-menu playbook wrote per hostname,
# so a Site cut over from it has its block replaced in place rather than a second
# block for one address appended, which would make Caddy refuse the whole config.
- name: Check the Caddyfile exists
ansible.builtin.stat:
path: '{{ caddyfile_path }}'
register: menu_caddyfile
- name: Fail when Caddy is not where we expect it
ansible.builtin.fail:
msg: >-
{{ caddyfile_path }} is missing. Deploy the Caddy playbook
(ansible-playbooks/caddy/playbook.yml), which owns the Caddy unit and its
config, before this one.
when: not menu_caddyfile.stat.exists
# The Site's web address may still be claimed by an older snippet: the retired
# nginx-menu playbook wrote one per hostname. Two blocks for one address make
# Caddy refuse the whole config, and a refused reload leaves the old proxy in
# place, which is a 502 once the old container is gone.
- name: Drop any older snippet claiming this web address
- name: 'Add the {{ host }} site block to the Caddyfile'
ansible.builtin.blockinfile:
path: '{{ caddyfile_path }}'
marker: '# {mark} Ansible managed Caddy snippet for {{ host }}'
state: absent
notify: Reload Caddy
# blockinfile replaces whatever sits between its markers, so redeploying updates
# the snippet in place rather than appending a second one.
- name: Point the Site's web address at the app
ansible.builtin.blockinfile:
path: '{{ caddyfile_path }}'
marker: '# {mark} ANSIBLE MANAGED: menu app ({{ site }})'
block: '{{ caddy_snippet }}'
create: false
block: '{{ snippet_block }}'
create: true
owner: '{{ podman_user }}'
group: '{{ podman_user }}'
mode: '0644'