vigyasharma commented on code in PR #14738:
URL: https://github.com/apache/lucene/pull/14738#discussion_r2118736897


##########
.github/workflows/backport.yml:
##########
@@ -0,0 +1,305 @@
+name: Backport PR
+
+on:
+  pull_request:
+    types: [closed]
+
+jobs:
+  backport:
+    if: github.event.pull_request.merged == true
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+      pull-requests: write
+      statuses: read
+      checks: read
+      actions: read
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Setup Git
+        run: |
+          git config user.name "github-actions[bot]"
+          git config user.email "github-actions[bot]@users.noreply.github.com"
+
+      - name: Backport
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          PR_NUMBER: ${{ github.event.pull_request.number }}
+          MERGE_COMMIT_SHA: ${{ github.event.pull_request.merge_commit_sha }}
+          DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+        run: |
+          set -e
+          
+          # Cache PR data early
+          echo "📊 Fetching PR data..."
+          PR_DATA=$(gh pr view "$PR_NUMBER" --json 
title,body,labels,statusCheckRollup,commits)
+          PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
+          PR_BODY=$(echo "$PR_DATA" | jq -r '.body // "No description 
provided."')
+          PR_LABELS=$(echo "$PR_DATA" | jq -r '.labels[].name | 
select(test("^[Bb]ackport/"; "i"))')
+          
+          # Function to create GitHub comment
+          create_comment() {
+            gh pr comment "$PR_NUMBER" --body "$1"
+          }
+          
+          # Function to add label safely  
+          add_label() {
+            gh pr edit "$PR_NUMBER" --add-label "$1" 2>/dev/null || true
+          }
+          
+          # Check status checks once
+          echo "🔍 Checking PR status checks..."
+          FAILED_CHECKS=$(echo "$PR_DATA" | jq -r '.statusCheckRollup[]? | 
select(.isRequired == true and (.state == "FAILURE" or .state == "ERROR")) | 
.state' 2>/dev/null || echo "")
+          
+          if [ -n "$FAILED_CHECKS" ]; then
+            echo "❌ Required status checks failed"
+            create_comment "âš ī¸ **Automatic backports skipped**
+          
+          Some required status checks failed on this PR. Backports will not be 
created automatically.
+          
+          **Action required:**
+          1. Fix failing tests or checks
+          2. Manually create backport PRs after verification
+          3. Or re-run this workflow after fixes are merged"
+            add_label "backport-failed"
+            exit 0
+          fi
+          
+          # Early exit if no backport labels
+          if [ -z "$PR_LABELS" ]; then
+            echo "â„šī¸ No backport labels found. Nothing to do."
+            exit 0
+          fi
+          
+          echo "📋 Found backport labels: $(echo "$PR_LABELS" | tr '\n' ' ')"
+          
+          # Batch create labels
+          echo "đŸˇī¸ Ensuring backport labels exist..."
+          gh label create "backport" --description "Automated backport 
workflow" --color "0366d6" 2>/dev/null || true &
+          gh label create "backport-conflict" --description "Backport had 
conflicts" --color "d73a49" 2>/dev/null || true &
+          gh label create "backport-failed" --description "Backport failed" 
--color "d73a49" 2>/dev/null || true &
+          wait # Wait for background label creation
+          
+          # Cache all remote branches once
+          echo "đŸŒŋ Caching branch information..."
+          ALL_BRANCHES=$(git branch -r | grep -v HEAD | sed 's|.*origin/||' | 
sort)
+          
+          # Define templates in order of preference
+          declare -a target_branch_templates=(
+            "branch_{{version_us}}"

Review Comment:
   I like Stefan's suggestion of using the milestone label to find backport 
branch. We already have a bot to put that label based on `CHANGES` entry!
   
   In Lucene, a change meant for `10.3.0` will be back-ported into 
`branch_10x`. Generally, our back-port branches follow the naming scheme 
`branch_{MAJOR_VERSION}x`. Would that match any of the branch templates defined 
here?



##########
.github/workflows/backport.yml:
##########
@@ -0,0 +1,305 @@
+name: Backport PR
+
+on:
+  pull_request:
+    types: [closed]
+
+jobs:
+  backport:
+    if: github.event.pull_request.merged == true
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+      pull-requests: write
+      statuses: read
+      checks: read
+      actions: read
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Setup Git
+        run: |
+          git config user.name "github-actions[bot]"
+          git config user.email "github-actions[bot]@users.noreply.github.com"
+
+      - name: Backport
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          PR_NUMBER: ${{ github.event.pull_request.number }}
+          MERGE_COMMIT_SHA: ${{ github.event.pull_request.merge_commit_sha }}
+          DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+        run: |
+          set -e
+          
+          # Cache PR data early
+          echo "📊 Fetching PR data..."
+          PR_DATA=$(gh pr view "$PR_NUMBER" --json 
title,body,labels,statusCheckRollup,commits)
+          PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
+          PR_BODY=$(echo "$PR_DATA" | jq -r '.body // "No description 
provided."')
+          PR_LABELS=$(echo "$PR_DATA" | jq -r '.labels[].name | 
select(test("^[Bb]ackport/"; "i"))')
+          
+          # Function to create GitHub comment
+          create_comment() {
+            gh pr comment "$PR_NUMBER" --body "$1"
+          }
+          
+          # Function to add label safely  
+          add_label() {
+            gh pr edit "$PR_NUMBER" --add-label "$1" 2>/dev/null || true
+          }
+          
+          # Check status checks once
+          echo "🔍 Checking PR status checks..."
+          FAILED_CHECKS=$(echo "$PR_DATA" | jq -r '.statusCheckRollup[]? | 
select(.isRequired == true and (.state == "FAILURE" or .state == "ERROR")) | 
.state' 2>/dev/null || echo "")
+          
+          if [ -n "$FAILED_CHECKS" ]; then
+            echo "❌ Required status checks failed"
+            create_comment "âš ī¸ **Automatic backports skipped**
+          
+          Some required status checks failed on this PR. Backports will not be 
created automatically.
+          
+          **Action required:**
+          1. Fix failing tests or checks
+          2. Manually create backport PRs after verification
+          3. Or re-run this workflow after fixes are merged"
+            add_label "backport-failed"
+            exit 0
+          fi
+          
+          # Early exit if no backport labels
+          if [ -z "$PR_LABELS" ]; then
+            echo "â„šī¸ No backport labels found. Nothing to do."
+            exit 0
+          fi
+          
+          echo "📋 Found backport labels: $(echo "$PR_LABELS" | tr '\n' ' ')"
+          
+          # Batch create labels
+          echo "đŸˇī¸ Ensuring backport labels exist..."
+          gh label create "backport" --description "Automated backport 
workflow" --color "0366d6" 2>/dev/null || true &
+          gh label create "backport-conflict" --description "Backport had 
conflicts" --color "d73a49" 2>/dev/null || true &
+          gh label create "backport-failed" --description "Backport failed" 
--color "d73a49" 2>/dev/null || true &
+          wait # Wait for background label creation
+          
+          # Cache all remote branches once
+          echo "đŸŒŋ Caching branch information..."
+          ALL_BRANCHES=$(git branch -r | grep -v HEAD | sed 's|.*origin/||' | 
sort)
+          
+          # Define templates in order of preference
+          declare -a target_branch_templates=(
+            "branch_{{version_us}}"
+            "release/{{version}}"
+            "v{{version}}"
+            "{{version}}"
+            "{{version}}-stable"
+          )
+          
+          # Optimized branch finder with caching
+          find_target_branch() {
+            local version="$1"
+            local version_us="${version//\./_}"
+          
+            for template in "${target_branch_templates[@]}"; do
+              local pattern="${template//\{\{version\}\}/$version}"
+              pattern="${pattern//\{\{version_us\}\}/$version_us}"
+          
+              # Use cached branches instead of repeated git calls
+              local target=$(echo "$ALL_BRANCHES" | grep -E "^${pattern}$" | 
head -1)
+              if [ -n "$target" ]; then
+                echo "$target"
+                return 0
+              fi
+            done
+          
+            # Fallback with cached data
+            echo "$ALL_BRANCHES" | grep -E "${version}([^0-9]|$)" | head -1 || 
echo ""
+          }
+          
+          # Pre-check all target branches exist before processing
+          declare -a valid_backports=()
+          declare -a failed_versions=()
+          
+          echo "🔍 Pre-validating target branches..."
+          while IFS= read -r label; do
+            [ -z "$label" ] && continue
+            version=${label#[Bb]ackport/}
+            target=$(find_target_branch "$version")
+          
+            if [ -n "$target" ]; then
+              valid_backports+=("$version:$target")
+              echo "✅ $version -> $target"
+            else
+              failed_versions+=("$version")
+              echo "❌ $version -> NOT FOUND"
+            fi
+          done <<< "$PR_LABELS"
+          
+          # Report all missing branches at once
+          if [ ${#failed_versions[@]} -gt 0 ]; then
+            available_sample=$(echo "$ALL_BRANCHES" | head -5 | tr '\n' ', ' | 
sed 's/,$//')
+            failed_list=""
+            for version in "${failed_versions[@]}"; do
+              failed_list="${failed_list}- \`${version}\`"$'\n'
+            done
+          
+            create_comment "❌ **Backport failed for some versions - Missing 
target branches**
+          
+          Could not find target branches for:
+          $failed_list
+          
+          **Sample available branches:** $available_sample
+          
+          **Expected patterns:**
+          - \`branch_{version_with_underscores}\` (Lucene style)
+          - \`release/{version}\`
+          - \`v{version}\`
+          
+          Please create the missing branches or update the backport labels.
+          
+          **Note:** Valid backports will still be processed."
+          
+            add_label "backport-failed"
+          fi
+          
+          # Only exit if NO valid backports exist
+          if [ ${#valid_backports[@]} -eq 0 ]; then
+            echo "❌ No valid backports found. Exiting."
+            exit 1
+          fi
+          
+          echo "✅ Processing ${#valid_backports[@]} valid backport(s)..."
+          
+          # Process valid backports
+          success_count=0
+          failure_count=${#failed_versions[@]}
+          
+          for backport in "${valid_backports[@]}"; do
+            IFS=':' read -r version target <<< "$backport"
+            echo "🔄 Processing backport: $version -> $target"
+          
+            branch="backport-${PR_NUMBER}-to-${target}"
+          
+            # Clean checkout with error handling
+            git checkout "$DEFAULT_BRANCH" >/dev/null 2>&1
+            git branch -D "$branch" 2>/dev/null || true
+          
+            if ! git checkout -b "$branch" "origin/$target" >/dev/null 2>&1; 
then
+              echo "❌ Failed to checkout origin/$target"
+              create_comment "❌ **Backport to \`$target\` failed**
+          
+              Could not create branch from \`origin/$target\`. Branch may have 
been deleted or is unreachable."
+              add_label "backport-failed"
+              failure_count=$((failure_count + 1))
+              continue
+            fi
+          
+            # Check if PR already exists (early check)
+            existing_pr=$(gh pr list --head "$branch" --base "$target" --json 
number -q '.[0].number' 2>/dev/null || echo "")
+            if [ -n "$existing_pr" ]; then
+              echo "â„šī¸ Backport PR already exists: #$existing_pr"
+              success_count=$((success_count + 1))
+              continue
+            fi
+          
+            # Get individual commits from the PR instead of merge commit
+            echo "🔍 Getting PR commits..."
+            PR_COMMITS=$(echo "$PR_DATA" | jq -r '.commits[].oid' | tr '\n' ' 
')
+            echo "📝 PR commits: $PR_COMMITS"
+
+            if [ -z "$PR_COMMITS" ]; then
+              echo "❌ No commits found in PR"
+              create_comment "❌ **Backport to \`$target\` failed**
+          
+              Could not find individual commits in PR #${PR_NUMBER}."
+              add_label "backport-failed"
+              failure_count=$((failure_count + 1))
+              continue
+            fi
+
+            # Cherry-pick each commit individually
+            cherry_pick_success=true
+            for commit in $PR_COMMITS; do
+              echo "🍒 Cherry-picking commit: $commit"
+              if ! git cherry-pick -x "$commit" 2>&1; then
+                echo "❌ Failed to cherry-pick $commit"
+                cherry_pick_success=false
+                break
+              fi
+            done

Review Comment:
   Any reason to cherry-pick each commit individually instead of the merged 
commit. How PRs are merged in Lucene depends on what committers think make 
sense. For some of them, the full git history is retained, for others changes 
are squash merged.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscr...@lucene.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscr...@lucene.apache.org
For additional commands, e-mail: issues-h...@lucene.apache.org

Reply via email to