Skip to content

Trunk sync lock

Trunk sync lock #2091

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 has not been synced back
# yet), green once they are in sync. Make `trunk-synced` a required status
# check on staging `main` to block custom-code merges onto a stale trunk
# during the release window.
#
# This only gates PRs, not direct pushes: the back-sync and codegen push to
# staging `main` directly as the orb-stlc-automation app, which is the ruleset
# bypass actor. 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. Trigger this workflow manually BEFORE adding
# `trunk-synced` to the required checks, so the first status is green rather
# than missing.
#
# Sealed into every two-repo staging SDK repo as custom code; the `if` guard
# routes so only the staging copy runs. Source of truth: dev-docs
# stainless/sdk-workflows/.
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: runs-on=${{ github.run_id }}/image=ubuntu24-full-x64/runner=2cpu-linux-x64/spot=false/tag=sdk-trunk-lock
if: >-
endsWith(github.repository, '-staging') &&
github.repository != 'orbcorp/orb-csharp-staging' &&
github.repository != 'orbcorp/orb-typescript-staging'
env:
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Evaluate sync state and post status to open main PRs
run: |
set -euo pipefail
short="${GITHUB_REPOSITORY#*/}"
PRODUCTION_REPO="orbcorp/${short%-staging}"
# Production repos are public; reads need 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 origin/main; 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