promote: main → release #132
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Jinemi/thermograph#132
Loading…
Reference in a new issue
No description provided.
Delete branch "main"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Promotes
main→release, deploying to prod (thermograph.org).Who merges. The owner. This hop deploys to prod and the decision is theirs; Centralis prepared it and stops here.
Divergence. 47 ahead, 17 behind. 47 commit(s) to carry, but the target has moved on by 17 — a merge commit is required and conflicts are possible
Not fast-forwardable: the target holds 17 commit(s) the source does not. That is the accumulated design of this release chain, not a mistake anyone just made — every promotion merges the source into the target, which creates a merge commit ON THE TARGET that the source never receives, so each promotion widens the gap it just closed. Expect
--ff-onlyto fail with "diverging branches"; the promotion is a merge commit.Predicted conflicts.
infra/deploy/Caddyfile— modify/delete, git will stop here.claude/hooks/README.md— edited on both sides.claude/hooks/prod-guard.sh— edited on both sides.forgejo/workflows/deploy.yml— edited on both sides.forgejo/workflows/observability-validate.yml— edited on both sides.forgejo/workflows/ops-cron.yml— edited on both sidesCLAUDE.md— edited on both sidesCUTOVER-NOTES.md— edited on both sidesREADME.md— edited on both sidesbackend/web/app.py— edited on both sidesdocs/onboarding/01-orientation.md— edited on both sidesdocs/onboarding/02-setup.md— edited on both sidesdocs/onboarding/03-repo-map.md— edited on both sidesdocs/onboarding/04-backend.md— edited on both sidesdocs/onboarding/05-frontend.md— edited on both sidesdocs/onboarding/07-ci-and-release.md— edited on both sidesdocs/onboarding/08-infra-secrets.md— edited on both sidesdocs/onboarding/09-observability.md— edited on both sidesdocs/onboarding/10-recipes.md— edited on both sidesdocs/onboarding/11-traps.md— edited on both sidesdocs/onboarding/README.md— edited on both sidesfrontend/server/internal/render/templates/home.html.tmpl— edited on both sidesfrontend/static/app.js— edited on both sidesinfra/.claude/skills/key-gaps/SKILL.md— edited on both sidesinfra/.env.example— edited on both sidesinfra/CLAUDE.md— edited on both sidesinfra/DEPLOY-DEV.md— edited on both sidesinfra/DEPLOY.md— edited on both sidesinfra/README.md— edited on both sidesinfra/deploy/backup/README.md— edited on both sidesinfra/deploy/deploy-dev.sh— edited on both sidesinfra/deploy/deploy.sh— edited on both sidesinfra/deploy/forgejo/README.md— edited on both sidesinfra/deploy/forgejo/docker-stack.yml— edited on both sidesinfra/deploy/forgejo/register-lan-runner.sh— edited on both sidesinfra/deploy/provision-mail.sh— edited on both sidesinfra/deploy/render-secrets.sh— edited on both sidesinfra/deploy/secrets/README.md— edited on both sidesinfra/deploy/secrets/beta.yaml— edited on both sidesinfra/deploy/secrets/dev.yaml— edited on both sidesinfra/deploy/stack/deploy-stack.sh— edited on both sidesinfra/deploy/thermograph.env.example— edited on both sidesinfra/docker-compose.dev.yml— edited on both sidesinfra/ops/ICEBERG-HANDOFF.md— edited on both sidesinfra/ops/README.md— edited on both sidesinfra/ops/dbq.sh— edited on both sidesinfra/ops/iceberg.sh— edited on both sidesobservability/.env.example— edited on both sidesobservability/CLAUDE.md— edited on both sidesobservability/README.md— edited on both sidesobservability/alloy/config.alloy— edited on both sidesobservability/docker-compose.yml— edited on both sidesobservability/grafana/provisioning/alerting/contact-points.yml— edited on both sidesobservability/grafana/provisioning/alerting/rules-prod.yml— edited on both sidesobservability/loki/config.yml— edited on both sidesCommits (47).
dbc79351Merge pull request 'promote: dev → main' (#131) from dev into main2776a6cdMerge pull request 'openbao: add a dormant OpenBao backend alongside SOPS' (#130) from openbao-backend into dev1941b184Merge dev into openbao branch (dev moved when #128 landed)8a2c8386forgejo: restart the db service on a clean exit, not just on failure7878763bforgejo: restart the db service on a clean exit, not just on failure6bd07d33openbao: add a dormant OpenBao backend alongside SOPS94de933cMerge pull request 'promote: dev → main (sign in with Google; shared OAuth engine)' (#127) from dev into maindeb039eeaccounts: sign in with Google, on a shared provider-agnostic OAuth engine (#122)1d4defefBRANCHING: the escape hatch, now that apply_to_admins binds the owner too890cb4a7docs: the orchestrator runbook and the module mapad4d2da1promote: dev → main0617a3b0db: raise max_connections to 200, cap work_mem at 64MB, trim async pool overflow (#124)beb5fa52bookmarks: don't fabricate a local save when the server rejects it (#121)2a4f5167frontend: load bookmarks-ui.js from the Go SSR home template too (#120)3ec72ca7bookmarks: don't fabricate a local save when the server rejects it (#121)b57e280afrontend: load bookmarks-ui.js from the Go SSR home template too (#120)4f21e477Merge pull request 'promote: dev → main (location bookmarks)' (#119) from dev into main1c6ecc2abookmarks: map + account UI for saved locations17cc14b4bookmarks: map + account UI for saved locationsd6ead569bookmarks: map + account UI for saved locationsMerge with a merge commit, not a squash or a fast-forward.
releaseruns ahead ofmainfrom earlier promotions, so--ff-onlyfails with "diverging branches", and squashing would rewrite the shared history so the next promotion sees every prior commit as new. (Making promotions fast-forward-only is an open question, not current policy — it needs a one-time history reconciliation on the protected branches first.)forgejo_db has been at 0/1 replicas since 2026-07-29 01:24 UTC. Postgres hit an invalid data-directory lock file ("could not open file postmaster.pid ... performing immediate shutdown because data directory lock file is invalid") and exited 0. With restart_policy.condition=on-failure, Swarm read the zero status as successful completion, marked the task Complete, and never rescheduled it. The forgejo service itself stayed Up and kept serving its homepage, so the outage presented as every repository page, the whole API and all CI returning 500 with "dial tcp: lookup db on 127.0.0.11:53: no such host" — including the auth path, which is why API calls reported "user does not exist [uid: 0]" rather than a database error. on-failure cannot distinguish "finished successfully" from "shut itself down and should be restarted", and Postgres exits 0 on several such paths, so it is the wrong policy for an always-on stateful service. This is the durable fix; it does not restart the currently stopped task.forgejo_db has been at 0/1 replicas since 2026-07-29 01:24 UTC. Postgres hit an invalid data-directory lock file ("could not open file postmaster.pid ... performing immediate shutdown because data directory lock file is invalid") and exited 0. With restart_policy.condition=on-failure, Swarm read the zero status as successful completion, marked the task Complete, and never rescheduled it. The forgejo service itself stayed Up and kept serving its homepage, so the outage presented as every repository page, the whole API and all CI returning 500 with "dial tcp: lookup db on 127.0.0.11:53: no such host" — including the auth path, which is why API calls reported "user does not exist [uid: 0]" rather than a database error. on-failure cannot distinguish "finished successfully" from "shut itself down and should be restarted", and Postgres exits 0 on several such paths, so it is the wrong policy for an always-on stateful service. This is the durable fix; it does not restart the currently stopped task.Centralis could not migrate at all. `seed-from-sops.sh --all` refused centralis.prod with `CENTRALIS_TOKENS: contains a shell metacharacter`, and render-secrets-openbao.sh had no Centralis path whatsoever — so the seeded data would have had nothing to read it. Both halves come from applying the app stack's rules to a file that does not share its shape. /etc/thermograph.env is raw unquoted KEY=value. /etc/centralis.env is consumed by exactly one thing and that thing is a shell: sudo bash -c 'set -a && . /etc/centralis.env && set +a && docker compose up' CENTRALIS_TOKENS is JSON, so it can never satisfy a no-metacharacters rule, and rendering it unquoted is precisely the 2026-07-24 failure: bash strips the quotes, Centralis fails closed on the malformed registry and serves a single `shared` identity, indistinguishable from the file never being written. So: - seed-from-sops.sh validates against the renderer that will consume the path. The dotenv rules still apply to common and env/*; centralis/* is exempt because it is single-quoted downstream. - render-secrets-openbao.sh gains thermograph_render_openbao_centralis, mirroring render_centralis_secrets: fetch as JSON, emit POSIX single-quoted assignments, then PROVE the file by sourcing it under `env -i` with `set -a` and comparing every value back before anything touches the destination. Mirrored rather than shared, per the existing note above thermograph_render_openbao — consolidate when the SOPS path is deleted. - render_centralis_secrets gains the same early-return backend dispatch the app path already uses, so the new function is reachable. Without it the SOPS path was the only path regardless of TG_SECRETS_BACKEND. Verified against a synthetic registry containing a JSON token map, an apostrophe, and `$(...)`/backtick/backslash/double-quote payloads: 5 keys round-trip byte-for-byte through `set -a; . file`, CENTRALIS_TOKENS parses to the right subjects, and the command substitution does not execute. The negative case is covered too — fed the unquoted 2026-07-24 shape, the round-trip check reports the value as changed (by length, never content) and refuses to write. Also corrects the claim in render-secrets-openbao.sh that the SOPS path never tripped the metacharacter hazard "because every current value happens to be alphanumeric". CENTRALIS_TOKENS is the counter-example; it never tripped those paths because it was never in them.