mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
ci(image): assert the GeoLite2 MMDBs land in the image when the key is set
v1.4.28 shipped with `offlineGeoEnabled: false` on both production hosts because the Fetch step could silently emit the `.empty` marker and the workflow trusted the script's exit code. The script-side hardening in the previous commit closes the fail-loud gap for download errors; this commit adds a workflow-side belt-and-braces check so a stale `.empty` marker or a workspace-cache surprise cannot survive into the image either. New "Verify GeoLite2 databases" step runs on each platform leg after the Fetch step and before `docker/build-push-action`. When `MAXMIND_LICENSE_KEY` is set it asserts both MMDBs exist in `assets/geolite2/` with non-zero size and that no `.empty` marker survived; either failure exits the leg with a clear `::error::` line. When the secret is unset the step short-circuits to a notice — the intentional "ship without offline tier" path stays usable. `secrets.*` is not allowed in step-level `if:` expressions, so the gate is implemented via env-surfaced presence check inside the step. Validated with `actionlint`. Co-references the live-environment shape in `.planning/round-5-geolite2-ci-fix-report.md`.
This commit is contained in:
1 parent
ed71f334fe
commit
9c265024f4
1 file changed
+54
-11
@@ -103,17 +103,24 @@ jobs:
|
||||
# downloads both editions into `assets/geolite2/` so the Dockerfile
|
||||
# COPY at line 78 picks them up.
|
||||
#
|
||||
# The build is intentionally resilient to a missing
|
||||
# `MAXMIND_LICENSE_KEY` secret: when the key is unset we emit a
|
||||
# `::warning::` line, drop a `.empty` marker into the assets
|
||||
# directory so the Dockerfile COPY still has a non-empty source,
|
||||
# and continue cleanly. The runtime resolver in `src/lib/geo.ts`
|
||||
# detects the marker on first lookup and emits a one-shot
|
||||
# admin notification — the maintainer hears about the gap from
|
||||
# the running app rather than from a red CI badge that blocks
|
||||
# every release. To enable the offline feature, set the secret
|
||||
# under repo Settings → Secrets and variables → Actions and
|
||||
# redeploy.
|
||||
# Two intentional paths:
|
||||
# - `MAXMIND_LICENSE_KEY` unset → emit a `::warning::`, drop a
|
||||
# `.empty` marker into the assets directory so the Dockerfile
|
||||
# COPY still has a non-empty source, and continue cleanly. The
|
||||
# runtime resolver in `src/lib/geo.ts` detects the marker on
|
||||
# first lookup and emits a one-shot admin notification — the
|
||||
# maintainer hears about the gap from the running app rather
|
||||
# than from a red CI badge that blocks every release.
|
||||
# - `MAXMIND_LICENSE_KEY` set → run `fetch-geolite2.sh` and
|
||||
# fail the build if it errors. The "Verify GeoLite2 databases"
|
||||
# step below adds a belt-and-braces assertion that both MMDB
|
||||
# files landed in `assets/geolite2/` before the Dockerfile
|
||||
# COPY reads the directory, so v1.4.28-era silent fallback
|
||||
# (offlineGeoEnabled: false on both nodes after a tag build)
|
||||
# can no longer ship.
|
||||
#
|
||||
# To enable the offline feature, set the secret under repo
|
||||
# Settings → Secrets and variables → Actions and redeploy.
|
||||
- name: Fetch GeoLite2 databases
|
||||
env:
|
||||
MAXMIND_LICENSE_KEY: ${{ secrets.MAXMIND_LICENSE_KEY }}
|
||||
@@ -126,6 +133,42 @@ jobs:
|
||||
fi
|
||||
./scripts/fetch-geolite2.sh
|
||||
|
||||
# Build-time assertion: with the license key present, both MMDB
|
||||
# files must land in `assets/geolite2/` before the Dockerfile
|
||||
# COPY at line 78 picks them up. The fetch script already fails
|
||||
# loud on a curl / tarball error, but a stale `.empty` marker
|
||||
# from a previous keyless run or a workspace-cache surprise could
|
||||
# otherwise survive into the image — this step is the operator
|
||||
# guarantee that "key set" really means "MMDBs in the image".
|
||||
#
|
||||
# `secrets.*` is not available in step-level `if:` expressions,
|
||||
# so we surface the presence flag through env and gate inline.
|
||||
- name: Verify GeoLite2 databases
|
||||
env:
|
||||
MAXMIND_LICENSE_KEY: ${{ secrets.MAXMIND_LICENSE_KEY }}
|
||||
run: |
|
||||
if [ -z "$MAXMIND_LICENSE_KEY" ]; then
|
||||
echo "MAXMIND_LICENSE_KEY unset — verification skipped (offline geo tier intentionally absent on this build)."
|
||||
exit 0
|
||||
fi
|
||||
missing=()
|
||||
for mmdb in GeoLite2-City.mmdb GeoLite2-ASN.mmdb; do
|
||||
if [ ! -s "assets/geolite2/$mmdb" ]; then
|
||||
missing+=("$mmdb")
|
||||
fi
|
||||
done
|
||||
if [ "${#missing[@]}" -gt 0 ]; then
|
||||
echo "::error::MAXMIND_LICENSE_KEY is set but the following GeoLite2 MMDB files are missing or empty in assets/geolite2/: ${missing[*]}. The Dockerfile would COPY them into /opt/geolite2/ in the final image, leaving the offline geo tier disabled at runtime. Investigate the previous 'Fetch GeoLite2 databases' step before retrying."
|
||||
exit 1
|
||||
fi
|
||||
if [ -f assets/geolite2/.empty ]; then
|
||||
echo "::error::Stale .empty marker present in assets/geolite2/ despite MAXMIND_LICENSE_KEY being set. The runtime resolver would treat the image as missing the offline tier. Investigate the previous 'Fetch GeoLite2 databases' step before retrying."
|
||||
exit 1
|
||||
fi
|
||||
city_size="$(stat -c%s assets/geolite2/GeoLite2-City.mmdb)"
|
||||
asn_size="$(stat -c%s assets/geolite2/GeoLite2-ASN.mmdb)"
|
||||
echo "::notice::GeoLite2 MMDBs verified — GeoLite2-City.mmdb ${city_size} bytes; GeoLite2-ASN.mmdb ${asn_size} bytes."
|
||||
|
||||
# Per-ref + per-platform gha cache scope. Tag builds warm-start
|
||||
# off main's last good layer set for the same platform; tag and
|
||||
# main builds at the same SHA do not contend on writes, and
|
||||
|
||||
Reference in new issue
Block a user