Promote to production #145

Merged
jfraeys merged 17 commits from main into prod 2026-10-14 11:55:48 -04:00
Owner

Promotion: staging passed. Run scripts/plan-prod.sh locally before merging

Merging this PR is the approval record; it deploys nothing. Then check out the validated commit and run scripts/promote-prod.sh (or scripts/deploy-services.sh --tags ... --limit services for an Ansible-only change); both refuse any other commit.

Staging validation: passed on commit 5b023cc9ef (this run)
Production drift check: clean

Ansible preview (staging clone, synthetic secrets — approximate, not exact prod values)


PLAY [Deploy the Docker-based services stack] **********************************
skipping: no hosts matched

PLAY RECAP *********************************************************************
### Promotion: staging passed. Run `scripts/plan-prod.sh` locally before merging Merging this PR is the approval record; it deploys nothing. Then check out the validated commit and run `scripts/promote-prod.sh` (or `scripts/deploy-services.sh --tags ... --limit services` for an Ansible-only change); both refuse any other commit. **Staging validation:** passed on commit 5b023cc9ef6cf328384a8addc3c1406505a01578 (this run) **Production drift check:** clean ### Ansible preview (staging clone, synthetic secrets — approximate, not exact prod values) ``` PLAY [Deploy the Docker-based services stack] ********************************** skipping: no hosts matched PLAY RECAP ********************************************************************* ```
fix(ci): staging failure dump includes Traefik's ACME log lines and the certificate names it holds
All checks were successful
Lint / lint (pull_request) Successful in 1m48s
Secret scan / scan (pull_request) Successful in 16s
f9fccad181
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
docs(ci): record the wakeup webhook's events and what this Forgejo version cannot send
All checks were successful
Lint / lint (pull_request) Successful in 1m43s
Secret scan / scan (pull_request) Successful in 13s
5f4036e898
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
chore(secrets): record the 2026-10-13 rotations in the manifest
All checks were successful
Secret scan / scan (pull_request) Successful in 13s
4e94c68f80
PR #125 rotated eight Vaultwarden/Authelia secrets and #133 replaced
POSTFIX_SENDER_ALERTS_APIKEY, all on 2026-10-13. Three still carried the
2026-10-09 date and six had none. Expiry report: unknown 48 -> 44, ok 13 -> 18.

