Promote to production #77
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?
Promotion: staging passed. Run
scripts/plan-prod.shlocally before mergingMerging this PR is the approval record; it deploys nothing. Then check out the validated commit and run
scripts/promote-prod.sh(orscripts/deploy-services.sh --tags ... --limit servicesfor an Ansible-only change); both refuse any other commit.Staging validation: passed on commit
fd26dc385e(this run)Production drift check: clean
Ansible preview (staging clone, synthetic secrets — approximate, not exact prod values)
GitOps plan phase 1, steps 1a-1d. Real credentials CI already has for staging (LINODE_TOKEN, CLOUDFLARE_API_TOKEN) extend cleanly to owning the Terraform layer end-to-end -- no invariant conflict, unlike the Ansible layer (see roles/gitops_watcher, phase 4, not yet built). New: .forgejo/workflows/terraform-plan.yml (1a) -- posts `tofu plan` against the pinned `linode` workspace as a PR comment on any PR touching terraform/**. Read-only, never applies. Explicit workspace select always -- never the orphaned "default" workspace (docs/incidents.md: ungated by local.skip_dns, ~62 unrelated resources). New: .forgejo/workflows/drift-check.yml (1d) -- scheduled (6h) + workflow_dispatch `tofu plan -refresh-only -detailed-exitcode` across linode/ovh/ovh-dr (matrix), opens a Forgejo issue on drift. This is the direct, automatic fix for the incident that prompted this whole plan: Terraform declared cloudflare_record.proxied=true for vpn.jfraeys.com, live had drifted to false. -refresh-only already does this comparison correctly; the gap was purely "nobody runs it on a schedule." Modified: .forgejo/workflows/staging-validate.yml -- added a `promote` job (needs: validate) implementing 1b (promotion gate) and setting up 1c's trigger: - Pre-promotion production drift check: same -refresh-only mechanism as drift-check.yml, but run synchronously at promotion time -- answers "is prod already wrong, don't build on top of it" rather than only "did something change since the last deploy" (drift-check still runs on its own schedule too, for drift introduced *between* promotions). - On clean drift check: opens/updates a main->prod PR with the staging summary, drift-check result, and a synthetic-secrets Ansible preview attached. Merging that PR is the entire production approval event -- no separate approve command. - Removed the old "Run locally to deploy: ./setup --confirm-prod" message -- this replaces it. - Implemented as a same-workflow `needs:` job dependency rather than a separate promote-to-prod.yml relying on workflow_run/workflow_call, since Forgejo Actions' support for cross-workflow triggers wasn't confirmed -- needs: is unambiguously supported and guarantees ordering either way. Flagged as an open question in the plan; resolved this way during implementation. New: .forgejo/workflows/terraform-apply.yml (1c) -- applies Terraform automatically on push to `prod` (i.e. exactly when the promotion PR above gets merged). Deliberately does not touch Ansible-managed config -- roles/gitops_watcher (phase 4) owns that with real secrets this CI job never has. Includes a placeholder step for signaling gitops_watcher once that role exists. All four files' YAML validated with pyyaml, embedded bash blocks synax- checked with `bash -n`, embedded Python snippets compile-checked -- no live Forgejo Actions runner available in this environment to test an actual run.BSD mktemp doesn't substitute XXXXXX when a suffix follows, so it created a literal staging_tailscale_key.XXXXXX.err and every later run failed with an empty path ("cat: : No such file"), aborting the ephemeral Tailscale key mint. Drop the .log/.err suffixes in all seven templates. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>terraform-plan.yml assumed the runner baseline already had the S3 backend settings and said to confirm on first real run; run 254 failed at tofu init ("bucket must not be empty", "Missing region"). Declare the bucket/region/ endpoint and the OBJECT_STORAGE_* repo secrets at workflow level in terraform-plan, terraform-apply, staging-validate and staging-reboot-check, matching drift-check. The two repo secrets must exist for this to take effect. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>The runner's ansible-core 2.14 cannot parse a quoted multi-line -a string ("Unable to parse argument string"), so the failure dump in run 479 never printed. Use ansible.builtin.script with a temp file instead. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>cAdvisor used ~93 MiB at defaults. Only container_last_seen{name=...} is consumed, so: docker-only, 30s housekeeping, no container labels/events, cpu/memory/network/oom metrics. Staging now queries Prometheus for container_last_seen{name="postfix-postfix-1"}. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>