Migrate climatemappedafrica builds to Docker Bake - #1510
Open
kelvinkipruto wants to merge 4 commits into
Open
Conversation
packages/commons-ui-payload is pinned to Payload v3 (catalog:payload-v3)
for trustlab's benefit. Its barrel export (src/index.js) re-exports
./fields, which imports the v3-only subpath `payload/shared`. Any
Payload v2 app importing anything from the barrel — even just
`{ RichText }` — forces webpack to resolve the whole barrel, including
the broken v3-only fields module, and fails with "Module not found:
Can't resolve 'payload/shared'".
Add a dedicated `./RichText` export path that bypasses the `fields`
module entirely, and switch every Payload v2 consumer (climatemappedafrica
and the shared hurumap-next package) to import from it directly instead
of the barrel. trustlab's LexicalRichText usage is untouched — it's on
Payload v3, so the barrel already resolves fine there.
civicsignalblog has the same broken import in 3 files but is out of
scope here; flagged separately.
@payloadcms/bundler-webpack peer-requires ajv, which nothing else in climatemappedafrica's dependency tree pulls in directly — same class of issue already fixed for codeforafrica/charterafrica in #1509. Needed for `payload build` to resolve it when installed in isolation (both the legacy Dockerfile's per-app install and the new turbo-pruned Bake build install climatemappedafrica separately from its siblings).
Next.js requires getStaticProps' return value to be JSON-serializable —
`undefined` is rejected outright ("Reason: undefined cannot be
serialized as JSON"). getFooter/getNavBar destructured several fields
(description, title, connect, newsletter, analytics, tutorialEnabled)
directly off Payload's Settings global with no fallback, so an
unconfigured field crashed the build rather than just rendering empty.
Also rewrite imageFromMedia to return null outright when no media is
provided, instead of an object with every field undefined — the
existing Footer/Navigation components already guard for a falsy
logo/drawerLogo (`{logoProps && (...)}`, `{...logo}`), so this is a
no-op for consumers and matches the "no image configured" case more
correctly than piecemeal per-field fallbacks.
Surfaced while validating the Docker Bake migration against an
unseeded local database — any of these fields being empty in a real,
lightly-configured Settings global would hit the exact same crash.
climatemappedafrica was still built from the legacy shared root Dockerfile via a single docker/build-push-action workflow with no version-check gate. Move it onto the same per-app Dockerfile + Docker Bake architecture as pesayetu/roboshield/techlabblog/trustlab. Standardize on DATABASE_URL for the Payload DB connection instead of MONGO_URL, matching roboshield/trustlab, across the app code, .env files, and the whole build pipeline — no MONGO_URL left anywhere in the new path. ### What changed - Add a pruned, multi-stage Dockerfile at docker/apps/climatemappedafrica/Dockerfile. Unlike the other migrated apps, this one doesn't use Next.js `output: "standalone"`, so the runner stage copies the full node_modules and .next folder rather than a pruned standalone bundle — matching the legacy Dockerfile's own approach, just via turbo prune instead of the base-deps/app-deps split. The custom server (server.ts, tsc-compiled to dist/server.js) connects to MongoDB via payload.init() before spawning `next build`, so this build genuinely needs a reachable database, not just a well-formed URL. - Add the climatemappedafrica target to docker-bake.hcl and the shared apps group, inheriting _payload-app-runner directly (no app-specific secrets needed beyond that and next_public app URL/project root args). - Reuse the published ui-builder-base/ui-runner-base images in CI. - Package the full app, plus Dokku health-check metadata. - Switch local `make climatemappedafrica` and Compose usage to the Bake-built image. - Mark climatemappedafrica migrated in docker/README.md. - Leave the legacy root Dockerfile stage and climatemappedafrica-deploy-dev.yml workflow untouched for now — same rollback-safety precedent as the other migrations, to be disabled once this pipeline is validated in production. ### CI/CD - Add a reusable Bake workflow (_build-climatemappedafrica.yml) and a new orchestration workflow (climatemappedafrica.yml): build + deploy to DEV on every push, no version-check gate — there's no PROD Dokku app for this one yet, matching the legacy workflow it replaces. - Add climatemappedafrica to the pr-build.yml image-validation workflow and scripts/pr-build-targets.mjs's target detection. PR builds use the real CLIMATEMAPPEDAFRICA_MONGO_URL secret (mapped to DATABASE_URL) rather than a dummy value, since the build needs a reachable database to succeed. - No new GitHub secrets required; existing CLIMATEMAPPEDAFRICA_MONGO_URL/ CLIMATEMAPPEDAFRICA_PAYLOAD_SECRET carry over, just remapped to DATABASE_URL/PAYLOAD_SECRET build secrets. ### Validation - `docker buildx bake climatemappedafrica` against a real local MongoDB instance — full turbo-prune -> install -> next build (compile, lint, static export of all 3 pages) -> payload build all pass. - `docker run` smoke test — the built image boots, Payload connects, Next.js starts, and `/` returns HTTP 200. - `pnpm test:scripts` passes (10/10), including the updated pr-build-targets detection tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Migrate
climatemappedafricafrom the legacy shared rootDockerfileto the repository's per-app Dockerfile and Docker Bake build architecture, and fix three pre-existing bugs uncovered along the way that were silently blocking its build.Fixes the
Climate Mapped Africa | Deploy | DEVworkflow.Why
climatemappedafrica's deploy workflow has been failing for a while, most recently on missing@types/expressandajvdependencies (partially addressed in #1509). Rather than keep patching the legacy sharedDockerfile's two-stepbase-deps/app-depsinstall — which has repeatedly lost dependency resolution for things like peer-injected packages — this moves the app onto the same turbo-prune-based Bake architecture already proven forpesayetu/roboshield/techlabblog/trustlab.Validating the migration against a real database (rather than assuming success) surfaced three additional, genuinely pre-existing bugs that would have blocked this build under any tooling:
@commons-ui/payload's barrel export is Payload-v3-only. The package is pinned tocatalog:payload-v3(fortrustlab), and its barrel (src/index.js) re-exports./fields, which imports the v3-only subpathpayload/shared. Any Payload v2 app importing anything from the barrel — even just{ RichText }— forces webpack to resolve the whole barrel and fails withModule not found: Can't resolve 'payload/shared'. This affectedclimatemappedafricadirectly and the sharedhurumap-nextpackage (whichclimatemappedafricatransitively depends on).getFooter/getNavBarassumed the Settings global is always fully populated. They destructured several fields (description,title,connect,newsletter,analytics,tutorialEnabled) with no fallback. Next.js'sgetStaticPropsrejectsundefinedoutright ("cannot be serialized as JSON"), so any of these being unset in a lightly-configured Settings global crashes the build, not just renders empty.imageFromMediadidn't handle a missing image. It readmedia.altunconditionally — if no logo were configured, this threwCannot read properties of undefined (reading 'alt')rather than the intended "no image" state.None of these three are related to Docker or Bake — the legacy Dockerfile would hit the exact same failures once
ajv/@types/expresswere fixed.What changed
Pre-existing bug fixes
./RichTextexport tocommons-ui-payload'spackage.jsonthat bypasses the brokenfieldsbarrel, and switch every Payload v2 consumer (climatemappedafrica,hurumap-next) to import from it directly.trustlab'sLexicalRichTextusage is untouched — it's on Payload v3, so the barrel already resolves fine there.civicsignalbloghas the same broken import in 3 files but is out of scope here (flagged separately).ajvdevDependency toclimatemappedafrica(same class of fix as Fix legacy Payload build failures for climatemappedafrica/codeforafrica/charterafrica #1509's forcodeforafrica/charterafrica).getFooter/getNavBarwith?? null, and rewriteimageFromMediato returnnulloutright when no media is provided — the existingFooter/Navigationcomponents already guard for a falsy logo ({logoProps && (...)},{...logo}), so this is a no-op for working cases.climatemappedafrica Docker Bake migration
docker/apps/climatemappedafrica/Dockerfile. Unlike the other migrated apps, this one doesn't use Next.jsoutput: "standalone", so the runner stage copies the fullnode_modulesand.nextfolder rather than a pruned standalone bundle — matching the legacy Dockerfile's own approach, just viaturbo pruneinstead of thebase-deps/app-depssplit. The custom server (server.ts,tsc-compiled todist/server.js) connects to MongoDB viapayload.init()before spawningnext build, so this build genuinely needs a reachable database, not just a well-formed URL.climatemappedafricatarget todocker-bake.hcland the sharedappsgroup, inheriting_payload-app-runnerdirectly.DATABASE_URLfor the Payload DB connection instead ofMONGO_URL, matchingroboshield/trustlab, across the app code,.envfiles, and the whole build pipeline.ui-builder-base/ui-runner-baseimages in CI.make climatemappedafricaand Compose usage to the Bake-built image.climatemappedafricamigrated indocker/README.md.Dockerfilestage andclimatemappedafrica-deploy-dev.ymlworkflow untouched for now, matching the rollback-safety precedent from the other migrations — to be disabled once this pipeline is validated in production.CI/CD
_build-climatemappedafrica.yml) and a new orchestration workflow (climatemappedafrica.yml): build + deploy to DEV on every push, no version-check gate — there's no PROD Dokku app for this one yet, matching the legacy workflow it replaces.climatemappedafricato thepr-build.ymlimage-validation workflow andscripts/pr-build-targets.mjs's target detection. PR builds use the realCLIMATEMAPPEDAFRICA_MONGO_URLsecret (mapped toDATABASE_URL) rather than a dummy value, since the build needs a reachable database to succeed.CLIMATEMAPPEDAFRICA_MONGO_URL/CLIMATEMAPPEDAFRICA_PAYLOAD_SECRETcarry over, just remapped toDATABASE_URL/PAYLOAD_SECRETbuild secrets.Validation
docker buildx bake climatemappedafricaagainst a real local MongoDB instance — fullturbo prune→ install →next build(compile, lint, static export of all 3 pages) →payload buildall pass.docker runsmoke test — the built image boots, Payload connects, Next.js starts, and/returns HTTP 200.pnpm test:scripts— 10/10 pass, including the updatedpr-build-targetsdetection tests.Type of change
Screenshots
N/A
Checklist