Skip to content

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.