From b4ebe40d644ac4748af482ae50677f8f0d2f6e7c Mon Sep 17 00:00:00 2001 From: "github.sudoku" Date: Sun, 31 May 2026 21:46:49 +0200 Subject: [PATCH] ci: build + boot the Docker image on every PR (#212 follow-up) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a docker-smoke job to ci.yml: builds the self-host image (amd64, native), boots it, and polls /api/health until {"ok":true}; then runs `caddy validate` on both Caddyfile and Caddyfile.lan with a blank ACME_EMAIL. The existing jobs only build/boot the SPA+server from the SOURCE tree, so image-specific failure modes (multi-stage npm ci --omit=dev, the COPY set, baked NODE_ENV=production, Caddyfile parsing) were never exercised — that is why issue #212 shipped five such bugs through green CI. This job would have caught four of the five automatically (build, boot crash x2, empty-email Caddyfile parse). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3e63ede..2f19788f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -227,3 +227,52 @@ jobs: - name: Dump server log on failure if: failure() run: tail -100 /tmp/fz-serve.log || true + + docker-smoke: + name: Docker image (build + boot) + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: [typecheck] + # The jobs above build and boot the SPA + Hono server from the SOURCE + # tree. None of them build the Docker IMAGE — which is what self-hosters + # actually run, and which has its own failure modes the source path can't + # surface: the multi-stage `npm ci --omit=dev`, the `COPY` set, and a + # baked `NODE_ENV=production`. Issue #212 shipped five such bugs (husky + # `npm ci` exit 127, a missing `COPY packages/`, a production-only boot + # guard, a Caddyfile parse error) precisely because nothing here built + # and ran the image. This job closes that gap. + steps: + - uses: actions/checkout@v6 + + - name: Build the self-host image (amd64, native) + run: docker build -t feedzero:smoke . + + - name: Boot the image and probe /api/health + run: | + docker run -d --name fz -p 3000:3000 feedzero:smoke + ok= + for i in $(seq 1 30); do + body=$(curl -fsS http://localhost:3000/api/health 2>/dev/null || true) + echo "attempt $i: ${body:-}" + case "$body" in *'"ok":true'*) ok=1; break;; esac + sleep 2 + done + echo "--- container logs (tail) ---" + docker logs fz 2>&1 | tail -40 + if [ -z "$ok" ]; then + echo "::error::image never reported healthy on /api/health" + exit 1 + fi + + - name: Validate Caddy configs (public + LAN, blank ACME_EMAIL) + run: | + for f in Caddyfile Caddyfile.lan; do + echo "validating $f" + docker run --rm -e HOSTNAME=127.0.0.1 -e ACME_EMAIL= \ + -v "$PWD/$f:/etc/caddy/Caddyfile:ro" caddy:2-alpine \ + caddy validate --adapter caddyfile --config /etc/caddy/Caddyfile + done + + - name: Stop the container + if: always() + run: docker rm -f fz >/dev/null 2>&1 || true