From 76f9e48827904554eebcd84ce808bad690c04557 Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Tue, 28 Jul 2026 11:30:45 +1200 Subject: [PATCH 1/5] [ML] Arm backport auto-merge with the ephemeral token so cleanup fires remove-backport-pending never ran for auto-merged backports: auto-merge was armed with GITHUB_TOKEN, so GitHub attributed the deferred merge to github-actions[bot], and merges by github-actions[bot] do not trigger workflows (GitHub's recursion guard). The source PR's backport-pending label therefore had to be cleared by hand. Arm auto-merge with the ephemeral App token (elastic-vault-github-plugin-prod[bot]) that already authors the backport PRs, falling back to GITHUB_TOKEN only when it is unavailable. GitHub attributes the deferred merge to whichever identity enabled auto-merge, so an App-identity merge triggers remove-backport-pending normally. The approval stays as github-actions[bot] (a distinct identity from the author, so it counts). This is the GitHub-recommended remedy and mirrors what elastic/kibana gets for free by merging as a machine user. Co-authored-by: Cursor --- .github/workflows/backport.yml | 39 ++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index ff2f36f5f..b71d26e14 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -139,11 +139,13 @@ jobs: steps.backport.outcome == 'success' && contains(github.event.pull_request.labels.*.name, 'auto-backport') env: + # github-actions[bot] token: used to APPROVE the backport PRs (a distinct + # identity from the ephemeral-token author, so the approval counts). GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # 'true' only when the ephemeral token was minted (Token Policy registered), - # i.e. the backport PRs were authored by the ephemeral-token app identity and - # can therefore be approved by github-actions[bot]. - EPHEMERAL_AUTHORED: ${{ steps.ephemeral_token.outputs.token != '' }} + # The ephemeral App token (elastic-vault-github-plugin-prod[bot]) that authored + # the backport PRs, when it was minted (Token Policy registered). Empty on the + # fallback path. Used to ARM auto-merge — see the note below. + EPHEMERAL_TOKEN: ${{ steps.ephemeral_token.outputs.token }} REPO: ${{ github.repository }} run: | # Extract created PR numbers from the backport info log. @@ -153,10 +155,25 @@ jobs: echo "No backport PRs found in log. Skipping approval." exit 0 fi + + # Choose the token that ARMS auto-merge. GitHub attributes a deferred auto-merge + # to whichever identity enabled it, and merges attributed to github-actions[bot] + # (i.e. GITHUB_TOKEN) do NOT trigger further workflows — GitHub's recursion guard. + # If we armed auto-merge with GITHUB_TOKEN, the merge would never fire the + # remove-backport-pending job, so backport-pending would stay stuck on the source + # PR (it had to be cleared by hand). Arming with the ephemeral App identity makes + # the eventual merge trigger that job normally. This is the same reason we author + # the PRs with the ephemeral token: so downstream workflows (CI here, cleanup on + # merge) actually run. Falls back to GITHUB_TOKEN when the ephemeral token is + # unavailable (Token Policy not registered) — in that degraded case cleanup still + # needs doing manually, but backports at least open and can be merged by a human. + MERGE_TOKEN="${EPHEMERAL_TOKEN:-$GH_TOKEN}" + for pr in $PR_NUMBERS; do - if [ "$EPHEMERAL_AUTHORED" = "true" ]; then + if [ -n "$EPHEMERAL_TOKEN" ]; then # Authored by the ephemeral-token identity, so this github-actions[bot] - # approval is from a distinct identity and satisfies the required review. + # approval (via the step-level GH_TOKEN) is from a distinct identity and + # satisfies the required review. echo "Approving backport PR #$pr as github-actions[bot]" gh pr review "$pr" --repo "$REPO" --approve \ --body "Automated approval: clean backport of an already-reviewed change. Auto-merge is armed and will merge once the required CI checks are green." || \ @@ -165,12 +182,12 @@ jobs: echo "::warning::Ephemeral token was unavailable (is the backport Token Policy registered in elastic/catalog-info?); #$pr was authored by github-actions[bot], so it cannot be auto-approved and needs a manual approval. Auto-merge is still armed and will merge once approved and CI is green." fi - # Arm auto-merge (squash). This does NOT merge immediately: GitHub merges only - # once every required status check (including buildkite/ml-cpp-pr-builds) is - # green and the review requirement is satisfied. If CI never runs or fails, the - # PR simply stays open — fail-safe. + # Arm auto-merge (squash) as MERGE_TOKEN. This does NOT merge immediately: + # GitHub merges only once every required status check (including + # buildkite/ml-cpp-pr-builds) is green and the review requirement is satisfied. + # If CI never runs or fails, the PR simply stays open — fail-safe. echo "Enabling auto-merge (squash) on backport PR #$pr" - gh pr merge "$pr" --repo "$REPO" --auto --squash || \ + GH_TOKEN="$MERGE_TOKEN" gh pr merge "$pr" --repo "$REPO" --auto --squash || \ echo "::warning::Could not enable auto-merge on #$pr" done From a71009c375b8c00382b41e935c0932ef8ff9b78e Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Tue, 28 Jul 2026 11:32:29 +1200 Subject: [PATCH 2/5] Update docs/changelog/3128.yaml --- docs/changelog/3128.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/changelog/3128.yaml diff --git a/docs/changelog/3128.yaml b/docs/changelog/3128.yaml new file mode 100644 index 000000000..030e173c4 --- /dev/null +++ b/docs/changelog/3128.yaml @@ -0,0 +1,5 @@ +area: Machine Learning +issues: [] +pr: 3128 +summary: Fix backport-pending never clearing on auto-merged backports +type: bug From 2fbfa948279a21a34d755101436a091036ed71eb Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Tue, 28 Jul 2026 11:52:36 +1200 Subject: [PATCH 3/5] Delete docs/changelog/3128.yaml --- docs/changelog/3128.yaml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 docs/changelog/3128.yaml diff --git a/docs/changelog/3128.yaml b/docs/changelog/3128.yaml deleted file mode 100644 index 030e173c4..000000000 --- a/docs/changelog/3128.yaml +++ /dev/null @@ -1,5 +0,0 @@ -area: Machine Learning -issues: [] -pr: 3128 -summary: Fix backport-pending never clearing on auto-merged backports -type: bug From 9230dd9a523f68852d9cd05800dbcb95a949c214 Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Tue, 28 Jul 2026 11:58:47 +1200 Subject: [PATCH 4/5] [ML] Stop copying backport-pending/auto-backport labels onto backport PRs copySourcePRLabels copied every source label except 'backport' and version labels onto the backport PRs the action creates, so backport-pending and auto-backport leaked onto them. backport-pending is misleading on a backport (it isn't itself awaiting backports) and pollutes 'pending backports' queries; auto-backport is meaningless there (the 'backport' label already guards against re-backporting). Exclude both from the copy regex; :ml, >type and other labels still propagate. Co-authored-by: Cursor --- .backportrc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.backportrc.json b/.backportrc.json index e8f9fe059..58c70abd3 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -15,7 +15,7 @@ "^v9.6.0$": "main", "^v(\\d+).(\\d+).\\d+(?:-(?:alpha|beta|rc)\\d+)?$": "$1.$2" }, - "copySourcePRLabels": "^(?!backport$)(?!v\\d).*$", + "copySourcePRLabels": "^(?!backport(?:-pending)?$)(?!auto-backport$)(?!v\\d).*$", "sourcePRLabels": [ "backport-pending" ] From 9b8653e090639ac3ea149b9cdb68d812511d8650 Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Tue, 28 Jul 2026 13:44:26 +1200 Subject: [PATCH 5/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/backport.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index b71d26e14..65287d554 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -162,9 +162,9 @@ jobs: # If we armed auto-merge with GITHUB_TOKEN, the merge would never fire the # remove-backport-pending job, so backport-pending would stay stuck on the source # PR (it had to be cleared by hand). Arming with the ephemeral App identity makes - # the eventual merge trigger that job normally. This is the same reason we author - # the PRs with the ephemeral token: so downstream workflows (CI here, cleanup on - # merge) actually run. Falls back to GITHUB_TOKEN when the ephemeral token is + # the eventual merge trigger that job normally. This is the same reason we don't + # arm auto-merge with GITHUB_TOKEN: events attributed to github-actions[bot] won't + # start this workflow on merge. Falls back to GITHUB_TOKEN when the ephemeral token is # unavailable (Token Policy not registered) — in that degraded case cleanup still # needs doing manually, but backports at least open and can be merged by a human. MERGE_TOKEN="${EPHEMERAL_TOKEN:-$GH_TOKEN}"