From 9a77f251f46635cfce05c23a0e60e0bc4688d59f Mon Sep 17 00:00:00 2001 From: Yun Wang Date: Thu, 17 Sep 2026 16:08:02 +0200 Subject: [PATCH 1/3] ci: skip the test suite on release-please Release PRs A Release PR bumps the version and rewrites the changelog. Every source commit in it already passed the suite on the PR it came from, so running it again only delays the release. The guard sits on each job rather than on the calling job in ci.yml: a job skipped by `if:` reports success and satisfies a required status check, while a reusable workflow that is never called produces no check at all and would leave a required one pending forever. The pre-tag gate is unaffected. release.yml triggers on push, where github.head_ref is empty, so the suite still runs before a tag lands. --- .github/workflows/lint.yml | 4 ++++ .github/workflows/reviewdog.yml | 4 ++++ .github/workflows/run_tests.yml | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2c26c2c..23577b2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -9,8 +9,12 @@ concurrency: permissions: contents: read +# Release PRs only bump the version and rewrite the changelog, so there is nothing here +# that has not already passed on the PR it came from. Skipping by `if:` still reports +# success, so a required status check stays satisfied. jobs: lint: + if: ${{ !startsWith(github.head_ref, 'release-please--') }} name: 👮 Lint runs-on: ubuntu-latest steps: diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index 136c816..18425ab 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -10,8 +10,12 @@ permissions: contents: read pull-requests: write +# Release PRs only bump the version and rewrite the changelog, so there is nothing here +# that has not already passed on the PR it came from. Skipping by `if:` still reports +# success, so a required status check stays satisfied. jobs: reviewdog: + if: ${{ !startsWith(github.head_ref, 'release-please--') }} name: 🐶 Reviewdog runs-on: ubuntu-latest steps: diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 9781d20..fc2146d 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -17,8 +17,12 @@ concurrency: permissions: contents: read +# Release PRs only bump the version and rewrite the changelog, and every commit in one +# already passed this suite on its own PR. The guard sits on each job, not on the caller, +# so a skipped job still reports success to a required status check. jobs: test-build: + if: ${{ !startsWith(github.head_ref, 'release-please--') }} name: 👷 Test & Build ${{ matrix.goVer }}${{ matrix.label }} environment: feeds-enabled-shard runs-on: ubuntu-latest From b584c1f05bc205707ca7e05d1856ff09b203d532 Mon Sep 17 00:00:00 2001 From: Yun Wang Date: Thu, 17 Sep 2026 16:35:06 +0200 Subject: [PATCH 2/3] ci: verify release-please authorship before skipping checks The branch-name guard was spoofable: any PR, a fork's included, could name its head branch release-please--x and skip every required check, which branch protection then counts as satisfied. The guard now also requires the PR to be opened by github-actions[bot] from a branch in this repository. A job-level `if:` could not be used here at all. main requires the six matrix legs of test-build by name, and a job `if:` is evaluated before the matrix expands, so a skipped job produces one check run and not six: those required contexts would never appear and the Release PR would sit blocked. The guard moved to the steps, so every leg still reports while the integration leg is the part that goes away. The lint and reviewdog guards are reverted. Neither is a required context, so there was nothing to keep satisfied, and they are the only workflows that would still have linted release-please's rewrite of version.go. --- .github/workflows/lint.yml | 4 ---- .github/workflows/reviewdog.yml | 4 ---- .github/workflows/run_tests.yml | 25 ++++++++++++++++++------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 23577b2..2c26c2c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -9,12 +9,8 @@ concurrency: permissions: contents: read -# Release PRs only bump the version and rewrite the changelog, so there is nothing here -# that has not already passed on the PR it came from. Skipping by `if:` still reports -# success, so a required status check stays satisfied. jobs: lint: - if: ${{ !startsWith(github.head_ref, 'release-please--') }} name: 👮 Lint runs-on: ubuntu-latest steps: diff --git a/.github/workflows/reviewdog.yml b/.github/workflows/reviewdog.yml index 18425ab..136c816 100644 --- a/.github/workflows/reviewdog.yml +++ b/.github/workflows/reviewdog.yml @@ -10,12 +10,8 @@ permissions: contents: read pull-requests: write -# Release PRs only bump the version and rewrite the changelog, so there is nothing here -# that has not already passed on the PR it came from. Skipping by `if:` still reports -# success, so a required status check stays satisfied. jobs: reviewdog: - if: ${{ !startsWith(github.head_ref, 'release-please--') }} name: 🐶 Reviewdog runs-on: ubuntu-latest steps: diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index fc2146d..2b255a3 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -17,12 +17,23 @@ concurrency: permissions: contents: read -# Release PRs only bump the version and rewrite the changelog, and every commit in one -# already passed this suite on its own PR. The guard sits on each job, not on the caller, -# so a skipped job still reports success to a required status check. +env: + # True only for a Release PR opened by release-please itself. Keying on the branch name + # alone would let any PR, a fork's included, name its branch release-please--x and skip + # every required check, which branch protection then counts as satisfied. + RELEASE_PR: >- + ${{ github.event.pull_request.user.login == 'github-actions[bot]' + && github.event.pull_request.head.repo.full_name == github.repository + && startsWith(github.head_ref, 'release-please--') }} + +# The guard sits on the steps here rather than on the job, unlike the other five SDKs. main +# requires the six matrix legs of test-build by name, and a job-level `if:` is evaluated +# before the matrix expands, which is why the matrix context is not available there. A +# skipped job would produce one check run instead of six, so those required contexts would +# never appear and the Release PR would sit blocked forever. Guarding the steps keeps every +# leg reporting while the 20-minute integration leg is the part that goes away. jobs: test-build: - if: ${{ !startsWith(github.head_ref, 'release-please--') }} name: 👷 Test & Build ${{ matrix.goVer }}${{ matrix.label }} environment: feeds-enabled-shard runs-on: ubuntu-latest @@ -43,7 +54,7 @@ jobs: go-version: ${{ matrix.goVer }} - name: Unit tests via ${{ matrix.goVer }} - if: matrix.goVer != '1.19' + if: matrix.goVer != '1.19' && env.RELEASE_PR != 'true' env: STREAM_BASE_URL: ${{ vars.STREAM_BASE_URL }} STREAM_API_KEY: ${{ vars.STREAM_API_KEY }} @@ -51,7 +62,7 @@ jobs: run: go test -short -v -race ./... - name: Integration tests via ${{ matrix.goVer }} - if: matrix.goVer == '1.19' + if: matrix.goVer == '1.19' && env.RELEASE_PR != 'true' env: STREAM_BASE_URL: ${{ vars.STREAM_BASE_URL }} STREAM_API_KEY: ${{ vars.STREAM_API_KEY }} @@ -61,7 +72,7 @@ jobs: go tool cover -func=cover.out - name: Upload coverage to Codecov - if: matrix.goVer == '1.19' + if: matrix.goVer == '1.19' && env.RELEASE_PR != 'true' uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} From f5145b393639062d1ab72993c78494b7e0d26a0e Mon Sep 17 00:00:00 2001 From: Yun Wang Date: Thu, 17 Sep 2026 16:36:16 +0200 Subject: [PATCH 3/3] ci: test a human commit pushed onto a Release PR The guard keyed on who opened the PR, so a commit pushed by hand onto the release-please branch, to fix a conflict or a changelog entry, inherited the skip and reached the default branch having run nothing. github.actor is the pusher rather than the PR author, so that commit is now tested like any other and only release-please's own pushes skip. --- .github/workflows/run_tests.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 2b255a3..d3a31c4 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -18,11 +18,14 @@ permissions: contents: read env: - # True only for a Release PR opened by release-please itself. Keying on the branch name - # alone would let any PR, a fork's included, name its branch release-please--x and skip - # every required check, which branch protection then counts as satisfied. + # True only for a Release PR opened by release-please and last pushed by it. Keying on the + # branch name alone would let any PR, a fork's included, name its branch release-please--x + # and skip every required check, which branch protection then counts as satisfied. The + # github.actor clause is the pusher rather than the PR author, so a human commit pushed + # onto the Release PR is tested like any other instead of riding the skip into main. RELEASE_PR: >- - ${{ github.event.pull_request.user.login == 'github-actions[bot]' + ${{ github.actor == 'github-actions[bot]' + && github.event.pull_request.user.login == 'github-actions[bot]' && github.event.pull_request.head.repo.full_name == github.repository && startsWith(github.head_ref, 'release-please--') }}