Comfy-Org / Comfy-Org/ComfyUI_frontend
PR Backport workflow is not stack-aware: every PR above the first in a stack fails with spurious conflicts
- Dominant language
- TypeScript
- Stars
- 2k
- Forks
- 699
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 490
Description
## Summary
`.github/workflows/pr-backport.yaml` cherry-picks each labelled PR onto the release branch **independently**, with no awareness that PRs can be stacked. When a stack of N PRs is labelled `needs-backport`, every PR above the first fails with merge conflicts until the PR below it has actually merged into the target branch.
The work is not lost — re-triggering after the lower PR lands succeeds cleanly — but each level costs a spurious failed run, a scary "⚠️ Backport failed" comment on the PR, and manual re-triggering. For a 4-PR stack across 2 release branches that is 8 false conflict reports.
## Where
The `Backport commits` step builds every backport branch straight off the target:
```bash
git checkout -B "${BACKPORT_BRANCH}" "origin/${TARGET_BRANCH}"
...
if git cherry-pick "${MERGE_COMMIT}"; then
```
`origin/${TARGET_BRANCH}` is the tip of the release branch _at that moment_. If the parent PR's backport is still an open PR (rather than merged), the parent's changes are simply absent, and any child commit that touches the same lines conflicts.
## Evidence
Backporting #14142–#14145 (a 4-PR stack) to `core/1.49` and `cloud/1.49`:
| PR | first attempt | outcome |
| ------ | -------------------------------------------------- | ---------------------------------------------------- |
| #14142 | ✅ clean | #14634 (core), #14635 (cloud) |
| #14143 | ✅ clean | #14637 (core), #14638 (cloud) |
| #14144 | ❌ conflicts at `2026-08-04T02:15Z` | succeeded on re-trigger at `04:41Z` → #14662, #14663 |
| #14145 | ❌ conflicts at `03:12Z`, ❌ **again** at `04:41Z` | succeeded at `05:00Z` → #14664, #14665 |
#14145 is the clearest signal: it failed **twice**. The second failure at `04:41Z` is the moment #14144's backports were merely _created_ — #14145 only went through at `05:00Z`, once #14144's backport had actually merged into the release branch. Same pattern at all four levels, on both target branches.
### Control experiment
Starting from `core/1.49` at `80b8bd5` (its tip immediately before any of the four backports landed):
**A. What the workflow does — cherry-pick #14144 alone:**
```
CONFLICT in:
browser_tests/tests/gettingStartedTour.spec.ts
src/locales/en/main.json
src/platform/telemetry/types.ts
src/renderer/extensions/firstRunTour/FirstRunTour.vue
src/renderer/extensions/firstRunTour/tour/useFirstRunTourController.test.ts
src/renderer/extensions/firstRunTour/tour/useFirstRunTourController.ts
```
That is byte-for-byte the file list the bot reported on #14144.
**B. Cherry-pick all four in stack order** (`bc8eab3` → `e9d2bd7` → `a6dff1d` → `0f0590b`):
```
bc8eab3 CLEAN
e9d2bd7 CLEAN
a6dff1d CLEAN
0f0590b CLEAN
```
**Zero conflicts.** The conflicts are entirely an artifact of picking each commit in isolation, not a property of the changes.
## Workaround (current)
The `pull_request_target: [labeled]` trigger is the only reliable way to re-fire. After the lower PR's backport merges into the target branch, on the next PR up:
1. remove the `needs-backport` label
2. re-add it
The workflow re-runs and, because the parent is now present on the target branch, the cherry-pick applies cleanly. Repeat one level at a time, bottom-up. (Note `workflow_dispatch` is _not_ a usable alternative today — see below.)
## Possible fixes
Roughly in increasing order of risk:
1. **Comment-only (near-zero risk).** On a conflict, check whether any _other_ open PR labelled `backport` targets the same branch. If so, add a line to the failure comment: "this may be a stacking conflict — wait for #NNN to merge into ``, then remove and re-add `needs-backport`." Purely cosmetic; makes the failure self-explanatory instead of alarming.
2. **Defer instead of fail.** If the conflict coincides with an open backport PR to the same target, don't post a failure or delete the branch — re-queue. This needs a retry trigger (e.g. also run on `pull_request_target: closed` for PRs labelled `backport`, so a merging backport kicks its dependents).
3. **Stack-aware batching.** Detect the set of `needs-backport` PRs whose merge commits are contiguous on `main` and cherry-pick them onto a single branch in commit order. Correct in one pass, but the largest change and the one most likely to produce surprising multi-PR backport branches.
(1) is worth doing regardless of whether (2)/(3) ever happen.
## Separate defect in the same workflow, already fixed
The `workflow_dispatch` path fails validation with `PR #NNNNN not found or inaccessible`, because the `Validate inputs for manual triggers` step runs its `gh pr view` calls **before** `actions/checkout`, so `gh` has no repository context to infer. This is why manual dispatch cannot be used as the re-trigger workaround above.
That already has an open fix in #13699 — not duplicating it here, just noting that it removes the obvious escape hatch for this issue and is worth landing alongside any fix for the stacking problem.
Contributor guide
Research direction
Start with .github/workflows/pr-backport.yaml, especially the pull_request_target triggers, the Backport commits step, and the conflict-handling path. Reproduce the documented stacked-PR scenario on a test branch, then verify the chosen behavior against the #14142–#14145 evidence: stacked backports should not produce spurious conflict reports or require manual bottom-up retriggering.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, github-actions, shell
- Domain
- ci-cd, devops
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100