From 74db970c649e6fdbde0370df3972c2bf5ea9910f Mon Sep 17 00:00:00 2001 From: Jose Armesto Date: Mon, 1 Jun 2026 12:16:39 +0200 Subject: [PATCH 1/4] fix(release): normalize empty manifest version to 0.0.0 for first release The auto-merge reconcile step in the release workflow reads the current version from .release-please-manifest.json on the base branch. On a fresh repo the manifest is "{}" until the first release lands, so jq returns "null" for the "." key. The subsequent numeric -gt comparisons then all fall through and classify the bump as "none", which is never auto-merged at any auto-merge-level. Normalize a missing "." key to "0.0.0" so the first release is bump-classified correctly (e.g. 0.0.0 -> 1.0.0 = major, 0.0.0 -> 0.1.0 = minor). Whether the PR actually auto-merges still depends on the configured auto-merge-level. --- .github/workflows/release.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9930d7d..4f66941 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -105,9 +105,12 @@ jobs: base_branch=$(jq -r '.baseBranchName' <<<"$PR_JSON") # Read the single root (".") entry of the release-please manifest at a ref. + # On a fresh repo the manifest is an empty object until the first release lands, + # so a missing "." key is normalized to 0.0.0 — otherwise the numeric comparisons + # below silently fall through and the first release's bump is classified as none. manifest_version() { gh api "repos/${GH_REPO}/contents/.release-please-manifest.json?ref=$1" \ - -H "Accept: application/vnd.github.raw" | jq -r '.["."]' + -H "Accept: application/vnd.github.raw" | jq -r '.["."] // "0.0.0"' } current=$(manifest_version "$base_branch") From bbdbe24fe1c58d7a0fb0071288137ccf4b5ffe60 Mon Sep 17 00:00:00 2001 From: Jose Armesto Date: Mon, 1 Jun 2026 13:12:37 +0200 Subject: [PATCH 2/4] feat(release): auto-approve release PRs via dedicated GitHub App MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Branch protection's required-approval rule blocks `gh pr merge --auto` from ever completing the merge on release-please PRs: the PR author is the release-please App (which cannot self-approve), no human is meaningfully reviewing the auto-generated CHANGELOG diff, and bypass_pull_request_allowances on classic branch protection only allows direct branch pushes — not merging un-approved PRs via the API (empirically verified). Add an optional auto-approve step that uses a dedicated `release-please-approver` App to submit an approving review. The App is intentionally distinct from the release-please App that authors the PR so the review counts. With approval in place, the existing `--auto --squash` step merges as soon as required checks pass. Optional by design: if the approver secrets are not provided, both new steps are skipped and existing callers see no behavior change. --- .github/workflows/release.yaml | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4f66941..29461c8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -20,6 +20,12 @@ on: RELEASE_PLEASE_PRIVATE_KEY: description: Private key for create-github-app-token required: true + RELEASE_PLEASE_APPROVER_CLIENT_ID: + description: Client ID for the release-please-approver App used to satisfy required-approval branch protection on release PRs. Optional — when omitted, the auto-approve step is skipped and the release PR will need a human approval to merge. + required: false + RELEASE_PLEASE_APPROVER_PRIVATE_KEY: + description: Private key for the release-please-approver App. Required if RELEASE_PLEASE_APPROVER_CLIENT_ID is set. + required: false outputs: release_created: description: "Set to 'true' if a release was created." @@ -72,6 +78,38 @@ jobs: config-file: release-please-config.json manifest-file: .release-please-manifest.json + # Auto-approve release-please PRs via a dedicated GitHub App so branch + # protection's required-approval rule is satisfied and `--auto` can complete + # the merge once checks pass. The approving identity is intentionally distinct + # from the release-please App that authors the PR (GitHub rejects self-approval). + # release-please PRs aggregate commits that were already reviewed on their way + # to the base branch; this approval covers only the auto-generated CHANGELOG + # aggregation, not the underlying changes. + # + # The approver secrets are optional — when omitted, this whole pair of steps + # is skipped and the PR will need a human approval (existing-caller behavior). + - name: Generate release-please-approver App token + id: approver_token + if: ${{ steps.release_please.outputs.pr != '' && inputs.auto-merge-level != 'none' && env.APPROVER_CLIENT_ID != '' }} + env: + APPROVER_CLIENT_ID: ${{ secrets.RELEASE_PLEASE_APPROVER_CLIENT_ID }} + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ secrets.RELEASE_PLEASE_APPROVER_CLIENT_ID }} + private-key: ${{ secrets.RELEASE_PLEASE_APPROVER_PRIVATE_KEY }} + + - name: Auto-approve Release Please PR + if: ${{ steps.approver_token.outputs.token != '' }} + env: + GH_TOKEN: ${{ steps.approver_token.outputs.token }} + GH_REPO: ${{ github.repository }} + PR_JSON: ${{ steps.release_please.outputs.pr }} + run: | + set -euo pipefail + pr_number=$(jq -r '.number' <<<"$PR_JSON") + gh pr review "$pr_number" --approve \ + --body "Auto-approved by release-please-approver. Underlying commits were reviewed on their feature PRs; this satisfies branch protection's required-approval rule for the aggregated release PR." + # Reconcile auto-merge on the open Release Please PR every run. release-please # keeps a single PR open and rewrites it as commits land, so its bump level can # change over time; we enable or disable auto-merge to match the current bump. From 6b580c5ad280fd7da81ffc359a58a01258b30fe8 Mon Sep 17 00:00:00 2001 From: Jose Armesto Date: Mon, 1 Jun 2026 14:11:27 +0200 Subject: [PATCH 3/4] fix(release): drop review body on auto-approval The body added noise to the PR review history without giving reviewers useful information. The rationale lives in the YAML comment above the step, which is the right place for it. --- .github/workflows/release.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 29461c8..5aa3d0c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -107,8 +107,7 @@ jobs: run: | set -euo pipefail pr_number=$(jq -r '.number' <<<"$PR_JSON") - gh pr review "$pr_number" --approve \ - --body "Auto-approved by release-please-approver. Underlying commits were reviewed on their feature PRs; this satisfies branch protection's required-approval rule for the aggregated release PR." + gh pr review "$pr_number" --approve # Reconcile auto-merge on the open Release Please PR every run. release-please # keeps a single PR open and rewrites it as commits land, so its bump level can From 918d9371fcaeb4cda0f8a60380550df9639e8a74 Mon Sep 17 00:00:00 2001 From: Jose Armesto Date: Mon, 1 Jun 2026 14:13:40 +0200 Subject: [PATCH 4/4] fix(release): shorten APPROVER_CLIENT_ID description to satisfy yamllint The previous description was 248 chars; project's yamllint config caps lines at 200. Trim wording without losing the key signal (optional + what gets skipped when omitted). --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5aa3d0c..4a31ead 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -21,7 +21,7 @@ on: description: Private key for create-github-app-token required: true RELEASE_PLEASE_APPROVER_CLIENT_ID: - description: Client ID for the release-please-approver App used to satisfy required-approval branch protection on release PRs. Optional — when omitted, the auto-approve step is skipped and the release PR will need a human approval to merge. + description: Client ID for the release-please-approver App. Optional — when omitted, auto-approve is skipped and the release PR needs a human approval to merge. required: false RELEASE_PLEASE_APPROVER_PRIVATE_KEY: description: Private key for the release-please-approver App. Required if RELEASE_PLEASE_APPROVER_CLIENT_ID is set.