Skip to content

chore(release): 2.8.0 #451

chore(release): 2.8.0

chore(release): 2.8.0 #451

name: Pre-merge autosync
# Auto-amends PR branches with the output of:
#
# * uv run bernstein agents-md sync - regenerates AGENTS.md / CLAUDE.md /
# CONVENTIONS.md / .goosehints / .cursor/rules mirrors from the canonical IR
# * uv run ruff check . --fix --unsafe-fixes && uv run ruff format .
#
# This closes the post-merge drift class that put main red after the rapid
# merge burst: AGENTS.md cross-CLI sync, mirror docs, and `ruff format` output
# are not in every existing required-check list, so a PR could auto-merge
# while one of those mirrors was already stale, then `docs-drift` /
# `Repo hygiene` would fail on main right after.
#
# Design notes
# ------------
# * Job runs ONLY on same-repo PRs. Fork PRs are skipped because the bot has
# no push access to a fork's head ref - the existing docs-drift workflow
# already comments the regen instructions on fork PRs.
# * The PR author allow-list filters out bot-authored PRs to prevent an
# auto-amend loop (renovate / dependabot / github-actions / bernstein
# orchestrator bots cycle through their own PRs frequently and the
# auto-amend would race them).
# * A `skip-autosync` PR label disables this workflow per-PR for operator
# escape-hatches (e.g. tracking-only WIP PRs that intentionally keep stale
# mirrors).
# * Push requires BERNSTEIN_AUTOSYNC_TOKEN (fine-grained PAT or GitHub App
# token with contents:write on this repo). The workflow refuses to push
# with GITHUB_TOKEN because those commits do not retrigger the source PR's
# downstream checks.
on:
pull_request:
types: [opened, synchronize, ready_for_review]
concurrency:
group: pre-merge-autosync-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
jobs:
autosync:
name: Regenerate mirrors and format
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
# Skip bot-authored PRs (avoids feedback loops where the auto-amend
# commit re-triggers a bot that opens another PR / re-pushes), skip
# fork PRs (cannot push to a fork's head ref), and honour the
# `skip-autosync` label for operator opt-out.
if: >-
github.event.pull_request.head.repo.full_name == github.repository &&
github.event.pull_request.user.login != 'dependabot[bot]' &&
github.event.pull_request.user.login != 'renovate[bot]' &&
github.event.pull_request.user.login != 'github-actions[bot]' &&
github.event.pull_request.user.login != 'bernstein[bot]' &&
github.event.pull_request.user.login != 'bernstein-orchestrator[bot]' &&
!contains(github.event.pull_request.labels.*.name, 'skip-autosync')
steps:
- name: Harden runner (audit mode)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Require named autosync token
env:
BERNSTEIN_AUTOSYNC_TOKEN: ${{ secrets.BERNSTEIN_AUTOSYNC_TOKEN }}
run: |
set -euo pipefail
if [ -z "${BERNSTEIN_AUTOSYNC_TOKEN}" ]; then
echo "::error::BERNSTEIN_AUTOSYNC_TOKEN is required for autosync pushes; refusing to push with GITHUB_TOKEN."
exit 1
fi
# Persist credentials so the post-step push lands on the PR head ref.
# Use the named PAT/App token so the amend commit triggers downstream
# workflows on the PR.
- name: Checkout PR head
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 # zizmor: ignore[artipacked]
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
token: ${{ secrets.BERNSTEIN_AUTOSYNC_TOKEN }}
- uses: ./.github/actions/bootstrap
- name: Install project (for the bernstein CLI)
run: uv sync --no-dev --frozen
- name: Regenerate AGENTS.md mirrors
run: uv run bernstein agents-md sync --workdir .
- name: Run ruff fix and format
run: |
set -euo pipefail
uv run ruff check . --fix --unsafe-fixes
uv run ruff format .
- name: Detect drift
id: drift
run: |
if [ -z "$(git status --porcelain)" ]; then
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "::notice::No drift; nothing to amend."
else
echo "changed=true" >> "$GITHUB_OUTPUT"
git status --short
fi
- name: Commit and push regen to PR head ref
if: steps.drift.outputs.changed == 'true'
env:
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A
git commit -m "chore(auto): regenerate AGENTS.md mirrors + ruff format"
# Regular push (not force). The PR head ref only ever fast-forwards
# from autosync because we just rebuilt mirrors from the current
# tree. If a concurrent push from the PR author moved the tip,
# let the push fail and the next pull_request.synchronize event
# will re-run the job.
git push origin "HEAD:${PR_HEAD_REF}"