diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index 75228cb..bfb321b 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -188,6 +188,16 @@ jobs: DICT_VERSION: ${{ vars.TEST_DICT_VERSION }} LOG_LEVEL: ${{ vars.TEST_LOG_LEVEL }} run: | + # Seed the config files to a stable host path. The runner checks out into + # an ephemeral act workspace that is removed after the job, which would + # dangle the compose config bind mounts in the long-lived containers + # (e.g. Grafana then logs "no such file or directory"). Bind from a stable + # dir instead (mirrors ../galaxy-game's $HOME/.galaxy-dev/monitoring). + conf="$HOME/.scrabble-deploy" + rm -rf "$conf" + mkdir -p "$conf" + cp -r caddy otelcol prometheus tempo grafana "$conf"/ + export SCRABBLE_CONFIG_DIR="$conf" docker compose --ansi never build --progress plain docker compose --ansi never up -d --remove-orphans diff --git a/deploy/README.md b/deploy/README.md index 0b28545..62ab89d 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -42,6 +42,14 @@ runs `docker compose up -d --build` on the runner host. Stage 18 (prod) maps the **`PROD_`** set the same way. So a Gitea secret named `TEST_POSTGRES_PASSWORD` feeds the compose's `POSTGRES_PASSWORD`, etc. +The deploy job also **seeds the config files** (`caddy`, `otelcol`, `prometheus`, +`tempo`, `grafana`) to a stable host path (`$HOME/.scrabble-deploy`) and sets +`SCRABBLE_CONFIG_DIR` to it before `up`. The runner's checkout is an ephemeral act +workspace that is removed after the job — binding config straight from it would +dangle the mounts in the long-lived containers (Grafana would log +`no such file or directory`). Locally `SCRABBLE_CONFIG_DIR` defaults to `.`, so the +compose binds from this directory. + ## Required variables `docker compose` aborts immediately if any of these is unset (they use `:?`): diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index f36a531..09cb03d 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -5,6 +5,12 @@ # interpolated from Gitea Actions TEST_ secrets/variables exported by the deploy # job (see deploy/.env.example for the unprefixed names). # +# Config bind sources are prefixed with ${SCRABBLE_CONFIG_DIR:-.}: locally they bind +# straight from this directory, but CI seeds them to a stable host path and sets +# SCRABBLE_CONFIG_DIR to it, because the runner's checkout is ephemeral (act removes +# it after the job) and the bind mounts must outlive the job in the long-running +# containers (see .gitea/workflows/ci.yaml + deploy/README.md). +# # Networking (mirrors ../galaxy-game): # - `internal` (scrabble-internal): all inter-service traffic, project-private # DNS so service names never collide on the shared `edge` network. @@ -148,7 +154,7 @@ services: GM_BASICAUTH_USER: ${GM_BASICAUTH_USER:-gm} GM_BASICAUTH_HASH: ${GM_BASICAUTH_HASH:?set GM_BASICAUTH_HASH} volumes: - - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - ${SCRABBLE_CONFIG_DIR:-.}/caddy/Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/data networks: internal: {} @@ -162,7 +168,7 @@ services: restart: unless-stopped command: ["--config=/etc/otelcol/config.yaml"] volumes: - - ./otelcol/config.yaml:/etc/otelcol/config.yaml:ro + - ${SCRABBLE_CONFIG_DIR:-.}/otelcol/config.yaml:/etc/otelcol/config.yaml:ro networks: [internal] prometheus: @@ -173,7 +179,7 @@ services: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.retention.time=15d volumes: - - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ${SCRABBLE_CONFIG_DIR:-.}/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus-data:/prometheus networks: [internal] @@ -183,7 +189,7 @@ services: restart: unless-stopped command: ["-config.file=/etc/tempo/tempo.yaml"] volumes: - - ./tempo/tempo.yaml:/etc/tempo/tempo.yaml:ro + - ${SCRABBLE_CONFIG_DIR:-.}/tempo/tempo.yaml:/etc/tempo/tempo.yaml:ro - tempo-data:/var/tempo networks: [internal] @@ -204,11 +210,11 @@ services: GF_USERS_ALLOW_SIGN_UP: "false" GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin} volumes: - - ./grafana/provisioning:/etc/grafana/provisioning:ro + - ${SCRABBLE_CONFIG_DIR:-.}/grafana/provisioning:/etc/grafana/provisioning:ro # Dashboards live under /etc/grafana (NOT /var/lib/grafana, which the # grafana-data volume mounts over — a nested bind there is shadowed and the # provider logs "no such file or directory"). - - ./grafana/dashboards:/etc/grafana/dashboards:ro + - ${SCRABBLE_CONFIG_DIR:-.}/grafana/dashboards:/etc/grafana/dashboards:ro - grafana-data:/var/lib/grafana networks: [internal]