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.
58 lines
1.7 KiB
Bash
Executable File
58 lines
1.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Builds the image with podman, pushes it to the private registry, then deploys
|
|
# the Site with the playbook that lives in ansible/. The playbook runs rootless
|
|
# Podman Quadlet units on emily, behind the shared Caddy unit from
|
|
# ansible-playbooks/caddy.
|
|
#
|
|
# ./deploy.sh 2026-09-22 deploy the default Site
|
|
# SITE=greatbear ./deploy.sh 2026-09-22 deploy another Site
|
|
# SKIP_DEPLOY=1 ./deploy.sh 2026-09-22 build and push only
|
|
# SKIP_CHECKS=1 ./deploy.sh 2026-09-22 skip format, typecheck and tests
|
|
#
|
|
set -euo pipefail
|
|
|
|
TAG="${1:-}"
|
|
if [[ -z "$TAG" ]]; then
|
|
echo "usage: $0 <tag> (e.g. $(date +%Y-%m-%d) or a git short sha)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
parent_path=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)
|
|
cd "$parent_path"
|
|
|
|
if ! command -v podman >/dev/null 2>&1; then
|
|
echo "podman is required to build and push the image" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${SKIP_DEPLOY:-}" ]] && ! command -v ansible-playbook >/dev/null 2>&1; then
|
|
echo "ansible-playbook is required to deploy; SKIP_DEPLOY=1 builds and pushes only" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${SKIP_CHECKS:-}" ]]; then
|
|
echo "==> lint, format, types and tests"
|
|
npm run lint
|
|
npm run format:check
|
|
npm run typecheck
|
|
npm test
|
|
fi
|
|
|
|
echo "==> building and pushing registry.tanshu.com/menu-admin:$TAG"
|
|
make build-production TAG="$TAG"
|
|
|
|
if [[ -n "${SKIP_DEPLOY:-}" ]]; then
|
|
echo "SKIP_DEPLOY is set, so the playbook was not run."
|
|
exit 0
|
|
fi
|
|
|
|
cd ansible
|
|
if [[ -n "${SITE:-}" ]]; then
|
|
echo "==> deploying $SITE"
|
|
ansible-playbook --inventory=hosts playbook.yml --limit "$SITE" -e "tag=$TAG"
|
|
else
|
|
echo "==> deploying every Site"
|
|
ansible-playbook --inventory=hosts playbook.yml -e "tag=$TAG"
|
|
fi
|