Skip to content

build(deps): bump the java group in /java with 10 updates (#20140) #571

build(deps): bump the java group in /java with 10 updates (#20140)

build(deps): bump the java group in /java with 10 updates (#20140) #571

Workflow file for this run

name: Backport and Forwardport PRs
permissions: read-all
on:
push:
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_name == 'push'
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
with:
egress-policy: audit
- uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.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
env:
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
USER_ID=$(gh api "/users/${APP_SLUG}[bot]" --jq .id)
echo "user-id=${USER_ID}" >> "$GITHUB_OUTPUT"
- name: Configure Git
env:
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
APP_USER_ID: ${{ steps.get-user-id.outputs.user-id }}
run: |
git config --global user.name "${APP_SLUG}[bot]"
git config --global user.email "${APP_USER_ID}+${APP_SLUG}[bot]@users.noreply.github.com"
git config --global merge.conflictStyle zdiff3
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false
token: ${{ steps.app-token.outputs.token }}
- name: Perform backport/forwardport
env:
APP_SLUG: ${{ steps.app-token.outputs.app-slug }}
APP_USER_ID: ${{ steps.get-user-id.outputs.user-id }}
EVENT_NAME: ${{ github.event_name }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
INPUT_DRY_RUN: ${{ inputs.dry_run || 'false' }}
INPUT_PR_NUMBER: ${{ inputs.pr_number || '' }}
run: |
GIT_AUTHOR="${APP_SLUG}[bot] <${APP_USER_ID}+${APP_SLUG}[bot]@users.noreply.github.com>"
GIT_AUTH_HEADER="AUTHORIZATION: basic $(printf 'x-access-token:%s' "$GH_TOKEN" | base64 | tr -d '\n')"
# Determine PR numbers and dry-run mode based on trigger type.
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
PR_NUMBERS=("$INPUT_PR_NUMBER")
DRY_RUN="$INPUT_DRY_RUN"
else
# Find the merged main PRs represented by this push so each one can be
# backported or forwardported according to its labels.
PR_NUMBERS_FILE=$(mktemp)
if ! (
set -o pipefail
jq -r '.commits[].id' "$GITHUB_EVENT_PATH" |
while IFS= read -r COMMIT_SHA; do
if [[ ! "$COMMIT_SHA" =~ ^[0-9a-f]{40}$ ]]; then
echo "Skipping unexpected commit SHA: $COMMIT_SHA" >&2
continue
fi
if ! gh api \
-H "Accept: application/vnd.github+json" \
"repos/${GITHUB_REPOSITORY}/commits/${COMMIT_SHA}/pulls" \
--jq '.[] | select(.merged_at != null and .base.ref == "main") | .number'; then
echo "Error: failed to look up pull requests for commit $COMMIT_SHA" >&2
exit 1
fi
done |
sort -n -u > "$PR_NUMBERS_FILE"
); then
rm -f "$PR_NUMBERS_FILE"
exit 1
fi
mapfile -t PR_NUMBERS < "$PR_NUMBERS_FILE"
rm -f "$PR_NUMBERS_FILE"
DRY_RUN="false"
fi
if [ "${#PR_NUMBERS[@]}" -eq 0 ]; then
echo "No merged pull requests found for this push"
exit 0
fi
if [ "$DRY_RUN" = "true" ]; then
echo "🔍 DRY RUN MODE ENABLED - No changes will be made"
echo "=================================================="
fi
for PR_NUMBER in "${PR_NUMBERS[@]}"; do
# 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: " || true)
BACKPORT_BRANCHES=${BACKPORT_BRANCHES//Backport to: /}
FORWARDPORT_BRANCHES=$(echo "$LABELS" | grep "^Forwardport to: " || true)
FORWARDPORT_BRANCHES=${FORWARDPORT_BRANCHES//Forwardport to: /}
# 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="$GIT_AUTHOR" -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 -c http.https://github.com/.extraheader="$GIT_AUTH_HEADER" 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)
if ! 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) || [ -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} 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 " ------------------------------------------------"
while IFS= read -r line; do
echo " $line"
done <<< "$CONFLICT_COMMENT"
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="$GIT_AUTHOR" -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 -c http.https://github.com/.extraheader="$GIT_AUTH_HEADER" 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)
if ! 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) || [ -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} 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 " ------------------------------------------------"
while IFS= read -r line; do
echo " $line"
done <<< "$CONFLICT_COMMENT"
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"
done