Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 45 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,66 @@ name: CI
# whole change, which restored the old bytes rather than recording the new
# ones.
#
# The auto-commit steps below push with the default GITHUB_TOKEN, and GitHub
# does not start a workflow run for those pushes (there is no ci.yml run for
# b1be5d5, the "[ci] apply-formatting" commit). So this job sees each push
# exactly once and does not re-enter itself.
# The auto-commit steps below push with a PAT (secrets.PAT), NOT the
# default GITHUB_TOKEN. main's "require a pull request before merging"
# ruleset refuses GITHUB_TOKEN pushes outright:
Comment on lines +26 to +28
#
# remote: error: GH006: Protected branch update failed for refs/heads/main.
# remote: - Changes must be made through a pull request.
#
# That is a protection-rule rejection, not a permissions problem, so no
# `permissions:` value can fix it - the token already had contents: write
# when it was refused. The fix is that the PAT's account sits on that
# ruleset's bypass list. GITHUB_TOKEN cannot be given a ruleset bypass at
# all: bypass actors may be repository roles, teams, installed GitHub Apps
# or Dependabot, and the built-in Actions integration is none of those.
#
# CONSEQUENCE, and the reason for the job-level `if:` below: unlike
# GITHUB_TOKEN, a PAT's pushes DO start new workflow runs. Without the guard
# this workflow re-enters itself on its own "[ci] ..." commits, forever.
# Skipping those re-runs loses nothing, because the strict test run at the
# end of this job already ran against the final tree it produced.

on:
push:
branches:
- main

# Least privilege: the pushes in this job authenticate with PAT, so
# GITHUB_TOKEN itself never needs write.
permissions:
contents: write
contents: read

jobs:
build:
# See CONSEQUENCE in the header. Do not remove without first moving the
# pushes back to an identity whose commits do not trigger workflows.
if: ${{ !startsWith(github.event.head_commit.message, '[ci] ') }}
Comment on lines +58 to +60
runs-on: ubuntu-latest
steps:
# Fail fast and legibly. Without this, a missing secret surfaces as an
# opaque authentication error at the first auto-commit step, five steps
# later, which reads like a protection problem rather than a config one.
- name: Check PAT is configured
env:
PAT: ${{ secrets.PAT }}
run: |
set -euo pipefail
if [ -z "$PAT" ]; then
echo "secrets.PAT is not set." >&2
echo "This workflow cannot push to main without it: the branch ruleset" >&2
echo "rejects GITHUB_TOKEN pushes (GH006). See the header comment." >&2
exit 1
fi

- uses: actions/checkout@v7
with:
fetch-depth: 0
# Hands the PAT to git. persist-credentials defaults to true, and
# that is precisely what makes the three git-auto-commit-action
# steps below push as the bypassing account rather than as
# github-actions[bot]. They need no configuration of their own.
token: ${{ secrets.PAT }}

- name: Set up Python
uses: actions/setup-python@v7
Expand Down