Deploy Ansible weekly instead of on every merge - #33
Merged
Merged
Conversation
Every push to main touching ansible/** fired a deploy, so a batch of merges meant a batch of deploys — and each run risks bouncing the Minecraft container on microwave, which players notice. Converge once a week instead, and dispatch when a change should land sooner. live-restore keeps containers up across the dockerd restarts a weekly apt upgrade can trigger, so the scheduled run is safe to leave unattended. The cron sits at 00:17 rather than 00:00 to sidestep the top-of-hour scheduling herd. Also tighten the naming: drop "configuration" from the workflow name, give the job id a verb, and stop the job name asserting a host scope that nothing in CI verifies. The deploy step becomes "Run ansible-playbook", matching lint.yaml's "Run ansible-lint" and freeing "Run the playbook" for the job. docs/ansible.md follows, including the security framing: the boundary is now whatever is on main at cron time plus who can dispatch, not merely who can push. The ansible Environment's required reviewer is dropped — it would park every Sunday run in Waiting until someone approved it, and GitHub fails unapproved deployments after 30 days. The main-only deployment branch policy is what actually keeps a topic-branch workflow edit away from SSH_PRIVATE_KEY. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018CQZdfLFsVJwrGXaECdPpA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every push to
maintouchingansible/**fired a deploy, so a batch of merges meant a batch of deploys — and each run risks bouncing the Minecraft container, which players notice.Converge once a week instead, and dispatch when a change should land sooner.
live-restore: truekeeps containers up across the dockerd restarts a weeklyaptupgrade can trigger, so the scheduled run is safe to leave unattended.Changes
.github/workflows/ansible-deploy.yamlSeven lines.
permissions,concurrency,environment, every step body and every SHA pin are byte-identical.pushonansible/**+ dispatchschedule: 17 0 * * 0+ dispatchDeploy Ansible configurationDeploy Ansibleansible-playbookrun-playbookRun the playbook against all hostsRun the playbookRun the playbookRun ansible-playbookThe cron sits at
:17rather than:00to sidestep the top-of-hour scheduling herd — midnight-UTC-Sunday is the most contended slot GitHub has.On the naming:
configurationwas noise; the old job id was a bare noun; andagainst all hostsasserted a scope nothing in CI verifies — the playbook targetsall,dockerandmicrowavetoday, and nothing would catch it drifting. The deploy step is renamed to freeRun the playbookfor the job, andRun ansible-playbookmatches the existingRun ansible-lintinlint.yaml.The
cronvalue is deliberately unquoted: yamlfix runs withpreserve_quotes = falseand would strip quotes off a quoted cron, dirtying the tree on the nextprek run.Renaming the job is safe — this workflow never fires on
pull_request, so it can't be a required status check. Onlylint.yaml'sLintandAnsible lintare, and both are untouched.docs/ansible.mdThree prose edits: the
## CI deploysparagraph (the only prose statement of the trigger), the## Required repository settingsframing sentence, and theansibleEnvironment bullet.The security framing needed reworking, not just a find-and-replace. Under the push trigger, landing a commit was the deploy, so one boundary covered everything. There are now two: whatever is on
mainwhen the cron fires, and who can start a dispatch. Themain-only deployment branch policy is consequently load-bearing for a second reason.The docs now state the
ansibleEnvironment has no required reviewers. That's a setting outside the repo and it has to be cleared by hand.Environment protection rules apply to scheduled runs, so with a required reviewer in place every Sunday run parks in
Waitinguntil someone approves it, and GitHub fails unapproved deployments after 30 days — the cron would not be unattended, and the doc would disagree with reality.Dropping it is safe: the reviewer was belt-and-braces. The topic-branch protection on the deploy secret comes from restricting deployment branches to
main, which stays. Deployment branches must remain restricted tomain.Verification
Run locally against the pinned tool versions from
.pre-commit-config.yaml:schedule:are both stable fixed points, soprek run --all-filesis clean on the first try.--persona=auditor— no findings (the 1 ignored is the pre-existingdns-dry-run.yamlexception).pushgone, triggers exactly{schedule, workflow_dispatch}, cron loads as a string, job id / job name / step name all as intended.croniterconfirms the next three fires are Sundays at 00:17 UTC.docs/ansible.mdare the pre-existingsshandshredcommands, untouched.Two checks that couldn't run in the dev sandbox, both pre-existing environment limits rather than anything about this diff:
uv syncneeds uv ≥0.11.8 and Python 3.14 (sandbox had 0.8.17 / 3.11), so the linters were run directly rather than throughprek; and zizmor's online audits got a 401 through the sandbox proxy — those key off theuses:lines, which are unchanged. CI covers both.After merge,
scheduleonly ever reads the default branch, so the cron can't be exercised from this branch. Worth confirming onmainthat the workflow showsstate: active, that a dispatch still runs end to end, and that merging anansible/**change produces no run.Known gaps, not addressed here
roles/unattended_upgradessurfaces a pending kernel reboot as anansible.builtin.debugline, and a failed run is visible only to whoever opens Actions. Under the push trigger a human was watching; now nobody is. Afailure()-conditioned notify step would fix it properly.live-restoreis not absolute. It doesn't cover daemon restarts where daemon options changed, or upgrades across major daemon versions — and needrestart runs against the daily unattended-upgrades pass too, so dockerd can still bounce any day. This makes the Ansible-caused restarts predictable, not the only ones.docker_compose_v2is a no-op on an unchanged repo; the container is only recreated when the image is bumped. Those now land on a Sunday instead of mid-week, which is the actual win. Container patches wait up to a week after merge.Generated by Claude Code