Development¶
Updating the app image¶
Put the new jvm tag and its digest in marola-image (the tag is
jvm-<short sha> from the app's docker.yml; docker buildx imagetools inspect <ref> prints the
digest), then run just board-schema --update and commit both files together. board-schema.yml
fails a PR whose vendored schema differs from the image's, and site.yml checks the page against
the image's own schema before every deploy. The jvm image is required: the schema is read out of
/app/marola.jar, which the native image does not have. A board schema change in the app is two
PRs: the app's first, then this bump.
The build and deploy (site.yml)¶
Runs on a schedule (every three hours), on a push to main touching site/**, marola-image or
the workflow itself, on repository_dispatch: site-data-updated (sent by every site-data
writer), and on workflow_dispatch (optionally one area). concurrency: site queues a run rather
than cancelling one that already paid for its Overpass query.
The job: pull the pinned image (two tries) → check the harness and the page against the image's
own board schema → build every area's boards (or one, with an area input) → copy the static page
into site/dist → write the Mapbox token and style into mapbox-config.js → cache-bust
index.html's script/style tags with ?v=<sha> (Pages caches each file independently) → add the
site-data branch's panels → a required-files check (a half-built site must never reach Pages) →
the publish allowlist (only the page, its assets and data/, smoke/, coverage/, stats/ go
out) → a CNAME file → deploy to GitHub Pages.
The site-data branch layout¶
An orphan branch, never deployed by the workflows that write it: coverage/ and smoke/ (the
app's CI and docker-smoke.yml), stats/ (the umbrella). Each writer pushes with
scripts/site-data-push.sh (retries on a rejected push, replaying its one commit onto the new
tip) and then sends repository_dispatch: site-data-updated so site.yml refreshes the panels
without waiting for its schedule. A directory missing from the branch just means no panel for it;
a missing branch means none of the three.
Health checks¶
site-health.yml runs python3 scripts/site_live_check.py against the live site every six hours,
separate from site.yml on purpose: a red run here means upstream data is missing (a bulletin
gone stale, a provider down), not that the build is broken, so it must never block a deploy.
i18n bundling¶
site/i18n/*.json (pt-BR is the source locale, en is translated, x-pseudo is generated,
never translated) are checked and bundled into site/static/i18n.js by scripts/i18n_bundle.py
(MIP-0054 §5.8): every locale has exactly pt-BR's keys with no empty value, every message parses
in the ICU subset ui.js implements and names the same arguments as pt-BR's, and every key has a
note in context.json. Edit the catalogs, never i18n.js; --check fails a stale committed
bundle.
The MIP: trailer¶
A commit touching site/static/** carries MIP: MIP-NNNN or MIP: none — <reason>.
scripts/mip-trailer-check.sh enforces it; just prepush runs it over the commits about to be
pushed.