c6864996f0
The verify step ran 0.09s after `docker compose up -d --force-recreate`, well before the container's 30s start_period elapsed — so 'healthy' isn't yet in docker ps's Status string and the grep failed. False negative; container was operationally up (verify 1/2 already confirmed /health 200). Greping for '^Up' instead catches the case verify 2/2 actually wants to catch (container exited entirely) without racing the healthcheck.
124 lines
5.5 KiB
YAML
124 lines
5.5 KiB
YAML
# Deploy skaldsong (https://gitea.phasefinal.com/vh/skaldsong) to a
|
|
# Docker host following the PFI /opt/docker/ convention (ana-docker by
|
|
# default, but the playbook works against any host with Docker in place).
|
|
#
|
|
# Registry-pull pattern, NOT build-on-host: vh/skaldsong's CI builds and
|
|
# pushes `gitea.phasefinal.com/vh/skaldsong:<sha>` (+ :latest) to the
|
|
# gitea registry; this playbook just pulls + recreates the container.
|
|
# Differs from althing-chamber / asset-engine playbooks which build the
|
|
# image on-host from a git clone — those are local-build stacks.
|
|
#
|
|
# Idempotent: rerunning is safe. Creates-gates skip work that's already
|
|
# done; `docker compose up -d` is itself idempotent (no restart unless
|
|
# compose content, env, or image SHA changed).
|
|
#
|
|
# Usage:
|
|
# # CI passes the triggering commit SHA via --var ref=${{ github.sha }}:
|
|
# scripts/elway ana-docker --playbook playbooks/deploy-skaldsong.yaml \
|
|
# --var ref=<sha>
|
|
#
|
|
# # Manual runs can pass a branch or tag (will resolve to the registry
|
|
# # tag of that name — CI publishes :main and :v0.1.0 tags alongside
|
|
# # the SHA-pin):
|
|
# scripts/elway ana-docker --playbook playbooks/deploy-skaldsong.yaml \
|
|
# --var ref=main
|
|
#
|
|
# Prereqs on the target host:
|
|
# - Docker + docker compose plugin
|
|
# - Target user (lkraven) is in the `docker` group
|
|
# - `docker login gitea.phasefinal.com` has been done at least once
|
|
# (credentials persist in /home/lkraven/.docker/config.json). CI's
|
|
# workflow runs its own `docker login` step from REGISTRY_USER /
|
|
# REGISTRY_TOKEN secrets; manual runs assume the host's auth is
|
|
# already established.
|
|
|
|
vars:
|
|
registry_image: gitea.phasefinal.com/vh/skaldsong
|
|
ref: latest
|
|
compose_dir: /opt/docker/compose/skaldsong
|
|
db_dir: /opt/docker/conf/skaldsong/db
|
|
runs_dir: /opt/docker/conf/skaldsong/runs
|
|
host_port: "8300"
|
|
|
|
steps:
|
|
# ── compose + state dirs ────────────────────────────────────────────
|
|
- name: Ensure compose dir exists
|
|
shell: mkdir -p {{ compose_dir }}
|
|
creates: "{{ compose_dir }}"
|
|
|
|
- name: Ensure DB dir exists
|
|
# Created as lkraven (uid 1000 on these hosts), matching the
|
|
# container's app user — no chown dance needed.
|
|
shell: mkdir -p {{ db_dir }}
|
|
creates: "{{ db_dir }}"
|
|
|
|
- name: Ensure runs dir exists
|
|
# Separate from db_dir so runs/ can later move to a bigger volume
|
|
# without touching DB state.
|
|
shell: mkdir -p {{ runs_dir }}
|
|
creates: "{{ runs_dir }}"
|
|
|
|
# ── deploy compose files ────────────────────────────────────────────
|
|
- name: Upload compose.yaml
|
|
upload:
|
|
src: stacks/skaldsong/compose.yaml
|
|
dest: "{{ compose_dir }}/compose.yaml"
|
|
mode: "0644"
|
|
|
|
- name: Seed .env from template (only if absent)
|
|
upload:
|
|
src: stacks/skaldsong/.env.example
|
|
dest: "{{ compose_dir }}/.env"
|
|
mode: "0644"
|
|
when: "[ ! -f {{ compose_dir }}/.env ]"
|
|
|
|
# ── pin SKALDSONG_IMAGE to the requested ref ────────────────────────
|
|
- name: Set SKALDSONG_IMAGE pin to {{ ref }}
|
|
# Idempotent: strip any existing SKALDSONG_IMAGE= line and append
|
|
# the new one. CI passes --var ref=<sha>; manual runs pass branch /
|
|
# tag names (CI publishes those as registry tags too).
|
|
shell: |
|
|
sed -i '/^SKALDSONG_IMAGE=/d' {{ compose_dir }}/.env
|
|
echo "SKALDSONG_IMAGE={{ registry_image }}:{{ ref }}" >> {{ compose_dir }}/.env
|
|
|
|
# ── pull + bring up ─────────────────────────────────────────────────
|
|
- name: Pull image {{ registry_image }}:{{ ref }}
|
|
# Explicit pull step (rather than relying on compose up's pull) so
|
|
# any auth failure surfaces here, not buried in compose output.
|
|
shell: docker pull {{ registry_image }}:{{ ref }}
|
|
|
|
- name: docker compose up -d --force-recreate
|
|
# --force-recreate picks up env changes even if the image SHA is
|
|
# identical to what's running (rare but happens on env-only deploys).
|
|
# --pull never: we just pulled explicitly above, so no need for
|
|
# compose to re-check.
|
|
shell: cd {{ compose_dir }} && docker compose up -d --force-recreate --pull never
|
|
|
|
- name: Wait for skaldsong /health to respond
|
|
# Container's healthcheck is internal; this host-side poll confirms
|
|
# the published port is reachable + the FastAPI app finished booting.
|
|
# Generous retry budget — first-deploy bootstrapping (DB migrations,
|
|
# SPA asset indexing) can take 30s+.
|
|
shell: |
|
|
for i in $(seq 1 30); do
|
|
curl -sf -o /dev/null http://localhost:{{ host_port }}/health && exit 0
|
|
sleep 2
|
|
done
|
|
exit 1
|
|
changed_when: "false"
|
|
|
|
verify:
|
|
- name: skaldsong /health returns 200
|
|
shell: curl -sf -o /dev/null http://localhost:{{ host_port }}/health
|
|
changed_when: "false"
|
|
|
|
- name: skaldsong container running
|
|
# Grep for '^Up' (not 'healthy') — verify runs immediately after the
|
|
# deploy step finishes, which is well before the container's
|
|
# start_period (30s) elapses. The 'healthy' state from docker ps is
|
|
# a delayed echo of the same /health probe verify 1/2 already
|
|
# confirms, so racing the start_period here is bogus. Keep this
|
|
# check to catch the "container exited entirely" case.
|
|
shell: docker ps --filter name=^/skaldsong$ --format '{{.Status}}' | grep -q '^Up'
|
|
changed_when: "false"
|