Files
menu/ansible/roles/menu-app/tasks/main.yaml
T
tanshu 75b6492493 Move the deployment into the app repo, and build it with podman
The playbook, its roles, its vars and a new inventory now live in ansible/,
beside the app they deploy, the way the other apps on this machine are laid out.
The inventory follows the same shape: one entry per Site, all on the same box,
each naming its own vars file through var_file, so adding a Site is one line and
a file rather than a command-line flag. ansible_host is written as the
resolvable name rather than an ssh alias so the inventory works on any machine.

The image is now built with podman as a multi-arch manifest and pushed to
registry.tanshu.com/menu-admin, mirroring the Makefile used elsewhere on this
machine: build-production, plus build-check and build-check-local for a build
that does not push. deploy.sh runs the checks, builds, pushes, then runs the
playbook for the Site named by SITE, with SKIP_DEPLOY for a push without a
deploy.

ESLint comes out again: this project's typescript@7 has no JavaScript compiler
API, so typescript-eslint cannot install, and linting only the JavaScript was
not worth keeping. Prettier stays, and the README records why ESLint is absent
and what would bring it back.
2026-09-22 23:18:18 +05:30

112 lines
3.1 KiB
YAML

---
- name: Pull the app image
docker_image:
name: '{{ image_name }}'
tag: '{{ image_tag }}'
source: pull
force_source: yes
- name: Ensure the app root exists
file:
path: '{{ app_root }}'
state: directory
owner: '{{ app_uid }}'
group: '{{ app_gid }}'
mode: '0750'
- name: Ensure the data directory exists
file:
path: '{{ app_root }}/data'
state: directory
owner: '{{ app_uid }}'
group: '{{ app_gid }}'
mode: '0750'
- name: Look for the untracked site.env
stat:
path: '{{ app_root }}/site.env'
register: menu_site_env
- name: Warn when the admin has no password yet
debug:
msg: >-
{{ app_root }}/site.env is missing, so the admin is disabled: the public
menu still serves. Run scripts/provision-site-env.sh {{ app_root }} on
this host, then re-run this playbook.
when: not menu_site_env.stat.exists
- name: Run the menu app
docker_container:
name: '{{ docker_container }}'
image: '{{ image_name }}:{{ image_tag }}'
state: started
restart_policy: unless-stopped
user: '{{ app_uid }}:{{ app_gid }}'
env:
MENU_ROOT: /data
MENU_SITE_CONFIG: '/app/config/{{ site }}.site.json'
SECURE_COOKIES: 'true'
LOG_LEVEL: info
PORT: '3000'
volumes:
- '{{ app_root }}:/data'
networks:
- name: '{{ docker_network }}'
- name: Keep site.env readable only by the app user
file:
path: '{{ app_root }}/site.env'
owner: '{{ app_uid }}'
group: '{{ app_gid }}'
mode: '0600'
when: menu_site_env.stat.exists
- name: Wait for the app to report healthy
# The image's own HEALTHCHECK fetches /healthz inside the container. Polling
# that is deliberate: a container name resolves only on the Docker network, so
# the host cannot fetch http://{{ docker_container }}:3000/healthz.
docker_container_info:
name: '{{ docker_container }}'
register: menu_health
changed_when: false
retries: 20
delay: 5
until: >-
menu_health.container is defined and
menu_health.container.State.Health is defined and
menu_health.container.State.Health.Status == 'healthy'
- name: Fail when the app never becomes healthy
fail:
msg: >-
{{ docker_container }} did not report healthy within 100s. Check
`docker logs {{ docker_container }}`.
when: menu_health.container.State.Health.Status | default('unknown') != 'healthy'
- name: Check the data directory for integrity problems
# Reads the same directory the container writes, as the same user, so a
# permissions mistake shows up here rather than at 8pm service.
command:
argv:
- docker
- run
- --rm
- --user
- '{{ app_uid }}:{{ app_gid }}'
- -e
- MENU_ROOT=/data
- -e
- 'MENU_SITE_CONFIG=/app/config/{{ site }}.site.json'
- --entrypoint
- node
- '{{ image_name }}:{{ image_tag }}'
- src/cli/verify.ts
register: menu_verify
changed_when: false
failed_when: false
- name: Report integrity problems
debug:
msg: '{{ menu_verify.stdout_lines | default([]) }}'
when: menu_verify.rc != 0