Bump the all group with 9 updates #159
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Backport and Forwardport PRs | |
| permissions: read-all | |
| on: | |
| pull_request_target: | |
| types: [closed] | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| pr_number: | |
| description: 'Pull Request number to backport/forwardport' | |
| required: true | |
| type: number | |
| dry_run: | |
| description: 'Dry run mode - show what would happen without making changes' | |
| required: false | |
| type: boolean | |
| default: true | |
| jobs: | |
| backport: | |
| name: Backport/Forwardport PR | |
| permissions: | |
| pull-requests: write | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true | |
| steps: | |
| - name: Harden the runner (Audit all outbound calls) | |
| uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 | |
| id: app-token | |
| with: | |
| app-id: ${{ vars.APP_ID }} | |
| private-key: ${{ secrets.APP_PRIVATE_KEY }} | |
| - name: Get GitHub App User ID | |
| id: get-user-id | |
| run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT" | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| - name: Configure Git | |
| run: | | |
| git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]' | |
| git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com' | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ steps.app-token.outputs.token }} | |
| - name: Perform backport/forwardport | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| run: | | |
| # Determine PR number and dry-run mode based on trigger type | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| PR_NUMBER="${{ inputs.pr_number }}" | |
| DRY_RUN="${{ inputs.dry_run }}" | |
| else | |
| PR_NUMBER="${{ github.event.pull_request.number }}" | |
| DRY_RUN="false" | |
| fi | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "🔍 DRY RUN MODE ENABLED - No changes will be made" | |
| echo "==================================================" | |
| fi | |
| # Fetch PR details from API | |
| PR_DATA=$(gh pr view "$PR_NUMBER" --json number,title,author,mergeCommit,state) | |
| # Extract PR details | |
| PR_TITLE=$(echo "$PR_DATA" | jq -r '.title') | |
| PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login') | |
| MERGE_COMMIT_SHA=$(echo "$PR_DATA" | jq -r '.mergeCommit.oid // empty') | |
| PR_STATE=$(echo "$PR_DATA" | jq -r '.state') | |
| # Validate PR is merged | |
| if [ "$PR_STATE" != "MERGED" ]; then | |
| echo "Error: PR #$PR_NUMBER is not merged (state: $PR_STATE). Cannot backport unmerged PRs." | |
| exit 1 | |
| fi | |
| echo "Processing PR #$PR_NUMBER: $PR_TITLE" | |
| echo "Author: $PR_AUTHOR" | |
| echo "Merge commit: $MERGE_COMMIT_SHA" | |
| # Get all labels from the PR | |
| LABELS=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name') | |
| # Extract backport branches | |
| BACKPORT_BRANCHES=$(echo "$LABELS" | grep "^Backport to: " | sed 's/^Backport to: //' || true) | |
| FORWARDPORT_BRANCHES=$(echo "$LABELS" | grep "^Forwardport to: " | sed 's/^Forwardport to: //' || true) | |
| # Extract other labels (excluding backport/forwardport labels) | |
| OTHER_LABELS=$(echo "$LABELS" | grep -v "^Backport to: " | grep -v "^Forwardport to: " | jq -R -s -c 'split("\n") | map(select(length > 0))' || echo '[]') | |
| if [ -n "$BACKPORT_BRANCHES" ]; then | |
| echo "Will backport PR #$PR_NUMBER to branches: $BACKPORT_BRANCHES" | |
| fi | |
| if [ -n "$FORWARDPORT_BRANCHES" ]; then | |
| echo "Will forwardport PR #$PR_NUMBER to branches: $FORWARDPORT_BRANCHES" | |
| fi | |
| # Backport processing | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "🔍 DRY RUN: Backport Processing" | |
| echo "================================" | |
| fi | |
| # Process each backport branch | |
| while IFS= read -r BRANCH; do | |
| [ -z "$BRANCH" ] && continue | |
| echo "Processing backport to branch: $BRANCH" | |
| PORT_TYPE="backport" | |
| NEW_BRANCH="${PORT_TYPE}-${PR_NUMBER}-to-${BRANCH}" | |
| # Fetch the target branch | |
| git fetch origin "$BRANCH:$BRANCH" || { | |
| echo "Error: Failed to fetch branch $BRANCH" | |
| continue | |
| } | |
| # Create and checkout new branch from target branch | |
| git checkout -b "$NEW_BRANCH" "$BRANCH" || { | |
| echo "Error: Failed to create branch $NEW_BRANCH" | |
| continue | |
| } | |
| # Attempt cherry-pick | |
| CONFLICT=false | |
| if ! git cherry-pick -m 1 "$MERGE_COMMIT_SHA" 2>&1; then | |
| # Check if there are conflicts | |
| if git status | grep -q "Unmerged paths\|both modified"; then | |
| echo "Conflicts detected during cherry-pick" | |
| CONFLICT=true | |
| # Stage all changes | |
| git add . | |
| # Commit with conflict message, setting bot as author | |
| git commit --author='${{ steps.app-token.outputs.app-slug }}[bot] <${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>' -m "Cherry-pick $MERGE_COMMIT_SHA with conflicts" || { | |
| echo "Error: Failed to commit conflicts" | |
| git checkout main | |
| git branch -D "$NEW_BRANCH" 2>/dev/null || true | |
| continue | |
| } | |
| else | |
| echo "Error: Cherry-pick failed with non-conflict error" | |
| git cherry-pick --abort 2>/dev/null || true | |
| git checkout main | |
| git branch -D "$NEW_BRANCH" 2>/dev/null || true | |
| continue | |
| fi | |
| else | |
| # Cherry-pick succeeded, amend to update author | |
| git commit --amend --no-edit --reset-author || { | |
| echo "Error: Failed to amend commit" | |
| git checkout main | |
| git branch -D "$NEW_BRANCH" 2>/dev/null || true | |
| continue | |
| } | |
| fi | |
| # Push the new branch | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo " [DRY RUN] Would push branch: $NEW_BRANCH" | |
| echo " Command: git push -f origin $NEW_BRANCH" | |
| NEW_PR_NUMBER="<dry-run>" | |
| else | |
| git push -f origin "$NEW_BRANCH" || { | |
| echo "Error: Failed to push branch $NEW_BRANCH" | |
| git checkout main | |
| git branch -D "$NEW_BRANCH" 2>/dev/null || true | |
| continue | |
| } | |
| # Create PR body | |
| PR_BODY="## Description"$'\n'"This is a $PORT_TYPE of #${PR_NUMBER}" | |
| # Determine if PR should be draft | |
| DRAFT_FLAG="" | |
| if [ "$CONFLICT" = true ]; then | |
| DRAFT_FLAG="--draft" | |
| fi | |
| # Build labels JSON array from OTHER_LABELS and add port type specific labels | |
| if [ "$CONFLICT" = true ]; then | |
| LABELS_JSON=$(echo "$OTHER_LABELS" | jq -c '. + ["Merge Conflict", "Skip CI", "Backport"]') | |
| else | |
| LABELS_JSON=$(echo "$OTHER_LABELS" | jq -c '. + ["Backport"]') | |
| fi | |
| # Convert JSON array to comma-separated list for gh pr create --label | |
| LABELS_LIST=$(echo "$LABELS_JSON" | jq -r 'join(",")') | |
| echo " Creating pull request..." | |
| # Create the pull request with labels (returns URL like https://github.com/owner/repo/pull/123) | |
| PR_URL=$(gh pr create \ | |
| --title "[$BRANCH] $PR_TITLE (#$PR_NUMBER)" \ | |
| --body "$PR_BODY" \ | |
| --base "$BRANCH" \ | |
| --head "$NEW_BRANCH" \ | |
| --label "$LABELS_LIST" \ | |
| $DRAFT_FLAG 2>&1) | |
| if [ $? -ne 0 ] || [ -z "$PR_URL" ]; then | |
| echo "Error: Failed to create PR for branch $NEW_BRANCH" | |
| echo "$PR_URL" | |
| git checkout main | |
| continue | |
| fi | |
| # Extract PR number from URL using gh pr view | |
| NEW_PR_NUMBER=$(gh pr view "$PR_URL" --json number --jq '.number') | |
| echo "Created backport PR #$NEW_PR_NUMBER ($PR_URL)" | |
| fi | |
| # Build labels JSON array for dry-run display | |
| if [ "$CONFLICT" = true ]; then | |
| LABELS_JSON=$(echo "$OTHER_LABELS" | jq -c '. + ["Merge Conflict", "Skip CI", "Backport"]') | |
| else | |
| LABELS_JSON=$(echo "$OTHER_LABELS" | jq -c '. + ["Backport"]') | |
| fi | |
| # Display PR information (for both dry-run and real mode) | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "" | |
| echo " [DRY RUN] Would create PR with:" | |
| echo " --------------------------------" | |
| echo " Title: [$BRANCH] $PR_TITLE (#$PR_NUMBER)" | |
| echo " Body: ## Description" | |
| echo " This is a $PORT_TYPE of #${PR_NUMBER}" | |
| echo " Base: $BRANCH" | |
| echo " Head: $NEW_BRANCH" | |
| if [ "$CONFLICT" = true ]; then | |
| echo " Draft: true (due to conflicts)" | |
| else | |
| echo " Draft: false" | |
| fi | |
| echo " Repository: ${{ github.repository }}" | |
| echo "" | |
| echo " [DRY RUN] Would add labels:" | |
| echo "$LABELS_JSON" | jq -r '.[]' | while read -r label; do | |
| echo " - $label" | |
| done | |
| fi | |
| # Add conflict comment if there were conflicts | |
| if [ "$CONFLICT" = true ]; then | |
| CONFLICT_COMMENT="Hello @${PR_AUTHOR}, there are conflicts in this ${PORT_TYPE}."$'\n\n' | |
| CONFLICT_COMMENT+="Please address them in order to merge this Pull Request. You can execute the snippet below to reset your branch and resolve the conflict manually."$'\n\n' | |
| CONFLICT_COMMENT+="Make sure you replace \`origin\` by the name of the ${{ github.repository_owner }}/${{ github.event.repository.name }} remote"$'\n' | |
| CONFLICT_COMMENT+="\`\`\`"$'\n' | |
| CONFLICT_COMMENT+="git fetch --all"$'\n' | |
| CONFLICT_COMMENT+="gh pr checkout ${NEW_PR_NUMBER}"$'\n' | |
| CONFLICT_COMMENT+="git reset --hard origin/${BRANCH}"$'\n' | |
| CONFLICT_COMMENT+="git cherry-pick -m 1 ${MERGE_COMMIT_SHA}"$'\n' | |
| CONFLICT_COMMENT+="\`\`\`" | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "" | |
| echo " [DRY RUN] Would add conflict resolution comment:" | |
| echo " ------------------------------------------------" | |
| echo "$CONFLICT_COMMENT" | sed 's/^/ /' | |
| else | |
| if ! OUTPUT=$(gh pr comment "$NEW_PR_NUMBER" --body "$CONFLICT_COMMENT" 2>&1); then | |
| echo "Warning: Could not add conflict resolution comment" | |
| echo " Error: $OUTPUT" | |
| fi | |
| fi | |
| fi | |
| # Get reviewers from original PR and build JSON array | |
| REVIEWERS_JSON=$(gh api "repos/${{ github.repository }}/pulls/${PR_NUMBER}/requested_reviewers" \ | |
| --jq '[.users[].login, .teams[].slug] + ["'"$PR_AUTHOR"'"]' 2>/dev/null || echo '["'"$PR_AUTHOR"'"]') | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "" | |
| echo " [DRY RUN] Would request reviews from:" | |
| echo "$REVIEWERS_JSON" | jq -r '.[]' | while read -r reviewer; do | |
| echo " - $reviewer" | |
| done | |
| else | |
| # Convert JSON array to CSV and request reviewers | |
| REVIEWERS_CSV=$(echo "$REVIEWERS_JSON" | jq -r '@csv') | |
| if ! OUTPUT=$(gh pr edit "$NEW_PR_NUMBER" --add-reviewer "$REVIEWERS_CSV" 2>&1); then | |
| echo "Note: Could not add some reviewers (may include PR author or have insufficient permissions)" | |
| echo " Error: $OUTPUT" | |
| fi | |
| fi | |
| # Return to main branch for next iteration | |
| git checkout main | |
| done <<< "$BACKPORT_BRANCHES" | |
| # Forwardport processing | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "🔍 DRY RUN: Forwardport Processing" | |
| echo "===================================" | |
| fi | |
| # Process each forwardport branch | |
| while IFS= read -r BRANCH; do | |
| [ -z "$BRANCH" ] && continue | |
| echo "Processing forwardport to branch: $BRANCH" | |
| PORT_TYPE="forwardport" | |
| NEW_BRANCH="${PORT_TYPE}-${PR_NUMBER}-to-${BRANCH}" | |
| # Fetch the target branch | |
| git fetch origin "$BRANCH:$BRANCH" || { | |
| echo "Error: Failed to fetch branch $BRANCH" | |
| continue | |
| } | |
| # Create and checkout new branch from target branch | |
| git checkout -b "$NEW_BRANCH" "$BRANCH" || { | |
| echo "Error: Failed to create branch $NEW_BRANCH" | |
| continue | |
| } | |
| # Attempt cherry-pick | |
| CONFLICT=false | |
| if ! git cherry-pick -m 1 "$MERGE_COMMIT_SHA" 2>&1; then | |
| # Check if there are conflicts | |
| if git status | grep -q "Unmerged paths\|both modified"; then | |
| echo "Conflicts detected during cherry-pick" | |
| CONFLICT=true | |
| # Stage all changes | |
| git add . | |
| # Commit with conflict message, setting bot as author | |
| git commit --author='${{ steps.app-token.outputs.app-slug }}[bot] <${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>' -m "Cherry-pick $MERGE_COMMIT_SHA with conflicts" || { | |
| echo "Error: Failed to commit conflicts" | |
| git checkout main | |
| git branch -D "$NEW_BRANCH" 2>/dev/null || true | |
| continue | |
| } | |
| else | |
| echo "Error: Cherry-pick failed with non-conflict error" | |
| git cherry-pick --abort 2>/dev/null || true | |
| git checkout main | |
| git branch -D "$NEW_BRANCH" 2>/dev/null || true | |
| continue | |
| fi | |
| else | |
| # Cherry-pick succeeded, amend to update author | |
| git commit --amend --no-edit --reset-author || { | |
| echo "Error: Failed to amend commit" | |
| git checkout main | |
| git branch -D "$NEW_BRANCH" 2>/dev/null || true | |
| continue | |
| } | |
| fi | |
| # Push the new branch | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo " [DRY RUN] Would push branch: $NEW_BRANCH" | |
| echo " Command: git push -f origin $NEW_BRANCH" | |
| NEW_PR_NUMBER="<dry-run>" | |
| else | |
| git push -f origin "$NEW_BRANCH" || { | |
| echo "Error: Failed to push branch $NEW_BRANCH" | |
| git checkout main | |
| git branch -D "$NEW_BRANCH" 2>/dev/null || true | |
| continue | |
| } | |
| # Create PR body | |
| PR_BODY="## Description"$'\n'"This is a $PORT_TYPE of #${PR_NUMBER}" | |
| # Determine if PR should be draft | |
| DRAFT_FLAG="" | |
| if [ "$CONFLICT" = true ]; then | |
| DRAFT_FLAG="--draft" | |
| fi | |
| # Build labels JSON array from OTHER_LABELS and add port type specific labels | |
| if [ "$CONFLICT" = true ]; then | |
| LABELS_JSON=$(echo "$OTHER_LABELS" | jq -c '. + ["Merge Conflict", "Skip CI", "Forwardport"]') | |
| else | |
| LABELS_JSON=$(echo "$OTHER_LABELS" | jq -c '. + ["Forwardport"]') | |
| fi | |
| # Convert JSON array to comma-separated list for gh pr create --label | |
| LABELS_LIST=$(echo "$LABELS_JSON" | jq -r 'join(",")') | |
| echo " Creating pull request..." | |
| # Create the pull request with labels (returns URL like https://github.com/owner/repo/pull/123) | |
| PR_URL=$(gh pr create \ | |
| --title "[$BRANCH] $PR_TITLE (#$PR_NUMBER)" \ | |
| --body "$PR_BODY" \ | |
| --base "$BRANCH" \ | |
| --head "$NEW_BRANCH" \ | |
| --label "$LABELS_LIST" \ | |
| $DRAFT_FLAG 2>&1) | |
| if [ $? -ne 0 ] || [ -z "$PR_URL" ]; then | |
| echo "Error: Failed to create PR for branch $NEW_BRANCH" | |
| echo "$PR_URL" | |
| git checkout main | |
| continue | |
| fi | |
| # Extract PR number from URL using gh pr view | |
| NEW_PR_NUMBER=$(gh pr view "$PR_URL" --json number --jq '.number') | |
| echo "Created forwardport PR #$NEW_PR_NUMBER ($PR_URL)" | |
| fi | |
| # Build labels JSON array for dry-run display | |
| if [ "$CONFLICT" = true ]; then | |
| LABELS_JSON=$(echo "$OTHER_LABELS" | jq -c '. + ["Merge Conflict", "Skip CI", "Forwardport"]') | |
| else | |
| LABELS_JSON=$(echo "$OTHER_LABELS" | jq -c '. + ["Forwardport"]') | |
| fi | |
| # Display PR information (for both dry-run and real mode) | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "" | |
| echo " [DRY RUN] Would create PR with:" | |
| echo " --------------------------------" | |
| echo " Title: [$BRANCH] $PR_TITLE (#$PR_NUMBER)" | |
| echo " Body: ## Description" | |
| echo " This is a $PORT_TYPE of #${PR_NUMBER}" | |
| echo " Base: $BRANCH" | |
| echo " Head: $NEW_BRANCH" | |
| if [ "$CONFLICT" = true ]; then | |
| echo " Draft: true (due to conflicts)" | |
| else | |
| echo " Draft: false" | |
| fi | |
| echo " Repository: ${{ github.repository }}" | |
| echo "" | |
| echo " [DRY RUN] Would add labels:" | |
| echo "$LABELS_JSON" | jq -r '.[]' | while read -r label; do | |
| echo " - $label" | |
| done | |
| fi | |
| # Add conflict comment if there were conflicts | |
| if [ "$CONFLICT" = true ]; then | |
| CONFLICT_COMMENT="Hello @${PR_AUTHOR}, there are conflicts in this ${PORT_TYPE}."$'\n\n' | |
| CONFLICT_COMMENT+="Please address them in order to merge this Pull Request. You can execute the snippet below to reset your branch and resolve the conflict manually."$'\n\n' | |
| CONFLICT_COMMENT+="Make sure you replace \`origin\` by the name of the ${{ github.repository_owner }}/${{ github.event.repository.name }} remote"$'\n' | |
| CONFLICT_COMMENT+="\`\`\`"$'\n' | |
| CONFLICT_COMMENT+="git fetch --all"$'\n' | |
| CONFLICT_COMMENT+="gh pr checkout ${NEW_PR_NUMBER} -R ${{ github.repository }}"$'\n' | |
| CONFLICT_COMMENT+="git reset --hard origin/${BRANCH}"$'\n' | |
| CONFLICT_COMMENT+="git cherry-pick -m 1 ${MERGE_COMMIT_SHA}"$'\n' | |
| CONFLICT_COMMENT+="\`\`\`" | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "" | |
| echo " [DRY RUN] Would add conflict resolution comment:" | |
| echo " ------------------------------------------------" | |
| echo "$CONFLICT_COMMENT" | sed 's/^/ /' | |
| else | |
| if ! OUTPUT=$(gh pr comment "$NEW_PR_NUMBER" --body "$CONFLICT_COMMENT" 2>&1); then | |
| echo "Warning: Could not add conflict resolution comment" | |
| echo " Error: $OUTPUT" | |
| fi | |
| fi | |
| fi | |
| # Get reviewers from original PR and build JSON array | |
| REVIEWERS_JSON=$(gh api "repos/${{ github.repository }}/pulls/${PR_NUMBER}/requested_reviewers" \ | |
| --jq '[.users[].login, .teams[].slug] + ["'"$PR_AUTHOR"'"]' 2>/dev/null || echo '["'"$PR_AUTHOR"'"]') | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "" | |
| echo " [DRY RUN] Would request reviews from:" | |
| echo "$REVIEWERS_JSON" | jq -r '.[]' | while read -r reviewer; do | |
| echo " - $reviewer" | |
| done | |
| else | |
| # Convert JSON array to CSV and request reviewers | |
| REVIEWERS_CSV=$(echo "$REVIEWERS_JSON" | jq -r '@csv') | |
| if ! OUTPUT=$(gh pr edit "$NEW_PR_NUMBER" --add-reviewer "$REVIEWERS_CSV" 2>&1); then | |
| echo "Note: Could not add some reviewers (may include PR author or have insufficient permissions)" | |
| echo " Error: $OUTPUT" | |
| fi | |
| fi | |
| # Return to main branch for next iteration | |
| git checkout main | |
| done <<< "$FORWARDPORT_BRANCHES" |