|
All checks were successful
PR build (required check) / changes (pull_request) Successful in 9s
PR build (required check) / build-backend (pull_request) Has been skipped
PR build (required check) / build-frontend (pull_request) Has been skipped
secrets-guard / encrypted (pull_request) Successful in 7s
shell-lint / shellcheck (pull_request) Successful in 9s
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / gate (pull_request) Successful in 2s
The six deploy workflows were one file written six times, differing only in a branch name, a paths filter, a concurrency group, the service name, its *_IMAGE_TAG variable and a secret prefix. The two build-push workflows were the same file twice, differing only in the domain string. The contract into infra/deploy/deploy.sh (SERVICE + BACKEND_IMAGE_TAG/FRONTEND_IMAGE_TAG) was already fully parameterised, so the duplication bought nothing and cost eight files to keep in step. deploy.yml: branch selects the environment (main -> beta, release -> prod), a matrix covers backend and frontend, and each leg decides whether this push actually touched its domain before rolling anything. The workflow-level paths filter only says backend OR frontend moved; without the per-leg refinement a backend-only push would also roll the frontend and lose the independent-deploy property the FE/BE split exists for. build-push.yml: the same shape for images. Images stay separate and independently deployable; nothing about the published artefacts changes. The two *-deploy-dev.yml workflows are deleted rather than ported. They were already documented as inert -- they call a monorepo path on the LAN box whose ~/thermograph-dev is still a split-era thermograph-infra checkout. LAN dev is a local `make dev-up` concern, not a CI environment. Deliberately boring expressions throughout. No dynamic matrix (fromJSON), and the *_IMAGE_TAG selection is done in shell rather than with a `matrix.service == 'x' && a || b` ternary. Those are GitHub idioms a Forgejo/act runner may evaluate differently, and the failure mode is silent: an empty *_IMAGE_TAG makes deploy.sh fall back to the tag already running, so the job goes green having deployed nothing. Beta and prod get two explicit, mutually exclusive steps rather than a ternary over secrets, where an empty host would be worse still. Everything load-bearing is preserved: fetch-depth 0 and the domain-keyed 12-hex tag (the branch tip is often another domain's commit), per-service per-ref concurrency with cancel-in-progress false, separate PROD_SSH_* credentials, the v*.*.* both-images exception, and appleboy/ssh-action by full URL. pr-build.yml is untouched. Its workflow name and `gate` job are the required status check branch protection is configured against, and renaming that context makes every PR unmergeable with fully green CI. secrets-guard.yml and shell-lint.yml are likewise left alone: they are the only other consolidation candidates that run on pull_request, and the branch-protection API needs credentials this could not read, so merging them was not worth the risk for two files. Logic verified by replaying the plan step against real history: an infra-only push rolls nothing, a backend-only push rolls backend and leaves frontend alone, and a run with no usable before-sha defaults to deploying rather than silently skipping. The computed frontend tag for a backend-only push is sha-ca84e0ce95f0 -- exactly the tag live on beta -- so the derivation matches what the old workflows produced. 15 workflows -> 9, 1365 lines -> 1019. Docs naming the deleted files are updated in the same commit, per the rule the root CLAUDE.md now carries. |
||
|---|---|---|
| .. | ||
| .claude/skills/key-gaps | ||
| deploy | ||
| lake-iceberg | ||
| ops | ||
| terraform | ||
| .env.example | ||
| .gitignore | ||
| .sops.yaml | ||
| ACCESS.md | ||
| CLAUDE.md | ||
| DEPLOY-DEV.md | ||
| DEPLOY.md | ||
| docker-compose.dev.yml | ||
| docker-compose.openmeteo.yml | ||
| docker-compose.yml | ||
| Makefile | ||
| README.md | ||
infra/
Infrastructure for Thermograph: Terraform host
provisioning, the SOPS+age secrets vault, WireGuard/Swarm networking, Forgejo,
Caddy, mail, and the deploy scripts that run the already-built app images on each
host. This is a domain of the emi/thermograph monorepo — hosts' /opt/thermograph
is a checkout of the whole monorepo, and infra/ never builds app source; it only
runs published images.
terraform/— provisions/configures hosts (SSH-driven by default; an optional GCP-creating module is scaffolded, no live resources yet). Seeterraform/README.md. No tfstate is persisted anywhere — treatapplyas executable documentation, not a routine operation.deploy/secrets/— the git-native SOPS+age secrets vault (every app secret, encrypted at rest, rendered at deploy time). Seedeploy/secrets/README.md.deploy/swarm/,deploy/forgejo/— the WireGuard/Swarm cluster hosting Forgejo (git + CI + registry). SeeACCESS.md.deploy/deploy.sh— the single deploy entry point for beta and prod. TakesSERVICE=backend|frontend|allplusBACKEND_IMAGE_TAG/FRONTEND_IMAGE_TAG, resets the host checkout, renders secrets, and routes to the right orchestrator.deploy/stack/— the Swarm path, live on prod:thermograph-stack.yml(db, web, worker, lake, daemon, frontend, autoscaler, autoscaler-lake),deploy-stack.sh,autoscale.sh, and the LB. Rolling updates are start-first, health-gated, with auto-rollback.STACK_TEST=1rehearses the whole stack on throwaway volumes and ports.docker-compose*.yml— the compose path, live on beta and LAN dev (db, backend, lake, daemon, frontend).docker-compose.dev.ymlis the LAN overlay;docker-compose.openmeteo.ymlis the self-hosted Open-Meteo overlay.
Which path a host takes is decided by /etc/thermograph/deploy-mode: the string
stack makes deploy.sh exec deploy/stack/deploy-stack.sh; anything else is
compose. The workflows never need to know which mode a host runs.
Branches & how changes reach each environment
main— what prod and beta run.infra-sync.ymlfires on a push tomaintouchinginfra/**, fast-forwards each host's/opt/thermographcheckout and re-renders/etc/thermograph.envfrom the vault. It deliberately does not roll any service: image tags are the app domains' axis, not infra's. A compose or stack change that must recreate containers takes effect on the next app deploy, or a by-handSERVICE=all … deploy/deploy.sh.dev— what LAN dev would run viadeploy/deploy-dev.sh. The CI trigger for this is currently inert (the LAN box still holds a split-era checkout); usemake dev-uplocally.release— consumed by app deploys only. Both hosts track infra viamain; prod's app images are staged byrelease, but its checkout followsmain.
Note the asymmetry with the app domains: app code IS environment-staged
(dev→main→release maps to LAN→beta→prod via image tags); infra is not.