Skip to content

feat(deploy): version-tagged release images + same-origin frontend mode - #54

Merged
tbcsec merged 1 commit into
mainfrom
feat/versioned-release-images
Jul 26, 2026
Merged

feat(deploy): version-tagged release images + same-origin frontend mode#54
tbcsec merged 1 commit into
mainfrom
feat/versioned-release-images

Conversation

@tbcsec

@tbcsec tbcsec commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Follow-up to the v1.1.0 release: pinned, reproducible images per version tag.

What

  • release-images.yml — pushing a v* tag builds and publishes ghcr.io/<owner>/flagpost-{backend,frontend}:vX.Y.Z (+ :latest). workflow_dispatch on a tag ref backfills an already-pushed tag (guarded so only tag refs can publish).
  • Same-origin frontend mode — the piece that makes a versioned frontend image meaningful. The demo image bakes an absolute NEXT_PUBLIC_API_URL into the bundle, so a tagged copy would be hard-wired to one operator's domain. The release image bakes an empty origin instead: apiFetch/apiAssetUrl already degrade to relative paths (contract now documented at the base constant), and ws.ts derives its ws(s):// base from window.location at connect time — relative URLs in new WebSocket() aren't reliable across browsers, hence the explicit derivation. One image then works behind any single-origin proxy (the documented Caddy topology), no PUBLIC_ORIGIN rebuild.
  • README documents the pull-instead-of-build path.

Proven end to end, not assumed

Built the frontend image exactly as the workflow will (--build-arg NEXT_PUBLIC_API_URL=) and ran it under the real production compose + Caddy:

  • first-run setup wizard completed through localhost:8080/api (same-origin)
  • login + app load: every API request in the network log went to localhost:8080/api/*, none to a baked origin
  • the scoreboard's WebSocket connected through ws://localhost:8080 — verified via the "live" badge, which renders only on an open socket

Bug found by the smoke test (fixed here)

docker-compose.yml set DEMO_MODE: ${DEMO_MODE:-} — an empty string when unset, which pydantic rejects as a bool, so a fresh docker compose up crash-looped the backend. Now defaults to "false". This affected any new operator following the README quick start.

After merge

I'll backfill v1.1.0 images via gh workflow run release-images.yml --ref v1.1.0 and verify the GHCR tags exist.

Existing deployments unaffected: demo images and the build-from-source compose path are unchanged (dev/demo bake absolute origins exactly as before; ?? keeps the deliberate "" from falling back).

🤖 Generated with Claude Code

Pushing a v* tag now publishes ghcr.io/<owner>/flagpost-{backend,
frontend}:vX.Y.Z (+ :latest) — pinned images for reproducible deploys
(release-images.yml; workflow_dispatch on a tag ref backfills an
existing tag).

The piece that makes a versioned *frontend* image meaningful: the demo
image bakes an absolute origin (NEXT_PUBLIC_API_URL) into the bundle, so
a tagged copy would be hard-wired to one operator's domain. The release
image instead bakes an **empty** origin — same-origin mode: apiFetch and
apiAssetUrl already degrade to relative paths (documented at the base
constant now), and ws.ts derives its ws(s) base from window.location at
connect time, since relative URLs in `new WebSocket()` aren't reliable
across browsers. One image then works behind any single-origin proxy
(the documented Caddy topology) with no PUBLIC_ORIGIN rebuild.

Proven end to end, not assumed: built the frontend image exactly as the
workflow does (empty build-arg) and ran it under the production compose
+ Caddy — first-run setup, login, and every API call went through
localhost:8080/api same-origin, and the scoreboard's WS room connected
through ws://localhost:8080 (the "live" badge renders only on an open
socket).

That smoke test also caught a real production-compose bug: DEMO_MODE
defaulted to an empty string (${DEMO_MODE:-}), which pydantic rejects as
a bool — a fresh `docker compose up` crash-looped the backend. Now
defaults to "false".

README documents the pull-instead-of-build path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tbcsec
tbcsec merged commit 0727f16 into main Jul 26, 2026
4 checks passed
@tbcsec
tbcsec deleted the feat/versioned-release-images branch July 26, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant