From 7615ecb6e74e23859f46ea15a0b330f1ad9d2b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 3 May 2026 22:18:06 +0200 Subject: [PATCH 1/2] Test PR for staging preview lifecycle Adds a one-line clarifying comment near TERMS_GATE_ALLOWLIST. The point of this PR is to exercise cd-staging.yml end-to-end (preview deploy on open, update on push, teardown on close). Co-Authored-By: Claude Opus 4.7 --- apps/journal/app/root.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/journal/app/root.tsx b/apps/journal/app/root.tsx index 2530ef3..1a475db 100644 --- a/apps/journal/app/root.tsx +++ b/apps/journal/app/root.tsx @@ -18,6 +18,7 @@ import stylesheet from "@trails-cool/ui/styles.css?url"; // Paths that must stay reachable even when the user has a stale // terms_version, so they can read the Terms, accept them, or log out. +// `/legal/` matches as a prefix (covers `/legal/terms`, `/legal/privacy`, etc.). const TERMS_GATE_ALLOWLIST = [ "/auth/accept-terms", "/auth/logout", From 25d21161c58baf282cf050b6abda35ba5870d329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Sun, 3 May 2026 22:30:05 +0200 Subject: [PATCH 2/2] Fix staging port binding + diagnostic ps env file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs in the staging-environments rollout: 1. Staging containers published on 127.0.0.1 are unreachable from the production Caddy container, which connects via the Docker bridge IP (host.docker.internal:host-gateway resolves to the bridge, not loopback). Bind to 0.0.0.0 instead — Hetzner Cloud firewall blocks ports 3000+ from the public internet, so it stays internal-only. 2. The trailing 'docker compose ps' diagnostic in deploy-staging / deploy-preview was missing --env-file staging.env, so compose failed env interpolation and the job exited non-zero even when the actual deploy succeeded. Co-Authored-By: Claude Opus 4.7 --- .github/workflows/cd-staging.yml | 4 ++-- infrastructure/docker-compose.staging.yml | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cd-staging.yml b/.github/workflows/cd-staging.yml index c741b49..5169f55 100644 --- a/.github/workflows/cd-staging.yml +++ b/.github/workflows/cd-staging.yml @@ -165,7 +165,7 @@ jobs: # via cd-infra) are live. Idempotent. docker compose exec -T caddy caddy reload --config /etc/caddy/Caddyfile || true - docker compose -f docker-compose.staging.yml -p trails-staging ps + docker compose -f docker-compose.staging.yml -p trails-staging --env-file staging.env --profile persistent ps # ── PR preview deploy ──────────────────────────────────────────────── deploy-preview: @@ -303,7 +303,7 @@ jobs: # Reload Caddy to pick up the per-PR snippet (writes/replaces it from the SCP step) docker compose exec -T caddy caddy reload --config /etc/caddy/Caddyfile - docker compose -f docker-compose.staging.yml -p "$PROJECT" ps + docker compose -f docker-compose.staging.yml -p "$PROJECT" --env-file staging.env ps - name: Comment preview URL on PR uses: peter-evans/create-or-update-comment@v4 diff --git a/infrastructure/docker-compose.staging.yml b/infrastructure/docker-compose.staging.yml index 1f91537..3b1d60d 100644 --- a/infrastructure/docker-compose.staging.yml +++ b/infrastructure/docker-compose.staging.yml @@ -31,8 +31,13 @@ services: journal: image: ghcr.io/trails-cool/journal:${JOURNAL_IMAGE_TAG:-latest} restart: unless-stopped + # Published on 0.0.0.0 (not 127.0.0.1) so the production Caddy container + # can reach it via host.docker.internal — Docker's host-gateway resolves + # to the bridge IP, not loopback. The Hetzner Cloud firewall blocks all + # inbound on ports 3000+ from the public internet, so this is effectively + # internal-only despite the bind address. ports: - - "127.0.0.1:${JOURNAL_HOST_PORT:?JOURNAL_HOST_PORT must be set}:3000" + - "${JOURNAL_HOST_PORT:?JOURNAL_HOST_PORT must be set}:3000" networks: - trails-shared environment: @@ -72,8 +77,9 @@ services: # planner.staging.trails.cool. Saves ~256MB per active preview. profiles: ["persistent"] restart: unless-stopped + # Same rationale as the journal port — see the comment there. ports: - - "127.0.0.1:${PLANNER_HOST_PORT:-3101}:3001" + - "${PLANNER_HOST_PORT:-3101}:3001" networks: - trails-shared environment: