Compare commits

..
2 Commits
Author SHA1 Message Date
randy 5c9516d038 Check the deployed revision, not just any 200, in the health poll
CI / deploy (push) Successful in 13s
CI / check (push) Successful in 53s
/api/health now reports REVISION alongside status, and the CI wait
step checks for this deploy's own short SHA specifically - a stray
old container that never got torn down (or a build that silently
kept serving stale code) would still 200 on a plain health check but
now won't match.
2026-09-08 21:19:03 +02:00
randy 4982f04258 Verify the deploy actually came up, not just that it was launched
systemctl restart (Type=simple, no readiness protocol) returns as
soon as podman compose up --build forks, before the build or the
container start have actually finished - a build failure or crash
loop would still show as a green deploy job. Poll /api/health for up
to 5 minutes (comfortably above the ~3.5min worst-case cold rebuild)
and fail the job if it never answers.
2026-09-08 21:16:41 +02:00
4 changed files with 33 additions and 2 deletions

No files matched your search

+23
View File
@@ -109,3 +109,26 @@ jobs:
# (gitea-runner has passwordless sudo for this on cube-host). # (gitea-runner has passwordless sudo for this on cube-host).
- name: Deploy - name: Deploy
run: sudo systemctl restart podman-compose@maptool.service run: sudo systemctl restart podman-compose@maptool.service
# systemctl restart above only confirms the process forked
# (Type=simple, no readiness protocol) — not that `podman compose up
# --build` actually finished and Puma is answering. Polls the health
# endpoint (cube-host itself, same machine the container's port is
# published on) instead of trusting that — checking for this
# deploy's own revision, not just any 200, so a stuck old container
# that never got torn down can't pass as a successful deploy of the
# new one. 5 minutes covers even a from-scratch rebuild (Gemfile
# change, ~3.5 minutes measured) with room to spare; a cached
# rebuild is normally up within a minute.
- name: Wait for it to come up
run: |
expected=$(git rev-parse --short HEAD)
for i in $(seq 1 30); do
if curl -sf http://localhost:4567/api/health | grep -q "\"revision\":\"$expected\""; then
echo "Healthy at $expected after $(( (i - 1) * 10 ))s"
exit 0
fi
sleep 10
done
echo "::error::maptool did not report revision $expected within 5 minutes"
exit 1
+1 -1
View File
@@ -169,7 +169,7 @@ module Maptool
end end
get "/api/health" do get "/api/health" do
json status: "ok" json status: "ok", revision: REVISION
end end
get "/api/offline-tiles" do get "/api/offline-tiles" do
+8
View File
@@ -246,3 +246,11 @@ a retagged image's old layers behind — turning every deploy into a
full `apt-get`/`bundle install` from scratch (~3 minutes measured, full `apt-get`/`bundle install` from scratch (~3 minutes measured,
vs. ~20s before). Fixed by adding `--build` to the unit instead (see vs. ~20s before). Fixed by adding `--build` to the unit instead (see
above); don't reintroduce the `podman rmi` step. above); don't reintroduce the `podman rmi` step.
. Polls `http://localhost:4567/api/health` for *this deploy's own*
revision (up to 5 minutes, the job fails if it never shows up)
before calling the deploy done — the restart above only confirms
`podman compose up --build` was launched (`Type=simple` has no
readiness protocol), not that the build actually succeeded and the
new code is what's answering; checking the revision specifically,
not just any 200, also catches a stuck old container that never got
torn down.
+1 -1
View File
@@ -7,7 +7,7 @@ class AppTest < Minitest::Test
def test_health def test_health
response = request.get("/api/health") response = request.get("/api/health")
assert_equal 200, response.status assert_equal 200, response.status
assert_equal({ "status" => "ok" }, json_body(response)) assert_equal({ "status" => "ok", "revision" => "dev" }, json_body(response))
end end
def test_index_renders_with_asset_versioning_and_importmap def test_index_renders_with_asset_versioning_and_importmap