From dacf606c6aae3aa241ac84ccab3ea2aa98136132 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Thu, 10 Sep 2026 12:37:28 -0700 Subject: [PATCH 1/5] Refresh the Nix vendorHash on Dependabot Go bumps Every Dependabot Go bump arrives red on "Nix flake builds": go.sum moves, nix/package.nix's vendorHash does not, and because the check is required the PR wedges until someone runs `make update-nix-hash` with Docker (#427). This thin caller runs basecamp/.github's reusable workflow on Dependabot PRs that touch go.mod or go.sum: it builds the flake at the PR's merge commit, takes the corrected hash from Nix's go-modules fixed-output mismatch, and commits exactly that one line back to the PR with a one-hour App token, so the PR's own Nix check re-runs and verifies it and auto-merge proceeds unattended. Pinned to the reusable workflow's branch SHA; re-pin once it merges. --- .../dependabot-sync-nix-vendor-hash.yml | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/dependabot-sync-nix-vendor-hash.yml diff --git a/.github/workflows/dependabot-sync-nix-vendor-hash.yml b/.github/workflows/dependabot-sync-nix-vendor-hash.yml new file mode 100644 index 00000000..f274777e --- /dev/null +++ b/.github/workflows/dependabot-sync-nix-vendor-hash.yml @@ -0,0 +1,48 @@ +name: Refresh Nix vendorHash on Dependabot PRs + +# A Dependabot Go bump changes go.sum and leaves nix/package.nix's vendorHash +# stale, so "Nix flake builds" — required on main — fails with a fixed-output +# hash mismatch and the PR wedges until someone runs `make update-nix-hash` +# with Docker (#427). This refreshes the hash on the PR itself. +# +# The SHA-pinned reusable workflow builds the flake at the PR's merge commit, +# takes the corrected hash from Nix's go-modules fixed-output mismatch, and +# commits exactly that one line of nix/package.nix back to the PR with a +# one-hour cli-release-bot token scoped to this repository, so the PR's own +# "Nix flake builds" check re-runs on the new head and verifies it. Go bumps +# only: the pull_request jobs that then re-run under the App bot's actor are +# main's own, and none of them references a secret a Go bump can reach — keep +# it that way. The full actor analysis is in the reusable workflow's header. +# +# Needs the cli-release-bot App installed here and RELEASE_APP_PRIVATE_KEY +# stored as a Dependabot secret (Dependabot-triggered runs do not see Actions +# secrets); the dispatch path reads the Actions secret release.yml already +# uses. + +on: + pull_request: + paths: + - go.mod + - go.sum + workflow_dispatch: + inputs: + pr: + description: Dependabot pull request number to refresh + required: true + type: string + +permissions: {} + +jobs: + refresh: + if: github.event_name == 'workflow_dispatch' || (github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]') # zizmor: ignore[bot-conditions] -- dual check: actor validates the current trigger (and stops the App bot's own push from looping back), user.login validates PR origin; on:pull_request, not pull_request_target, so GitHub sets the actor from who pushed + uses: basecamp/.github/.github/workflows/dependabot-sync-nix-vendor-hash.yml@8f44c1e73b288ef09d2a3e48a5771d34fba22b39 + with: + pr: ${{ inputs.pr }} + app-client-id: ${{ vars.RELEASE_CLIENT_ID }} + permissions: + contents: read + pull-requests: read + actions: read + secrets: + app-private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} From 0ace9b21778d0ceff5db3dbe49e176a507057ef0 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Thu, 10 Sep 2026 12:41:09 -0700 Subject: [PATCH 2/5] Pass the App client id literally; release.yml's copies are environment-scoped --- .../workflows/dependabot-sync-nix-vendor-hash.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dependabot-sync-nix-vendor-hash.yml b/.github/workflows/dependabot-sync-nix-vendor-hash.yml index f274777e..07b43ef4 100644 --- a/.github/workflows/dependabot-sync-nix-vendor-hash.yml +++ b/.github/workflows/dependabot-sync-nix-vendor-hash.yml @@ -14,10 +14,12 @@ name: Refresh Nix vendorHash on Dependabot PRs # main's own, and none of them references a secret a Go bump can reach — keep # it that way. The full actor analysis is in the reusable workflow's header. # -# Needs the cli-release-bot App installed here and RELEASE_APP_PRIVATE_KEY -# stored as a Dependabot secret (Dependabot-triggered runs do not see Actions -# secrets); the dispatch path reads the Actions secret release.yml already -# uses. +# Needs the cli-release-bot App installed here with contents: write, and its +# private key stored as a repository *Dependabot* secret named +# RELEASE_APP_PRIVATE_KEY (Dependabot-triggered runs see only Dependabot +# secrets) and, for the workflow_dispatch path, as a repository Actions secret +# of the same name. release.yml's copies live in the `release` environment +# and are not reachable from here, by design. on: pull_request: @@ -39,7 +41,10 @@ jobs: uses: basecamp/.github/.github/workflows/dependabot-sync-nix-vendor-hash.yml@8f44c1e73b288ef09d2a3e48a5771d34fba22b39 with: pr: ${{ inputs.pr }} - app-client-id: ${{ vars.RELEASE_CLIENT_ID }} + # cli-release-bot's client id — the `release` environment's + # RELEASE_CLIENT_ID, which is a public identifier (every release run + # prints it) and, being environment-scoped, is not readable here. + app-client-id: Iv23liAbERu6z8d7t5A8 permissions: contents: read pull-requests: read From 2a1a5aa906e65b41f92693bea2aeef4c403fb18a Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Thu, 10 Sep 2026 12:42:01 -0700 Subject: [PATCH 3/5] Pin the reusable workflow at the contract-comment revision --- .github/workflows/dependabot-sync-nix-vendor-hash.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-sync-nix-vendor-hash.yml b/.github/workflows/dependabot-sync-nix-vendor-hash.yml index 07b43ef4..5ded9336 100644 --- a/.github/workflows/dependabot-sync-nix-vendor-hash.yml +++ b/.github/workflows/dependabot-sync-nix-vendor-hash.yml @@ -38,7 +38,7 @@ permissions: {} jobs: refresh: if: github.event_name == 'workflow_dispatch' || (github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]') # zizmor: ignore[bot-conditions] -- dual check: actor validates the current trigger (and stops the App bot's own push from looping back), user.login validates PR origin; on:pull_request, not pull_request_target, so GitHub sets the actor from who pushed - uses: basecamp/.github/.github/workflows/dependabot-sync-nix-vendor-hash.yml@8f44c1e73b288ef09d2a3e48a5771d34fba22b39 + uses: basecamp/.github/.github/workflows/dependabot-sync-nix-vendor-hash.yml@12f8950fc925285f3dd5bce2e5cc9ea8b7f72da6 with: pr: ${{ inputs.pr }} # cli-release-bot's client id — the `release` environment's From 1d1ecc6adab0d6e1e4b7de14cb88564ca6bf4127 Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Thu, 10 Sep 2026 14:49:03 -0700 Subject: [PATCH 4/5] Pin the vendorHash refresh at the revision that enforces the Go-bump boundary, and say what the required check is for --- .../workflows/dependabot-sync-nix-vendor-hash.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dependabot-sync-nix-vendor-hash.yml b/.github/workflows/dependabot-sync-nix-vendor-hash.yml index 5ded9336..09c89b52 100644 --- a/.github/workflows/dependabot-sync-nix-vendor-hash.yml +++ b/.github/workflows/dependabot-sync-nix-vendor-hash.yml @@ -9,10 +9,15 @@ name: Refresh Nix vendorHash on Dependabot PRs # takes the corrected hash from Nix's go-modules fixed-output mismatch, and # commits exactly that one line of nix/package.nix back to the PR with a # one-hour cli-release-bot token scoped to this repository, so the PR's own -# "Nix flake builds" check re-runs on the new head and verifies it. Go bumps -# only: the pull_request jobs that then re-run under the App bot's actor are -# main's own, and none of them references a secret a Go bump can reach — keep -# it that way. The full actor analysis is in the reusable workflow's header. +# "Nix flake builds" check re-runs on the new head and verifies it. That +# check being required is what keeps the stale head from merging while the +# refresh runs: auto-merge waits for required checks only, so the requirement +# is part of this workflow's contract, not a nicety. Go bumps only, enforced +# by the reusable workflow on the PR's actual diff (go.mod, go.sum and +# nix/package.nix, nothing else; the paths filter below only spares a runner): +# the pull_request jobs that then re-run under the App bot's actor are main's +# own, and none of them references a secret a Go bump can reach — keep it that +# way. The full actor analysis is in the reusable workflow's header. # # Needs the cli-release-bot App installed here with contents: write, and its # private key stored as a repository *Dependabot* secret named @@ -38,7 +43,7 @@ permissions: {} jobs: refresh: if: github.event_name == 'workflow_dispatch' || (github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]') # zizmor: ignore[bot-conditions] -- dual check: actor validates the current trigger (and stops the App bot's own push from looping back), user.login validates PR origin; on:pull_request, not pull_request_target, so GitHub sets the actor from who pushed - uses: basecamp/.github/.github/workflows/dependabot-sync-nix-vendor-hash.yml@12f8950fc925285f3dd5bce2e5cc9ea8b7f72da6 + uses: basecamp/.github/.github/workflows/dependabot-sync-nix-vendor-hash.yml@49eaa2156461cee5ac15c5236d3ff3f5fe900d3f with: pr: ${{ inputs.pr }} # cli-release-bot's client id — the `release` environment's From a36ed5e955e676f5f4a50cb9e1efda25ed01fdbd Mon Sep 17 00:00:00 2001 From: Jeremy Daer Date: Sat, 12 Sep 2026 17:55:46 -0700 Subject: [PATCH 5/5] Pin the vendorHash refresh at basecamp/.github's merged revision basecamp/.github#19 squash-merged as 293dc74; the pin now names that commit rather than the branch head the merge left behind. --- .github/workflows/dependabot-sync-nix-vendor-hash.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-sync-nix-vendor-hash.yml b/.github/workflows/dependabot-sync-nix-vendor-hash.yml index 09c89b52..ebf45312 100644 --- a/.github/workflows/dependabot-sync-nix-vendor-hash.yml +++ b/.github/workflows/dependabot-sync-nix-vendor-hash.yml @@ -43,7 +43,7 @@ permissions: {} jobs: refresh: if: github.event_name == 'workflow_dispatch' || (github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]') # zizmor: ignore[bot-conditions] -- dual check: actor validates the current trigger (and stops the App bot's own push from looping back), user.login validates PR origin; on:pull_request, not pull_request_target, so GitHub sets the actor from who pushed - uses: basecamp/.github/.github/workflows/dependabot-sync-nix-vendor-hash.yml@49eaa2156461cee5ac15c5236d3ff3f5fe900d3f + uses: basecamp/.github/.github/workflows/dependabot-sync-nix-vendor-hash.yml@293dc747a2cee44c5b4dae72067adeb681636d26 with: pr: ${{ inputs.pr }} # cli-release-bot's client id — the `release` environment's