Files
recurring-checklist/.gitea/workflows/deploy.yml
T
Waters ChurchandClaude Opus 5 1d40872127
Deploy web to NAS / deploy (push) Successful in 2m47s
Drop the duplicate docker.sock mount from the deploy job
act_runner already mounts /var/run/docker.sock into every job container, so
declaring it again in the workflow made the daemon refuse to create the
container at all: "Duplicate mount point: /var/run/docker.sock". The deploy
directory still has to be declared, and still at its own host path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-24 14:02:51 -04:00

91 lines
3.8 KiB
YAML

# Push to main -> the NAS rebuilds itself.
#
# This replaces having to run ./release.sh --web by hand for a server change. It does the
# same two things that script's remote half did, in the same order and with the same
# protections: copy server/ over the live directory, then recreate the container in place.
#
# It runs on the "nas" runner (act_runner, in Docker on the NAS itself), so it does not
# care whether the laptop is awake, on the church network, or in the building.
#
# NOT covered here on purpose: the iOS and Android apps. They bundle a copy of the web UI
# at build time (see mobile/scripts/sync-web.js), so a UI change only reaches phones via a
# new store release. Server-side changes reach them immediately, because they call this
# same server live.
name: Deploy web to NAS
on:
push:
branches: [main]
paths:
- 'server/**'
- '.gitea/workflows/deploy.yml'
workflow_dispatch:
jobs:
deploy:
runs-on: nas
container:
image: node:20-alpine
volumes:
# The docker socket is deliberately NOT listed here: act_runner already mounts it
# into every job container, and declaring it again makes the daemon reject the
# container with "Duplicate mount point: /var/run/docker.sock".
#
# Mounted at the SAME path it has on the host. docker-compose.yml binds ./data
# relative to the compose file, and the daemon resolves that as a HOST path — so if
# this were mounted anywhere else the app would come up pointed at a directory that
# does not exist, with an empty data volume.
- /volume2/docker/recurring-checklist:/volume2/docker/recurring-checklist
steps:
- name: Install tools
run: apk add --no-cache docker-cli docker-cli-compose rsync
- name: Checkout
uses: actions/checkout@v4
- name: Copy server files over the live directory
run: |
set -eu
DEST=/volume2/docker/recurring-checklist
test -f "$DEST/docker-compose.yml" || { echo "REFUSING: $DEST is not the deploy directory"; exit 1; }
# data/ holds the live checklists, uploads and backups; .env holds the admin and
# Gmail credentials. Neither is in git, and neither may ever be written here.
# No --delete: this mirrors the old tarball deploy, which only ever added and
# overwrote. A stale file left behind is survivable; a wiped data/ is not.
rsync -a \
--exclude 'data/' \
--exclude 'data-smoke/' \
--exclude '.env' \
--exclude 'node_modules/' \
server/ "$DEST/"
- name: Verify the live data and credentials survived
run: |
set -eu
DEST=/volume2/docker/recurring-checklist
test -f "$DEST/.env" || { echo "ABORT: .env is gone"; exit 1; }
test -d "$DEST/data" || { echo "ABORT: data/ is gone"; exit 1; }
echo ".env and data/ intact"
- name: Rebuild the container in place
# No 'down' first: that removes the container AND the network, so an interrupted
# deploy would leave the whole stack torn down. 'up -d --build' recreates only the
# app container, and the worst case on failure is the old one still serving.
run: docker compose -f /volume2/docker/recurring-checklist/docker-compose.yml up -d --build --remove-orphans
- name: Healthcheck
run: |
set -eu
for i in $(seq 1 20); do
S=$(docker inspect --format '{{.State.Health.Status}}' recurring-checklist 2>/dev/null || echo starting)
[ "$S" = "healthy" ] && { echo "container healthy"; exit 0; }
sleep 3
done
echo "container did not report healthy in time:"
docker ps --filter name=recurring-checklist --format '{{.Names}} {{.Status}}'
docker logs --tail 30 recurring-checklist
exit 1