diff --git a/.github/workflows/pr-backport.yaml b/.github/workflows/pr-backport.yaml index 13e6dd74e8..1c9a292305 100644 --- a/.github/workflows/pr-backport.yaml +++ b/.github/workflows/pr-backport.yaml @@ -95,41 +95,61 @@ jobs: echo "skip=true" >> $GITHUB_OUTPUT echo "::warning::Backport PRs already exist for PR #${PR_NUMBER}, skipping to avoid duplicates" - - name: Extract version labels + - name: Collect backport targets if: steps.check-existing.outputs.skip != 'true' - id: versions + id: targets run: | - # Extract version labels (e.g., "1.24", "1.22") - VERSIONS="" - + TARGETS=() + declare -A SEEN=() + 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)" + add_target() { + local label="$1" + local target="$2" + + if [ -z "$target" ]; then + return + fi + + target=$(echo "$target" | xargs) + + if [ -z "$target" ] || [ -n "${SEEN[$target]}" ]; then + return + fi + + if git ls-remote --exit-code origin "$target" >/dev/null 2>&1; then + TARGETS+=("$target") + SEEN["$target"]=1 + else + echo "::warning::Label '${label}' references missing branch '${target}'" + fi + } + + while IFS= read -r label; do + [ -z "$label" ] && continue + + if [[ "$label" =~ ^branch:(.+)$ ]]; then + add_target "$label" "${BASH_REMATCH[1]}" + elif [[ "$label" =~ ^backport:(.+)$ ]]; then + add_target "$label" "${BASH_REMATCH[1]}" + elif [[ "$label" =~ ^[0-9]+\.[0-9]+$ ]]; then + add_target "$label" "core/${label}" + fi + done <<< "$LABELS" + + if [ "${#TARGETS[@]}" -eq 0 ]; then + echo "::error::No backport targets found (use labels like '1.24' or 'branch:release/hotfix')" exit 1 fi - echo "versions=${VERSIONS}" >> $GITHUB_OUTPUT - echo "Found version labels: ${VERSIONS}" + echo "targets=${TARGETS[*]}" >> $GITHUB_OUTPUT + echo "Found backport targets: ${TARGETS[*]}" - name: Backport commits if: steps.check-existing.outputs.skip != 'true' @@ -150,16 +170,17 @@ jobs: MERGE_COMMIT="${{ github.event.pull_request.merge_commit_sha }}" fi - for version in ${{ steps.versions.outputs.versions }}; do - echo "::group::Backporting to core/${version}" + for target in ${{ steps.targets.outputs.targets }}; do + TARGET_BRANCH="${target}" + SAFE_TARGET=$(echo "$TARGET_BRANCH" | tr '/' '-') + BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}" - TARGET_BRANCH="core/${version}" - BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${version}" + echo "::group::Backporting to ${TARGET_BRANCH}" # 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 " + FAILED="${FAILED}${TARGET_BRANCH}:branch-missing " echo "::endgroup::" continue fi @@ -170,7 +191,7 @@ jobs: # Try cherry-pick if git cherry-pick "${MERGE_COMMIT}"; then git push origin "${BACKPORT_BRANCH}" - SUCCESS="${SUCCESS}${version}:${BACKPORT_BRANCH} " + SUCCESS="${SUCCESS}${TARGET_BRANCH}:${BACKPORT_BRANCH} " echo "Successfully created backport branch: ${BACKPORT_BRANCH}" # Return to main (keep the branch, we need it for PR) git checkout main @@ -180,7 +201,7 @@ jobs: git cherry-pick --abort echo "::error::Cherry-pick failed due to conflicts" - FAILED="${FAILED}${version}:conflicts:${CONFLICTS} " + FAILED="${FAILED}${TARGET_BRANCH}:conflicts:${CONFLICTS} " # Clean up the failed branch git checkout main @@ -214,13 +235,13 @@ jobs: fi for backport in ${{ steps.backport.outputs.success }}; do - IFS=':' read -r version branch <<< "${backport}" + IFS=':' read -r target branch <<< "${backport}" if PR_URL=$(gh pr create \ - --base "core/${version}" \ + --base "${target}" \ --head "${branch}" \ - --title "[backport ${version}] ${PR_TITLE}" \ - --body "Backport of #${PR_NUMBER} to \`core/${version}\`"$'\n\n'"Automatically created by backport workflow." \ + --title "[backport ${target}] ${PR_TITLE}" \ + --body "Backport of #${PR_NUMBER} to \`${target}\`"$'\n\n'"Automatically created by backport workflow." \ --label "backport" 2>&1); then # Extract PR number from URL @@ -230,9 +251,9 @@ jobs: 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}" + echo "::error::Failed to create PR for ${target}: ${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}\`" + gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport branch created but PR creation failed for \`${target}\`. Please create the PR manually from branch \`${branch}\`" fi done @@ -253,16 +274,16 @@ jobs: fi for failure in ${{ steps.backport.outputs.failed }}; do - IFS=':' read -r version reason conflicts <<< "${failure}" + IFS=':' read -r target reason conflicts <<< "${failure}" if [ "${reason}" = "branch-missing" ]; then - gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`core/${version}\` does not exist" + gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`${target}\` 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'"
Conflicting files"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"
" + COMMENT_BODY="@${PR_AUTHOR} Backport to \`${target}\` failed: Merge conflicts detected."$'\n\n'"Please manually cherry-pick commit \`${MERGE_COMMIT}\` to the \`${target}\` branch."$'\n\n'"
Conflicting files"$'\n\n'"${CONFLICTS_LIST}"$'\n\n'"
" gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" fi done