From a2e37f590dfef5c5e031d8762c1bb14e6e8721f1 Mon Sep 17 00:00:00 2001 From: Maria Zhelezova <43066499+mazhelez@users.noreply.github.com> Date: Thu, 23 Apr 2026 16:19:32 +0200 Subject: [PATCH 1/4] Add backport-pr skill --- .github/skills/backport-pr/SKILL.md | 108 ++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 .github/skills/backport-pr/SKILL.md diff --git a/.github/skills/backport-pr/SKILL.md b/.github/skills/backport-pr/SKILL.md new file mode 100644 index 0000000000..7603f7dfa1 --- /dev/null +++ b/.github/skills/backport-pr/SKILL.md @@ -0,0 +1,108 @@ +--- +name: backport-pr +description: "Backport a merged or open GitHub pull request from microsoft/BCApps to one or more `releases/*` branches. Use when the user asks to: backport a PR, port a PR/fix to a release branch, cherry-pick a PR to releases/*, create backport PRs, propagate a change to release branches. Wraps `New-BCAppsBackport` from build/scripts/CrossBranchPorting.psm1, resolves the correct linked ADO work item per target branch (does NOT reuse the original work item by default), and optionally enables auto-merge on the resulting PRs." +--- + +# Backport a PR to release branches + +Automates backporting a PR from `main` to one or more `releases/*` branches in `microsoft/BCApps`. The repo provides [build/scripts/CrossBranchPorting.psm1](../../../build/scripts/CrossBranchPorting.psm1) which does the cherry-pick + push + PR creation; this skill wraps it with the right defaults and the per-branch work-item resolution rule used by the team. + +## Required inputs + +Ask the user for any missing inputs before proceeding: + +1. **Source PR number** (e.g. `7830`). +2. **Target branches** — explicit list, OR a glob/intent the agent must expand (e.g. "all `releases/*`", "27.* and 28.*", "all `*.x`"). Always confirm the resolved list before running. +3. **Auto-merge?** (yes/no, default no). If yes, default to `--squash`. + +## Pre-flight checks (run in parallel) + +1. `gh auth status` — must be authenticated to github.com for the `microsoft/BCApps` repo. +2. `gh pr view --repo microsoft/BCApps --json state,mergeCommit,potentialMergeCommit,baseRefName,title,body` — verify a cherry-pickable commit exists. Either `mergeCommit` or `potentialMergeCommit` must be non-null. If both are null, stop and tell the user the PR is not yet mergeable. +3. `git fetch origin` and `git branch -r` — confirm every requested target branch exists as `origin/`. List all `releases/*` branches when expanding a glob. +4. Make sure the working tree is clean (no uncommitted changes) — the script will otherwise prompt to stash. + +Show the resolved target branch list to the user and get confirmation before invoking the script. + +## Work item handling (IMPORTANT — do NOT reuse the original work item) + +The script supports `-ReuseWorkItem`, but **do not pass it by default**. Each release branch has its own ADO work item that mirrors the original (typically created as a child / "Related" link on the source work item, scoped to the target branch's iteration/area path). The backport PR description must reference the work item that targets that specific branch. + +For each target branch: + +1. Extract the original ADO work item id from the source PR body (regex `AB#(\d+)`). +2. Resolve the per-branch work item: + - Preferred: query ADO for child / linked work items of the original work item whose Iteration Path or Area Path matches the target release (e.g. `releases/27.3` → iteration containing `27.3`). Use the Azure DevOps work item tools if available (e.g. `mcp_ado_mcp_*` work-item query tools), or `az boards work-item relation show`. + - If the user has the linked work item ids handy, ask them for a mapping `branch -> work-item-id`. + - If exactly one linked work item per branch cannot be resolved automatically, **stop and ask the user** for the correct id rather than guessing or reusing the parent. +3. Use `[**Insert Work Item Number Here**]` as the placeholder only as a last resort, and clearly tell the user which PRs need manual editing. + +Because per-branch work items are required, run the script **one branch at a time** when you need to inject different work item numbers. The script's bulk mode is only safe when every target branch should use the same id. + +## Execution pattern + +The script lives at [build/scripts/CrossBranchPorting.psm1](../../../build/scripts/CrossBranchPorting.psm1). Run from the repo root. + +### Option A — per-branch loop (recommended, gives per-branch work item) + +```powershell +Import-Module ./build/scripts/CrossBranchPorting.psm1 -Force + +$pr = '' +# Map of target branch -> linked ADO work item id (resolved as described above) +$map = @{ + 'releases/27.5' = '' + 'releases/28.x' = '' +} + +foreach ($branch in $map.Keys) { + # New-BCAppsBackport hard-codes the work item handling, so to inject a per-branch id + # the cleanest path is to run it once per branch and then patch the resulting PR body. + New-BCAppsBackport -PullRequestNumber $pr -TargetBranches @($branch) -SkipConfirmation + # The script leaves the PR body with "[**Insert Work Item Number Here**]" — replace it. + $backportPr = gh pr list --repo microsoft/BCApps --state open --search "[$branch] in:title $pr in:body" --json number,body,url | ConvertFrom-Json | Select-Object -First 1 + if ($backportPr) { + $newBody = $backportPr.body -replace '\[\*\*Insert Work Item Number Here\*\*\]', $map[$branch] + gh pr edit $backportPr.number --repo microsoft/BCApps --body $newBody + } +} +``` + +### Option B — bulk run with the same work item (only if user explicitly opts in) + +```powershell +Import-Module ./build/scripts/CrossBranchPorting.psm1 -Force +New-BCAppsBackport -PullRequestNumber -TargetBranches @('releases/27.x','releases/28.x') -SkipConfirmation -ReuseWorkItem +``` + +### Handling transient push failures + +`git push` may fail with `Recv failure: Connection was reset`. The cherry-pick branch is already created locally and committed, so: +1. Manually retry: `git push origin backport///`. +2. Then create the PR: `gh pr create --title "[] " --body "This pull request backports # to `r`n`r`nFixes AB#" --base --head `. +3. Re-run the script for the *remaining* branches only (the duplicate-detection in the script prevents re-creating an existing PR for the same title+base, but skipping the already-handled branch is cleaner). + +## Auto-merge + +If the user asked to auto-merge, run after all PRs are created: + +```powershell +$prNumbers = @() +foreach ($n in $prNumbers) { + gh pr merge $n --repo microsoft/BCApps --auto --squash +} +``` + +`--squash` is the default for this repo. Only switch to `--merge` or `--rebase` if the user asks. + +## Final report to the user + +Always print a markdown table of `branch | PR url`, plus any branches that failed and the reason. If the source PR is still open, remind the user that further changes to it may require refreshing the backports. + +## Pitfalls + +- **Reusing the parent work item.** Default behavior of the underlying script with `-ReuseWorkItem`. Don't do this for real backports — see the work item section above. +- **Source PR not yet mergeable.** Both `mergeCommit` and `potentialMergeCommit` are null until GitHub finishes computing the merge. Wait or refresh. +- **Glob expansion surprises.** "All `releases/*`" includes very old branches (24.x, 25.x) that usually shouldn't be patched. Always show the expanded list and confirm. +- **Working directory state.** The script switches branches; uncommitted changes will trigger a stash prompt. Confirm a clean tree first. +- **Auto-merge on a blocked PR.** `--auto` queues the merge until all checks/required reviews pass; it does not bypass them. If branch protection requires reviews, the PR will sit until approved. From 0fbc16a347877304a342eee624ef06705923a666 Mon Sep 17 00:00:00 2001 From: Maria Zhelezova <43066499+mazhelez@users.noreply.github.com> Date: Thu, 23 Apr 2026 17:19:00 +0200 Subject: [PATCH 2/4] Update .github/skills/backport-pr/SKILL.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/skills/backport-pr/SKILL.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/skills/backport-pr/SKILL.md b/.github/skills/backport-pr/SKILL.md index 7603f7dfa1..1285ad0094 100644 --- a/.github/skills/backport-pr/SKILL.md +++ b/.github/skills/backport-pr/SKILL.md @@ -79,7 +79,10 @@ New-BCAppsBackport -PullRequestNumber -TargetBranches @('releases/27.x','re `git push` may fail with `Recv failure: Connection was reset`. The cherry-pick branch is already created locally and committed, so: 1. Manually retry: `git push origin backport///`. -2. Then create the PR: `gh pr create --title "[] " --body "This pull request backports # to `r`n`r`nFixes AB#" --base --head `. +2. Then create the PR: + + ```powershell + gh pr create --title "[] " --body "This pull request backports # to `r`n`r`nFixes AB#" --base --head 3. Re-run the script for the *remaining* branches only (the duplicate-detection in the script prevents re-creating an existing PR for the same title+base, but skipping the already-handled branch is cleaner). ## Auto-merge From ca365896f2f5041b082c3c44c5846f6c4a2ad8ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:23:19 +0000 Subject: [PATCH 3/4] docs: make backport PR body update lookup deterministic Agent-Logs-Url: https://github.com/microsoft/BCApps/sessions/852107e7-13ec-40ef-a8a6-0e85a9599f06 Co-authored-by: mazhelez <43066499+mazhelez@users.noreply.github.com> --- .github/skills/backport-pr/SKILL.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/skills/backport-pr/SKILL.md b/.github/skills/backport-pr/SKILL.md index 1285ad0094..5f514129a0 100644 --- a/.github/skills/backport-pr/SKILL.md +++ b/.github/skills/backport-pr/SKILL.md @@ -49,6 +49,7 @@ The script lives at [build/scripts/CrossBranchPorting.psm1](../../../build/scrip Import-Module ./build/scripts/CrossBranchPorting.psm1 -Force $pr = '' +$sourceTitle = gh pr view $pr --repo microsoft/BCApps --json title --jq .title # Map of target branch -> linked ADO work item id (resolved as described above) $map = @{ 'releases/27.5' = '' @@ -60,10 +61,22 @@ foreach ($branch in $map.Keys) { # the cleanest path is to run it once per branch and then patch the resulting PR body. New-BCAppsBackport -PullRequestNumber $pr -TargetBranches @($branch) -SkipConfirmation # The script leaves the PR body with "[**Insert Work Item Number Here**]" — replace it. - $backportPr = gh pr list --repo microsoft/BCApps --state open --search "[$branch] in:title $pr in:body" --json number,body,url | ConvertFrom-Json | Select-Object -First 1 + $expectedTitle = "[$branch] $sourceTitle" + $backportPr = $null + for ($attempt = 1; $attempt -le 5 -and -not $backportPr; $attempt++) { + $backportPr = gh pr list --repo microsoft/BCApps --state open --base $branch --search "`"$expectedTitle`" in:title" --json number,title,body,url | + ConvertFrom-Json | + Where-Object { $_.title -eq $expectedTitle } | + Select-Object -First 1 + if (-not $backportPr) { + Start-Sleep -Seconds 3 + } + } if ($backportPr) { $newBody = $backportPr.body -replace '\[\*\*Insert Work Item Number Here\*\*\]', $map[$branch] gh pr edit $backportPr.number --repo microsoft/BCApps --body $newBody + } else { + Write-Warning "Could not find backport PR for branch '$branch' after retries. Update the PR body manually." } } ``` @@ -83,6 +96,7 @@ New-BCAppsBackport -PullRequestNumber -TargetBranches @('releases/27.x','re ```powershell gh pr create --title "[] " --body "This pull request backports # to `r`n`r`nFixes AB#" --base --head + ``` 3. Re-run the script for the *remaining* branches only (the duplicate-detection in the script prevents re-creating an existing PR for the same title+base, but skipping the already-handled branch is cleaner). ## Auto-merge From 8f63040d38f66ca6a7bcbcc567799975c935edcc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:24:03 +0000 Subject: [PATCH 4/4] docs: add backport title lookup guards and retry rationale Agent-Logs-Url: https://github.com/microsoft/BCApps/sessions/852107e7-13ec-40ef-a8a6-0e85a9599f06 Co-authored-by: mazhelez <43066499+mazhelez@users.noreply.github.com> --- .github/skills/backport-pr/SKILL.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/skills/backport-pr/SKILL.md b/.github/skills/backport-pr/SKILL.md index 5f514129a0..6538e08663 100644 --- a/.github/skills/backport-pr/SKILL.md +++ b/.github/skills/backport-pr/SKILL.md @@ -50,6 +50,9 @@ Import-Module ./build/scripts/CrossBranchPorting.psm1 -Force $pr = '' $sourceTitle = gh pr view $pr --repo microsoft/BCApps --json title --jq .title +if (-not $sourceTitle) { + throw "Could not read title for source PR #$pr." +} # Map of target branch -> linked ADO work item id (resolved as described above) $map = @{ 'releases/27.5' = '' @@ -63,6 +66,7 @@ foreach ($branch in $map.Keys) { # The script leaves the PR body with "[**Insert Work Item Number Here**]" — replace it. $expectedTitle = "[$branch] $sourceTitle" $backportPr = $null + # GitHub search indexing can lag briefly after PR creation; retry for ~15 seconds. for ($attempt = 1; $attempt -le 5 -and -not $backportPr; $attempt++) { $backportPr = gh pr list --repo microsoft/BCApps --state open --base $branch --search "`"$expectedTitle`" in:title" --json number,title,body,url | ConvertFrom-Json |