8.1 KiB
8.1 KiB
Monorepo cutover notes (assembled 2026-07-22)
Scratch assembly for inspection — nothing live points here yet. Built by
subtree-merging each split repo's origin/main (full history, 380 commits):
backend/← thermograph-backend83c2e05(Discord gateway bot landed, #7)frontend/← thermograph-frontend3a98146(responsiveness hardening, #4)infra/← thermograph-infra99b4b3f(mail docs, #12)observability/← thermograph-observability19e74af(validator CI, #3)
What changed vs. the split repos
CI (all in root .forgejo/workflows/; the subtree copies were deleted —
Forgejo only reads root workflows, but dead copies are a trap):
backend-build-push.yml/frontend-build-push.yml— the old per-repo build-push, path-filtered (paths: ['backend/**']etc.), image paths now explicit:emi/thermograph/backend|frontend(the old${{ github.repository }}/appderivation collides in a monorepo). Per-domain concurrency groups. Caveat:v*.*.*tag pushes carry no paths context, so a version tag builds both images (intended: a release names the set).backend|frontend-deploy[.yml|-prod.yml|-dev.yml]— ports of the SSH deploy workflows, path-filtered, script path now/opt/thermograph/infra/deploy/deploy.sh. Same secret names (SSH_*beta,PROD_SSH_*prod) — but now one secret set on one repo.build.yml— the build-only sanity check, now a single reusable workflow taking adomaininput (context = the domain dir, so Dockerfiles see the same tree as in the split era).pr-build.yml— onechangesjob (git-diff domain detection) + per-domain gated builds + a single always-reportinggaterequired check. Deliberately not path-filtered: a required check that never fires blocks auto-merge forever.infra-sync.yml— new (the infra domain's own pipeline): push tomaintouchinginfra/**→ git-sync/opt/thermographon beta+prod and re-render secrets. Rolls no services (image tags are the app domains' axis).observability-validate.yml— port withobservability/prefixes +workflow_callso pr-build reuses it.secrets-guard.yml— glob is nowinfra/deploy/secrets/*.yaml; still runs on everything (cheap backstop).ops-cron.yml— compose invocationscd /opt/thermograph/infra.
Deploy scripts / compose (hosts see a monorepo checkout at
/opt/thermograph):
deploy.sh:INFRA_DIR="$APP_DIR/infra"; git ops at the checkout root,cd "$INFRA_DIR"for all compose work; lock/tags/render paths underinfra/deploy/; image-path defaults renamed.docker-compose.yml:name: thermographpinned. Critical: without it, running compose from…/infraderives project "infra" — a silently NEW project that would recreate the whole stack beside the running one.deploy-dev.shexportsCOMPOSE_PROJECT_NAME=thermograph-dev(env wins over the file key), preserving LAN dev's separate project.deploy/stack/deploy-stack.sh+thermograph-stack.yml: same path/image renames (prod is stack-mode;docker stack deploytakes an explicit stack name, so no project-name concern there).
Verified here: every workflow + compose file parses (PyYAML); all three
deploy scripts pass bash -n; no emi/thermograph-{backend,frontend}/app
stragglers in sh/yml. Not verified (impossible locally): Forgejo actually
honoring on.push.paths and workflow_call inputs on our version; any live
deploy.
Before cutover (in order)
- Run the pending prod network-recreate runbook first. Don't rebase the deploy path on a prod that wedges on its next deploy.
- Prove Forgejo path filters + reusable-workflow inputs with a throwaway
repo: one push touching only
backend/**must fire only backend workflows; auses: ./.forgejo/workflows/build.yml+with:job must pass its input. If paths aren't honored, fall back to the pr-build pattern (diff job + gated jobs) for the push workflows too. - Branch drift: the split repos' branch state as of 2026-07-22 evening is
fully migrated (see "Branch migration status" below) — re-run the check
(
git merge-tree --write-treeper branch vs dev/main) if cutover slips and upstream moves again.
Branch migration status (2026-07-22)
Checked every split-repo branch by dry-run merge; migrated everything real:
dev(this repo) =main+ backend/frontendorigin/devsubtrees: the in-image test CI feature (ported into rootbuild.yml— backend full suite, frontend unit tier; their subtreebuild.ymledits resolved as deleted) + backend test scripts/Makefile/compose.test + frontend two-tier suite with fixtures. Faithful to upstream: the feature is dev-only, so monomaindoesn't have it; promote with dev→main like the split flow.feature/geocode-local→feature/history-meteostat-gusts→feature/nasa-primary-flip→feature/era5-seed— the unmerged backend stack, rebuilt stacked on monodev. ⚠️ Two conflicts were resolved here that upstream has NOT resolved (the same conflicts exist merging those branches into split-repo dev):data/climate.py(dev's hardened_requestsignature combined with the gust-fill wrap) andtests/data/test_climate.py(kept both appended test blocks). Unverified — tests weren't runnable locally (Docker Hub blocked); run the in-image suite on these branches before trusting them.- Stale (absorbed upstream, nothing migrated — safe to delete in the split
repos): backend
ci-run-tests-in-image,perf/*,phase5/discord-gateway-bot,port-discord-gateway-bot,reconcile-dev-with-main,test-runner-and-smoke; frontendci-run-tests-in-image,reconcile-dev-with-main; all three infra branches (ci-fix/render-secrets-owner,phase5/discord-bot-flag,terraform-two-image-tags— all landed in inframain); both observability dashboard branches. - Dismissed, not stale-but-not-migrated: frontend
perf/frontend-responsiveness(its "new" content would resurrect test files dev reorganized into the unit tier) andtest-real-backend-harness(an older revision of dev'stests/unit/test_rendering.py).
Cutover runbook
- Create
emi/thermographon Forgejo (a new repo — do not resurrect the archived monorepo) and pushmain. - Actions config, once: secrets
REGISTRY_TOKEN,SSH_HOST/USER/KEY/PORT(beta),PROD_SSH_*(prod); variableREGISTRY_HOST=git.thermograph.org. - Branch protection on
dev+main: required checkgate, squash merges, auto-merge on green. - Hosts (beta, then prod): clone the monorepo to
/opt/thermograph.new; copydeploy/.image-tags.envfrom the old checkout intoinfra/deploy/(its path moved — losing it makes the next single-service deploy roll the sibling ontolocal); swap directories (running containers are untouched — they're images, not the checkout);/etc/thermograph*files stay as-is. - LAN box: re-point
~/thermograph-devat the monorepo the same way. - First push builds the new image paths — old
emi/thermograph-{backend,frontend}/apppackages stay in the registry for rollback; GC after burn-in. deploy.sh's tag prune already targets the new paths only. - One
workflow_dispatchofbackend-deploy.ymlafter its build-push completes = the live end-to-end validation, same as the split-era wiring test. - After burn-in: archive the four split repos (leave
thermograph-docsalone), update its runbooks to the monorepo paths.
Known gaps / deferred
- Discord/notifier extraction (phase 2): discord still ships inside the
backend image (
backend/notifications/); a discord tweak = a backend deploy. Truenotifier/domain (own image + compose service, singleton lock retired) is a refactor for after the monorepo settles. - Domain
CLAUDE.mds still speak repo-era ("this repo", sibling-repo links) — correct them as touched; rootCLAUDE.mdpoints the right way. backend/MONOREPO-CUTOVER-PLAN.mdreferences (the split plan) are now historical.- Infra
Makefilelocal-image targets still assume sibling checkouts — update when first needed locally.