From 55077c74e3fa83d93a3db2afa37d0f62f44a9996 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Sat, 1 Aug 2026 10:25:50 -0700 Subject: [PATCH] Carry the merge bot so Dependabot pull requests can merge `.github/dependabot.yml` is a baseline file for every fleet repo, and `merge-bot-pull-request.yml` is what services what it opens. This repo carried the first without the second, so bot pull requests had no way to merge and one was already sitting open. Two jobs are vendored: auto-merge on opened or reopened, and disable auto-merge when a maintainer pushes to the bot's branch. The merge method follows the base, squash for `develop` and a merge commit for `main`, matching what each ruleset allows. The codegen and upstream-version jobs are dropped. This repo has neither, and a job keyed on a bot login that never opens a pull request here would be dead code that reads as coverage. Every tier auto-merges, semver-major included, because the required checks are the gate rather than the size of the bump. Comments are rewritten to one sentence per line. The upstream copy is block-prose, which is the shape the comment rule forbids, and carrying it verbatim would import a defect into a file this repo owns. Clears the corresponding residual delta in `reports/Blog/audit.md` and the open decision in `TODO.md`. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/merge-bot-pull-request.yml | 96 ++++++++++++++++++++ TODO.md | 2 - reports/Blog/audit.md | 1 - 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/merge-bot-pull-request.yml diff --git a/.github/workflows/merge-bot-pull-request.yml b/.github/workflows/merge-bot-pull-request.yml new file mode 100644 index 0000000..5b08735 --- /dev/null +++ b/.github/workflows/merge-bot-pull-request.yml @@ -0,0 +1,96 @@ +name: Merge bot pull request action + +# Auto-merges in-repo Dependabot pull requests: enable on opened or reopened, disable on a maintainer push. +# The merge method follows the base, since the two rulesets allow different forms. +# - develop takes squash. +# - main takes a merge commit. +# An App token is used rather than GITHUB_TOKEN, for two reasons. +# - It fires downstream workflows on merge, which GITHUB_TOKEN deliberately does not. +# - A Dependabot pull request's GITHUB_TOKEN is read-only regardless of who triggered the event. +# The trigger is pull_request_target rather than pull_request, because these jobs hold the App key. +# That resolves the workflow and action SHAs from the trusted base rather than from the pull request head. +# It is safe here because no job checks out pull request code, each one merging by URL alone. +# This repo carries no codegen workflow and no upstream-version tracker, so those jobs are not vendored. +on: + pull_request_target: + types: [ opened, reopened, synchronize ] + +# Concurrency keys on the pull request number rather than github.ref. +# Under pull_request_target github.ref is the base branch, which would serialize every bot pull request. +# cancel-in-progress is false so a follow-up synchronize cannot cancel an in-flight opened run. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: false + +jobs: + + merge-dependabot: + name: Merge dependabot pull request job + runs-on: ubuntu-latest + # Dependabot pull requests raised in this repo, never from a fork. + # Restricted to opened and reopened so the disable job below stays sticky. + if: >- + (github.event.action == 'opened' || github.event.action == 'reopened') && + github.event.pull_request.user.login == 'dependabot[bot]' && + github.event.pull_request.head.repo.full_name == github.repository + permissions: + contents: write + pull-requests: write + + steps: + + - name: Generate GitHub App token step + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ secrets.CODEGEN_APP_CLIENT_ID }} + private-key: ${{ secrets.CODEGEN_APP_PRIVATE_KEY }} + + # Every tier is auto-merged, semver-major included. + # The required checks are the gate, not the size of the bump. + # A major that breaks the build fails its checks and never merges. + - name: Merge pull request step + run: | + set -Eeuo pipefail + case "${{ github.event.pull_request.base.ref }}" in + develop) method=--squash ;; + main) method=--merge ;; + *) + echo "::error::Unsupported base branch: ${{ github.event.pull_request.base.ref }}" + exit 1 + ;; + esac + gh pr merge --auto "$method" "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + disable-auto-merge-on-maintainer-push: + name: Disable auto-merge on maintainer push job + runs-on: ubuntu-latest + # Fires when a maintainer pushes to the bot's branch, which is a synchronize by a non-bot actor. + # Auto-merge is disabled so the maintainer's commits do not merge along with the bot's. + # Re-enabling it is then a deliberate manual act, and the disable call is idempotent. + if: >- + github.event.action == 'synchronize' && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.user.login == 'dependabot[bot]' && + github.actor != github.event.pull_request.user.login + permissions: + pull-requests: write + + steps: + + - name: Generate GitHub App token step + # An App token is required because a Dependabot pull request's GITHUB_TOKEN is read-only. + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ secrets.CODEGEN_APP_CLIENT_ID }} + private-key: ${{ secrets.CODEGEN_APP_PRIVATE_KEY }} + + - name: Disable auto-merge step + run: gh pr merge --disable-auto "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/TODO.md b/TODO.md index d4df466..1836168 100644 --- a/TODO.md +++ b/TODO.md @@ -23,7 +23,6 @@ The site is built and gated in CI. It is on GitHub, and it is not yet serving it ## Next, in dependency order - Dispatch `publish-release.yml` once to prove the release path, which exists but has never run. -- Decide on `merge-bot-pull-request.yml`. Dependabot is configured and its pull requests will otherwise sit open, and the App secrets it needs are now in place. - Provision the VPS: an unprivileged `blogdeploy` user, the deploy root, and `unattended-upgrades` with automatic reboot. - Restrict the deploy key with `restrict,command=...`, no pty and no forwarding, so it can do nothing but rsync into `releases/` and swap the symlink. Generate per-environment keys so staging cannot reach production. - Choose the staging FQDN, add its DNS record, and expose it through Pangolin as a public resource with **no auth**, since CI's live-URL check has to reach it. Authentication defaults to on for a public resource and has to be turned off deliberately. @@ -38,7 +37,6 @@ The site is built and gated in CI. It is on GitHub, and it is not yet serving it - The staging FQDN name. - `/robots.txt/` and `/osd.xml/` currently sit in `slugs.map` pointing at `/`. The first would be better pointing at the real `/robots.txt`. - Whether the 78 legacy date-archive redirects should point at `/all/` rather than `/`, so a visitor following a `/2015/` link lands somewhere they can find 2015 posts. It is a one-line change to rule R3. -- Whether to carry `merge-bot-pull-request.yml`. Dependabot is configured and its PRs will sit open without it, but it needs the App secrets, so it cannot be proven until those exist. ## Deliberate deviations from the fleet baseline diff --git a/reports/Blog/audit.md b/reports/Blog/audit.md index 86903f3..32ef14b 100644 --- a/reports/Blog/audit.md +++ b/reports/Blog/audit.md @@ -116,7 +116,6 @@ Carried forward rather than closed: - The live redirect gate has not run against this build, so 917 of the 1,245 contracted URLs are asserted rather than proven. - `publish-release.yml` has never been dispatched, so the release path is untested. - No deploy exists, so the publish surface stays deferred and the registry entry stays `publish: []`. -- `merge-bot-pull-request.yml` is not carried. Dependabot is configured and its pull requests will sit open until either the merge bot is added or they are merged by hand. The App secrets it needs are now in place, so this is no longer blocked, only undone. - `checks/README.md` carries a small prose backlog of `dash` and `semicolon` findings, left for the next edit of that file per the correct-as-you-next-edit rule.