--- name: kuma-backport description: Backport a merged kumahq/kuma PR to release branches end-to-end — trigger the backport workflow if needed, find the generated backport PRs, resolve cherry-pick conflicts (module-path rewrites, older APIs), fix DCO, build+test per branch, force-push, clean up PR descriptions/labels, mark ready, and watch CI. Use whenever the user gives a kuma PR link and mentions backporting, backport PRs, cherry-pick conflicts on release-X.Y branches, or asks to fix CI on chore/backport-* PRs. argument-hint: " [release-2.x,release-2.y | all]" user-invocable: true --- # Kuma Backport Fixer Input: the URL or number of the **original merged PR** on kumahq/kuma (master), optionally a list of target branches. Run from a kuma checkout. Below, `$UPSTREAM` is the git remote pointing at `github.com/kumahq/kuma` — backport head branches live there (not on forks), so push access to it is required. ## 1. Gather facts about the original PR ```bash gh pr view --repo kumahq/kuma --json state,mergeCommit,title,files ``` Record the merge commit SHA (`MERGE_SHA`) and the changed file list — they are the source of truth for every conflict resolution later. The PR must be MERGED; the workflow rejects unmerged PRs. ## 2. Find or create the backport PRs Backport PRs are created by workflow_dispatch (there is **no** label-driven backport in kuma): - Search first: `gh pr list --repo kumahq/kuma --search "backport of # in:title" --state all --json number,title,baseRefName,isDraft,labels,headRefName` - If none exist (or the user asked to trigger it): ```bash gh workflow run backport.yaml --repo kumahq/kuma --ref master -f PR= [-f branches="release-2.x,release-2.y"] ``` Empty `branches` targets all active branches. Then poll `gh run list --workflow=backport.yaml --repo kumahq/kuma --limit 1` until complete and re-run the PR search. Poll in background; the run takes a few minutes. Facts about what the action produces (this shapes all later steps): - Head branches: `chore/backport--`, pushed to kumahq/kuma directly. - Committer and author are both `kumahq[bot]`. That bot-authored cherry-pick commonly trips **DCO** because the bot's `Signed-off-by` is not a human attestation, and it happens even on conflict-free backports, so check DCO on every PR, not just conflicted ones. - Labels: the target branch, plus the original PR's labels except `backport` and `ci/force-publish`. - On conflict the action runs `git add . && git cherry-pick --continue` **blindly**: conflict markers get committed into files, and files new on master land with master's import paths. The PR is opened as draft with a `conflict` label and the `git status` dump in the body. ## 3. Resolve conflicts per branch For each backport PR whose body has the conflict warning (process branches one at a time; verify each before moving on): ```bash git fetch $UPSTREAM chore/backport-- git checkout -B backport- $UPSTREAM/chore/backport-- ``` ### Know the branch's module path first Master is `github.com/kumahq/kuma/v3`. Release branches differ and are **not uniform** — always read `git show $UPSTREAM/:go.mod | head -1` instead of assuming (as of mid-2026: 2.7–2.14 are `/v2` except 2.9 which is unsuffixed; don't trust this list, check). ### Resolution strategy, in order of preference 1. **Whole-file from original commit + path rewrite** — works when the branch's file matches master apart from import paths: ```bash git show $MERGE_SHA: | sed 's|github.com/kumahq/kuma/v3||g' > ``` 2. **Patch onto the branch's own file** — required when the branch has older third-party APIs (e.g., controller-runtime signatures changed; a whole-file copy then fails to build). Regenerate the file from the release branch and apply only the fix: ```bash git show $UPSTREAM/: > SOURCE_MODULE_PATH=$(git show $UPSTREAM/:go.mod | awk 'NR==1 { print $2 }') git diff $UPSTREAM/ backport- -- \ | perl -pe 's#\Q'"$SOURCE_MODULE_PATH"'\E##g' \ | git apply - ``` Reuse a patch produced from an already-fixed sibling branch — the fix hunks are identical across branches even when the surrounding file isn't. 3. Only if both fail: hand-merge, guided by `git show $MERGE_SHA -- ` (the intended change) against the branch file. ### Style and hygiene - Older branches (≤2.13 as of now) use `interface{}` where master uses `any`. Match the branch: `perl -pe 's/\bany\b/interface{}/g'` (BSD sed has no `\b`). Keep the backport diff minimal — don't rewrite branch signatures to master style. - New test/suite files from the cherry-pick also carry master's module path — rewrite those too. - Sweep for leftovers before committing: ```bash grep -Ern '<<<<<<<|kumahq/kuma/v3' ``` ## 4. Validate locally ```bash eval "$(mise env)" gofmt -l go build .//... && go test .//... ``` `mise env` gives the branch's toolchain; `gofmt -l` must print nothing. Then verify the branch diff has the same shape as the original PR — same files, similar stat: ```bash git diff $UPSTREAM/ HEAD --stat ``` Extra files or missing files mean the bot's blind auto-resolution left damage outside the reported conflicts — fix before pushing. Don't run `make check` on a dirty tree (it fails by design); the targeted build/test above is enough locally, CI runs the full check. ## 5. Fix DCO and push Amend (don't add a fixup commit — keeps the backport a single clean cherry-pick): ```bash git add -A git commit --amend --no-edit --reset-author -s git push --no-verify --force-with-lease $UPSTREAM HEAD:chore/backport-- ``` `--reset-author` replaces the bot-authored cherry-pick with your local identity, and `-s` adds the matching human `Signed-off-by` trailer the DCO probot expects. Add `-S` if you GPG-sign commits. Do this even on conflict-free backport PRs when DCO is red. A backport commit always needs rewriting (drop conflict markers, fix the author/sign-off), so the push is inherently non-fast-forward — a plain push is rejected and a fixup commit on top can't fix DCO on the underlying bot commit. Force is unavoidable. If a git hook or policy blocks force-pushes in your environment, resolve every branch locally first, then hand the exact `git push --force-with-lease $UPSTREAM :chore/backport--` commands to a human to run rather than trying to work around the block. ## 6. PR hygiene For each PR: - **Body**: keep the auto-generated header (cherry-pick line, action link, commit SHA); replace the `:warning:` block + git-status dump with 1–2 sentences stating what conflicted and how it was resolved (e.g., module import path v3→v2; `interface{}` kept per branch style). `gh pr edit --repo kumahq/kuma --body "..."`. - **Labels**: `--remove-label conflict`. Keep `release-X.Y` and the inherited `area/*`, `kind/*`, `ci/*` labels. - **Ready**: `gh pr ready --repo kumahq/kuma`. ## 7. Watch CI Poll all PRs in one background loop until nothing is `pending`; ignore `skipping` rows: ```bash gh pr checks --repo kumahq/kuma ``` If `check` fails, read the job log (`gh run view --log-failed`) — typical causes are lint (import aliases, gci ordering) or a package that only builds on newer branches. Fix, re-amend, re-push. Report the final per-PR status table to the user. ## Gotchas - Backport heads live on kumahq/kuma itself — push there, not to a fork, or the PR won't update. - `distributions`/`check` failing together usually just means the build was broken by committed conflict markers — fix the code, don't debug the jobs. - The changelog comes from the PR title (`... (backport of #N)`), so body edits are safe. - Force-push always with `--force-with-lease` right after a fresh fetch — the action or a teammate may have updated the branch.