mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
Enables manual backport triggering for scenarios where labels are added after PR merge. Adds workflow_dispatch trigger to the backport workflow with support for: - Specifying PR number to backport post-merge - Force rerun option to override duplicate detection - Proper handling of multi-version backport scenarios Solves the issue where adding version labels (e.g., 1.27) after a PR is already merged and backported (e.g., to 1.26) would not trigger additional backports. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-5651-feat-add-manual-dispatch-to-backport-workflow-2736d73d365081b6ba00c7a43c9ba06b) by [Unito](https://www.unito.io)
269 lines
11 KiB
YAML
269 lines
11 KiB
YAML
name: Auto Backport
|
|
|
|
on:
|
|
pull_request_target:
|
|
types: [closed, labeled]
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
inputs:
|
|
pr_number:
|
|
description: 'PR number to backport'
|
|
required: true
|
|
type: string
|
|
force_rerun:
|
|
description: 'Force rerun even if backports exist'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
|
|
jobs:
|
|
backport:
|
|
if: >
|
|
(github.event_name == 'pull_request_target' &&
|
|
github.event.pull_request.merged == true &&
|
|
contains(github.event.pull_request.labels.*.name, 'needs-backport')) ||
|
|
github.event_name == 'workflow_dispatch'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
issues: write
|
|
|
|
steps:
|
|
- name: Validate inputs for manual triggers
|
|
if: github.event_name == 'workflow_dispatch'
|
|
run: |
|
|
# Validate PR number format
|
|
if ! [[ "${{ inputs.pr_number }}" =~ ^[0-9]+$ ]]; then
|
|
echo "::error::Invalid PR number format. Must be a positive integer."
|
|
exit 1
|
|
fi
|
|
|
|
# Validate PR exists and is merged
|
|
if ! gh pr view "${{ inputs.pr_number }}" --json merged >/dev/null 2>&1; then
|
|
echo "::error::PR #${{ inputs.pr_number }} not found or inaccessible."
|
|
exit 1
|
|
fi
|
|
|
|
MERGED=$(gh pr view "${{ inputs.pr_number }}" --json merged --jq '.merged')
|
|
if [ "$MERGED" != "true" ]; then
|
|
echo "::error::PR #${{ inputs.pr_number }} is not merged. Only merged PRs can be backported."
|
|
exit 1
|
|
fi
|
|
|
|
# Validate PR has needs-backport label
|
|
if ! gh pr view "${{ inputs.pr_number }}" --json labels --jq '.labels[].name' | grep -q "needs-backport"; then
|
|
echo "::error::PR #${{ inputs.pr_number }} does not have 'needs-backport' label."
|
|
exit 1
|
|
fi
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Configure git
|
|
run: |
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
|
|
- name: Check if backports already exist
|
|
id: check-existing
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
|
run: |
|
|
# Check for existing backport PRs for this PR number
|
|
EXISTING_BACKPORTS=$(gh pr list --state all --search "backport-${PR_NUMBER}-to" --json title,headRefName,baseRefName | jq -r '.[].headRefName')
|
|
|
|
if [ -z "$EXISTING_BACKPORTS" ]; then
|
|
echo "skip=false" >> $GITHUB_OUTPUT
|
|
exit 0
|
|
fi
|
|
|
|
# For manual triggers with force_rerun, proceed anyway
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_rerun }}" = "true" ]; then
|
|
echo "skip=false" >> $GITHUB_OUTPUT
|
|
echo "::warning::Force rerun requested - existing backports will be updated"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Found existing backport PRs:"
|
|
echo "$EXISTING_BACKPORTS"
|
|
echo "skip=true" >> $GITHUB_OUTPUT
|
|
echo "::warning::Backport PRs already exist for PR #${PR_NUMBER}, skipping to avoid duplicates"
|
|
|
|
- name: Extract version labels
|
|
if: steps.check-existing.outputs.skip != 'true'
|
|
id: versions
|
|
run: |
|
|
# Extract version labels (e.g., "1.24", "1.22")
|
|
VERSIONS=""
|
|
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
# For manual triggers, get labels from the PR
|
|
LABELS=$(gh pr view ${{ inputs.pr_number }} --json labels | jq -r '.labels[].name')
|
|
else
|
|
# For automatic triggers, extract from PR event
|
|
LABELS='${{ toJSON(github.event.pull_request.labels) }}'
|
|
LABELS=$(echo "$LABELS" | jq -r '.[].name')
|
|
fi
|
|
|
|
for label in $LABELS; do
|
|
# Match version labels like "1.24" (major.minor only)
|
|
if [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
|
# Validate the branch exists before adding to list
|
|
if git ls-remote --exit-code origin "core/${label}" >/dev/null 2>&1; then
|
|
VERSIONS="${VERSIONS}${label} "
|
|
else
|
|
echo "::warning::Label '${label}' found but branch 'core/${label}' does not exist"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [ -z "$VERSIONS" ]; then
|
|
echo "::error::No version labels found (e.g., 1.24, 1.22)"
|
|
exit 1
|
|
fi
|
|
|
|
echo "versions=${VERSIONS}" >> $GITHUB_OUTPUT
|
|
echo "Found version labels: ${VERSIONS}"
|
|
|
|
- name: Backport commits
|
|
if: steps.check-existing.outputs.skip != 'true'
|
|
id: backport
|
|
env:
|
|
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
|
run: |
|
|
FAILED=""
|
|
SUCCESS=""
|
|
|
|
# Get PR data for manual triggers
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json title,mergeCommit)
|
|
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
|
|
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
|
|
else
|
|
PR_TITLE="${{ github.event.pull_request.title }}"
|
|
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
|
fi
|
|
|
|
for version in ${{ steps.versions.outputs.versions }}; do
|
|
echo "::group::Backporting to core/${version}"
|
|
|
|
TARGET_BRANCH="core/${version}"
|
|
BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${version}"
|
|
|
|
# Fetch target branch (fail if doesn't exist)
|
|
if ! git fetch origin "${TARGET_BRANCH}"; then
|
|
echo "::error::Target branch ${TARGET_BRANCH} does not exist"
|
|
FAILED="${FAILED}${version}:branch-missing "
|
|
echo "::endgroup::"
|
|
continue
|
|
fi
|
|
|
|
# Create backport branch
|
|
git checkout -b "${BACKPORT_BRANCH}" "origin/${TARGET_BRANCH}"
|
|
|
|
# Try cherry-pick
|
|
if git cherry-pick "${MERGE_COMMIT}"; then
|
|
git push origin "${BACKPORT_BRANCH}"
|
|
SUCCESS="${SUCCESS}${version}:${BACKPORT_BRANCH} "
|
|
echo "Successfully created backport branch: ${BACKPORT_BRANCH}"
|
|
# Return to main (keep the branch, we need it for PR)
|
|
git checkout main
|
|
else
|
|
# Get conflict info
|
|
CONFLICTS=$(git diff --name-only --diff-filter=U | tr '\n' ',')
|
|
git cherry-pick --abort
|
|
|
|
echo "::error::Cherry-pick failed due to conflicts"
|
|
FAILED="${FAILED}${version}:conflicts:${CONFLICTS} "
|
|
|
|
# Clean up the failed branch
|
|
git checkout main
|
|
git branch -D "${BACKPORT_BRANCH}"
|
|
fi
|
|
|
|
echo "::endgroup::"
|
|
done
|
|
|
|
echo "success=${SUCCESS}" >> $GITHUB_OUTPUT
|
|
echo "failed=${FAILED}" >> $GITHUB_OUTPUT
|
|
|
|
if [ -n "${FAILED}" ]; then
|
|
exit 1
|
|
fi
|
|
|
|
- name: Create PR for each successful backport
|
|
if: steps.check-existing.outputs.skip != 'true' && steps.backport.outputs.success
|
|
env:
|
|
GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
|
|
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number || github.event.pull_request.number }}
|
|
run: |
|
|
# Get PR data for manual triggers
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json title,author)
|
|
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
|
|
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
|
|
else
|
|
PR_TITLE="${{ github.event.pull_request.title }}"
|
|
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
|
fi
|
|
|
|
for backport in ${{ steps.backport.outputs.success }}; do
|
|
IFS=':' read -r version branch <<< "${backport}"
|
|
|
|
if PR_URL=$(gh pr create \
|
|
--base "core/${version}" \
|
|
--head "${branch}" \
|
|
--title "[backport ${version}] ${PR_TITLE}" \
|
|
--body "Backport of #${PR_NUMBER} to \`core/${version}\`"$'\n\n'"Automatically created by backport workflow." \
|
|
--label "backport" 2>&1); then
|
|
|
|
# Extract PR number from URL
|
|
PR_NUM=$(echo "${PR_URL}" | grep -o '[0-9]*$')
|
|
|
|
if [ -n "${PR_NUM}" ]; then
|
|
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Successfully backported to #${PR_NUM}"
|
|
fi
|
|
else
|
|
echo "::error::Failed to create PR for ${version}: ${PR_URL}"
|
|
# Still try to comment on the original PR about the failure
|
|
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`core/${version}\`. Please create the PR manually from branch \`${branch}\`"
|
|
fi
|
|
done
|
|
|
|
- name: Comment on failures
|
|
if: steps.check-existing.outputs.skip != 'true' && failure() && steps.backport.outputs.failed
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
PR_DATA=$(gh pr view ${{ inputs.pr_number }} --json author,mergeCommit)
|
|
PR_NUMBER="${{ inputs.pr_number }}"
|
|
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login')
|
|
MERGE_COMMIT=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid')
|
|
else
|
|
PR_NUMBER="${{ github.event.pull_request.number }}"
|
|
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
|
MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}"
|
|
fi
|
|
|
|
for failure in ${{ steps.backport.outputs.failed }}; do
|
|
IFS=':' read -r version reason conflicts <<< "${failure}"
|
|
|
|
if [ "${reason}" = "branch-missing" ]; then
|
|
gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`core/${version}\` does not exist"
|
|
|
|
elif [ "${reason}" = "conflicts" ]; then
|
|
# Convert comma-separated conflicts back to newlines for display
|
|
CONFLICTS_LIST=$(echo "${conflicts}" | tr ',' '\n' | sed 's/^/- /')
|
|
|
|
COMMENT_BODY="@${PR_AUTHOR} Backport to \`core/${version}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`core/${version}\` branch."$'\n\n'"<details><summary>Conflicting files</summary>"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"</details>"
|
|
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}"
|
|
fi
|
|
done
|