Skip to content

Trunk sync lock

Trunk sync lock #176

name: Trunk sync lock
# Posts a `trunk-synced` commit status to open PRs targeting staging `main`:
# red while production is ahead of staging (a release hasn't been synced back
# yet), green once they are in sync.
#
# You should make `trunk-synced` a required status check on staging `main`
# to block merges onto a stale trunk during the release
# window.
#
# This only gates PRs, not direct pushes. The back-sync bypasses this check
# by pushing to staging `main` directly. If staging `main` also requires a pull
# request, give the back-sync's identity ruleset-bypass so it can push. Never
# route the back-sync through a PR gated by this check, or it deadlocks.
#
# First-run note: GitHub treats a status check as "required" only once it has
# been reported at least once. At setup time staging and production are
# identical, so trigger this workflow once (push a PR or use the Run workflow
# button) BEFORE adding `trunk-synced` to the required checks, so the first
# status is green rather than missing.
on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
workflow_run:
workflows: ["Sync from production"]
types: [completed]
repository_dispatch:
types: [prod-released]
workflow_dispatch: {}
schedule:
- cron: '*/30 * * * *'
permissions:
contents: read
statuses: write
pull-requests: read
jobs:
lock:
runs-on: ubuntu-latest
if: github.repository == 'sentdm/sent-dm-python-staging'
env:
PRODUCTION_REPO: sentdm/sent-dm-python
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
- name: Evaluate sync state and post status to open main PRs
run: |
set -euo pipefail
# Production is public; the built-in token reads it with no credential.
git remote add production "https://github.com/${PRODUCTION_REPO}.git"
git fetch --no-tags production main
if git merge-base --is-ancestor production/main HEAD; then
state=success; desc="staging main is in sync with production"
else
state=failure; desc="production is ahead — wait for the back-sync before merging"
fi
echo "trunk-synced => $state ($desc)"
shas=$(gh pr list --repo "$GITHUB_REPOSITORY" --base main --state open --json headRefOid --jq '.[].headRefOid')
if [ -z "$shas" ]; then echo "no open PRs targeting main"; exit 0; fi
for sha in $shas; do
gh api -X POST "repos/$GITHUB_REPOSITORY/statuses/$sha" \
-f state="$state" -f context="trunk-synced" -f description="$desc" >/dev/null
echo "posted trunk-synced=$state to $sha"
done