Add guard rails for several agents working on the repo at once

Deploys from unpushed branches were being reverted by the next deploy
from main, and in-place vault edits could overwrite each other.

- deploy-guard.yml (imported by site.yml and dns.yml) refuses real runs
  unless the checkout is clean and exactly origin/main; --check is fine.
- bin/deploy holds a lock so only one deploy runs at a time.
- bin/vault-run edits the vault under a lock with an atomic swap.
- bin/worktree starts a task in a fresh worktree from origin/main.
- CLAUDE.md explains the workflow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Phil
2026-10-31 09:29:41 -06:00
co-authored by Claude Opus 5.5
parent 9363bc24d5
commit 0875389d02
8 changed files with 149 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
---
# Imported at the top of site.yml and dns.yml.
#
# Several agents work on this repo at once. If one deploys from an unpushed
# branch, the next deploy from main quietly puts the old config back. So a
# real run only goes ahead when this checkout is exactly origin/main:
# land your commit on main first, then deploy that commit.
#
# --check runs are always allowed. -e allow_unpushed=true skips the guard
# for emergencies; push the fix to main straight after.
- name: Only deploy what's on origin/main
hosts: localhost
gather_facts: false
tags: always
tasks:
- name: Check this checkout matches origin/main
ansible.builtin.shell: |
set -eu
git fetch -q origin main
if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
echo "This checkout has uncommitted changes. Commit them, land them on main, then deploy."
exit 1
fi
head=$(git rev-parse HEAD); main=$(git rev-parse origin/main)
if [ "$head" != "$main" ]; then
echo "HEAD $(git rev-parse --short HEAD) isn't origin/main ($(git rev-parse --short origin/main))."
if git merge-base --is-ancestor "$main" "$head"; then
echo "Your commits aren't on main yet: rebase, push to main, then deploy."
else
echo "main has moved on: rebase onto origin/main first, or other agents' changes get reverted."
fi
exit 1
fi
args:
chdir: "{{ playbook_dir }}"
changed_when: false
check_mode: false
when:
- not ansible_check_mode
- not (allow_unpushed | default(false) | bool)