Also clarify the remediation row for AUTHELIA_OIDC_PRIVATE_KEY_PEM: the key was
generated 2026-10-10 (the manifest's rotated_at); 2026-10-13 is the PR #52
merge and redeploy, so the two dates were never in conflict.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge pull request 'docs(ci): record the wakeup webhook's events and what this Forgejo version cannot send' (#102) from jfraeys/docs/wakeup-webhook-events into main
All checks were successful
Staging Validation / validate (push) Successful in 13s
Staging Validation / promote (push) Has been skipped
83b73a0ee1
Merge pull request 'fix(ci): staging failure dump includes Traefik's ACME log lines and the certificate names it holds' (#101) from jfraeys/fix/staging-diag-traefik-acme into main
All checks were successful
Staging Validation / validate (push) Successful in 13m30s
Staging Validation / promote (push) Successful in 7s
93bf71e896
feat(secrets): project-keys wizard for P7, and per-project age keygen
All checks were successful
Lint / lint (pull_request) Successful in 1m52s
Secret scan / scan (pull_request) Successful in 14s
f8cfe4bdce
scripts/project-keys-wizard.sh walks the operator through giving a repo its own
operator key (Keychain) and recovery key (Bitwarden), an offline copy, the
.sops.yaml recipients, encrypting secrets/dev.sops.yml from the local .env
(piped straight into sops; values never printed), the integration run, and the
isolation drill with the real keys in both directions. Re-runs derive public
keys from existing items instead of overwriting them.

age-operator-keygen.sh and age-recovery-keygen.sh take an optional project name
(default infra, so existing behaviour is unchanged).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
Merge pull request 'feat(secrets): project-keys wizard for P7, and per-project age keygen' (#144) from jfraeys/feat/project-keys-wizard into main
All checks were successful
Lint / lint (pull_request) Successful in 1m55s
Secret scan / scan (pull_request) Successful in 12s
9b94e101be
fix(secrets): wizard expands ~ and re-prompts for the offline-copy directory
Some checks failed
Lint / lint (pull_request) Has started running
Secret scan / scan (pull_request) Has been cancelled
998a5af7cb
read does not expand a leading ~, so "~/Documents/x" was rejected as "not a
directory" and the whole wizard exited. Expand it, and loop on a bad path
instead of exiting (blank still skips).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
fix(secrets): wizard reads bare KEY names from .env.example
Some checks failed
Lint / lint (pull_request) Has started running
Secret scan / scan (pull_request) Has been cancelled
f78320520c
forge-agent's .env.example lists bare names (no "="), which the parser skipped,
so it emitted nothing and sops failed with "Error unmarshalling file: EOF".
Accept bare names, KEY=..., export KEY=..., and trailing comments; fail with a
clear message when no names are found.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
fix(secrets): wizard keeps an existing offline copy of the recovery key on re-run
Some checks failed
Lint / lint (pull_request) Has started running
Secret scan / scan (pull_request) Has been cancelled
1567fc29dc
A re-run hit "already exists; refusing to overwrite" after the first run had
written the file. Never overwrite it, but keep it when its public key matches
the recovery key, and refuse with a clear message when it does not.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
fix(secrets): sops exec-env takes one quoted command, not "-- cmd args"
All checks were successful
Lint / lint (pull_request) Successful in 1m37s
Secret scan / scan (pull_request) Successful in 12s
b03e8d23ef
exec-env requires exactly two arguments (file, command string) and has no "--"
handling, so "sops exec-env FILE -- go run ./dev integration" fails with the
misleading "error: missing file to decrypt" (cmd/sops/main.go: c.NArg() != 2).
Found on the first real wizard run. Fix the wizard (run + printed command + the
recovery doc it writes) and the two docs that recommended the "--" form.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
Merge pull request 'fix(secrets): wizard expands ~ and re-prompts for the offline-copy directory' (#146) from jfraeys/fix/wizard-offline-dir-prompt into main
All checks were successful
Lint / lint (pull_request) Successful in 3m37s
Secret scan / scan (pull_request) Successful in 16s
403118da23
chore: retire the P7 key wizard and the stale terraform backup script, label the migration scripts
All checks were successful
Lint / lint (pull_request) Successful in 2m26s
Secret scan / scan (pull_request) Successful in 17s
a47e64c78b
- Remove scripts/project-keys-wizard.sh: P7 keys for forge-agent are done (keys,
  encrypted dev.sops.yml, integration run and isolation drill passed 2026-10-14);
  it was migration-only. The keygen scripts keep their optional project argument.
- Remove scripts/backup-terraform.sh: state lives in the S3 backend, the script
  archived local *.tfstate files that do not exist, printed a warning and exited 0
  (a silent no-op). node-failure.md now describes the remote state and a manual
  `tofu state pull` snapshot.
- Mark the three migration scripts (cloudflare wizard, zone export, ovh wizard)
  MIGRATION-ONLY with when to delete them.
- P7 row: keys met, gate test 2 still pending (not marked Met).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
fix(ci): delete every ephemeral runner registration, not just the last one per cycle
All checks were successful
Lint / lint (pull_request) Successful in 2m21s
Secret scan / scan (pull_request) Successful in 17s
f8099ffde9
A reused VM registers one ephemeral runner per job, and _reuse_vm_for_job reset
cycle["runner_id"] to None, so an earlier registration that never consumed
itself was forgotten and never deleted (valid, never-revoked runner credentials;
5 were found on 2026-10-14, the oldest ~11 days). cleanup_cycle only knew the
last one.

- _reuse_vm_for_job deletes the previous registration (if it still exists)
  before forgetting it.
- cleanup_cycle sweeps every offline ephemeral-* registration after the VM is
  destroyed (best effort; the sweep is skipped while a VM may still exist).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
Merge pull request 'fix(ci): delete every ephemeral runner registration, not just the last one per cycle' (#148) from jfraeys/fix/ephemeral-runner-registration-leak into main
All checks were successful
Staging Validation / validate (push) Successful in 11m51s
Lint / lint (pull_request) Successful in 1m53s
Secret scan / scan (pull_request) Successful in 13s
Staging Validation / promote (push) Successful in 9s
5b023cc9ef
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jfraeys/infra!145
No description provided.