This is an automated email from the ASF dual-hosted git repository.

yao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new 678f314c3345 [SPARK-54860][INFRA] Add JIRA Ticket Validating in GHA
678f314c3345 is described below

commit 678f314c334513ac1525234783c1df656abe8522
Author: Kent Yao <[email protected]>
AuthorDate: Tue Dec 30 11:23:46 2025 +0800

    [SPARK-54860][INFRA] Add JIRA Ticket Validating in GHA
    
    ### What changes were proposed in this pull request?
    
    This PR adds a new GitHub Action workflow that automatically validates pull 
request titles and extracts JIRA ticket information. The workflow includes:
    
    1. **JIRA ID Extraction**: Automatically extracts JIRA IDs (e.g., 
`SPARK-12345`) from PR titles
    2. **[MINOR] Tag Support**: Allows PRs without JIRA IDs if they are 
prefixed with `[MINOR]` for minor changes
    3. **JIRA Information Display**: Fetches and displays JIRA ticket details 
(type, summary, assignee, status, affected versions) as a PR comment
    4. **Title Validation**: Posts a reminder comment when PR titles lack both 
JIRA IDs and [MINOR] tags
    
    The workflow runs on `pull_request_target` events (opened, edited, 
reopened) and uses the public Apache JIRA API (no authentication required).
    
    **Output Format Example:**
    ```
    === Task SPARK-54859 ===
    Summary Arrow-optimized Python UD(T)F Docs
    Assignee None
    Status Open
    Affected ["4.2.0"]
    ```
    
    ### Why are the changes needed?
    
    Currently, Apache Spark PRs require manual verification of JIRA ticket 
associations. This automation:
    
    - **Improves contributor experience**: Provides immediate feedback on PR 
title format
    - **Streamlines review process**: Displays JIRA context directly in the PR, 
eliminating manual lookups
    - **Enforces consistency**: Ensures all non-minor PRs are linked to JIRA 
tickets
    - **Reduces review overhead**: Reviewers can quickly understand the context 
without switching to JIRA
    
    This is particularly useful for new contributors who may not be familiar 
with Apache's PR conventions. It's very likely for them to make mistakes like 
https://github.com/apache/spark/pull/53445#issuecomment-3691277799
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. This is an infrastructure change that only affects the GitHub PR 
workflow.
    
    ### How was this patch tested?
    
    1. **Local Testing**: Validated the logic using `test-jira-action.py` which 
simulates the GitHub Action behavior
       - Tested JIRA ID extraction for various title formats
       - Verified [MINOR] tag detection (case-insensitive)
       - Confirmed JIRA API calls return expected data
    
    2. **Test Cases**:
       - `[SPARK-54859] Title` → Displays JIRA info (✓ tested successfully)
       - `[SPARK-111][SPARK-222] Multiple` → Displays multiple JIRA infos
       - `[MINOR] Fix typo` → Silently skips validation
       - `Fix bug` → Posts reminder to add JIRA ID or [MINOR] tag
    
    3. **Real JIRA Verification**: Tested with actual Apache JIRA tickets 
(SPARK-54859, SPARK-50000) to confirm API responses, e.g. 
https://github.com/yaooqinn/spark/actions/runs/20567427354/job/59067970526?pr=4
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: GitHub Copilot (Claude Sonnet 4.5)
    
    Closes #53633 from yaooqinn/SPARK-54860.
    
    Lead-authored-by: Kent Yao <[email protected]>
    Co-authored-by: Kent Yao <[email protected]>
    Signed-off-by: Kent Yao <[email protected]>
---
 .github/workflows/labeler.yml | 154 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 153 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index ccfdeb1653b9..77b1e733d5e7 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -24,7 +24,9 @@
 # See also 
https://github.community/t/specify-check-suite-when-creating-a-checkrun/118380/10
 
 name: "On pull requests"
-on: pull_request_target
+on:
+  pull_request_target:
+    types: [opened, edited, reopened]
 
 jobs:
   label:
@@ -38,3 +40,153 @@ jobs:
       with:
         repo-token: "${{ secrets.GITHUB_TOKEN }}"
         sync-labels: true
+
+  jira-info:
+    name: Comment JIRA information
+    runs-on: ubuntu-latest
+    permissions:
+      pull-requests: write
+    steps:
+    - name: Extract JIRA IDs and comment
+      uses: actions/github-script@v7
+      with:
+        github-token: ${{ secrets.GITHUB_TOKEN }}
+        script: |
+          const prTitle = context.payload.pull_request.title;
+          const prNumber = context.payload.pull_request.number;
+
+          // Extract JIRA IDs from PR title
+          const jiraIdRegex = /\bSPARK-\d+\b/g;
+          const jiraIds = prTitle.match(jiraIdRegex);
+
+          // If no JIRA IDs found, check for [MINOR] tag
+          if (!jiraIds || jiraIds.length === 0) {
+            const minorRegex = /^\[MINOR\]/i;
+            if (minorRegex.test(prTitle)) {
+              console.log('PR title has [MINOR] tag, skipping');
+              return;
+            }
+
+            // Post reminder comment
+            const reminderComment = `## ⚠️ Pull Request Title 
Validation\n\nThis pull request title does not contain a JIRA issue 
ID.\n\nPlease update the title to either:\n- Include a JIRA ID: \`[SPARK-12345] 
Your description\`\n- Mark as minor change: \`[MINOR] Your description\`\n\nFor 
minor changes that don't require a JIRA ticket (e.g., typo fixes), please 
prefix the title with \`[MINOR]\`.\n\n---\n*This comment was automatically 
generated by GitHub Actions*`;
+
+            const comments = await github.rest.issues.listComments({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              issue_number: prNumber
+            });
+
+            const botComment = comments.data.find(comment =>
+              comment.user.type === 'Bot' &&
+              (comment.body.includes('## JIRA Issue Information') || 
comment.body.includes('## ⚠️ Pull Request Title Validation'))
+            );
+
+            if (botComment) {
+              await github.rest.issues.updateComment({
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                comment_id: botComment.id,
+                body: reminderComment
+              });
+              console.log('Updated reminder comment');
+            } else {
+              await github.rest.issues.createComment({
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                issue_number: prNumber,
+                body: reminderComment
+              });
+              console.log('Created reminder comment');
+            }
+            return;
+          }
+
+          // Remove duplicates
+          const uniqueJiraIds = [...new Set(jiraIds)];
+          console.log(`Found JIRA IDs: ${uniqueJiraIds.join(', ')}`);
+
+          // Fetch JIRA information for each ID
+          const jiraBaseUrl = 'https://issues.apache.org/jira';
+          const jiraInfos = [];
+
+          for (const jiraId of uniqueJiraIds) {
+            try {
+              const response = await 
fetch(`${jiraBaseUrl}/rest/api/2/issue/${jiraId}`);
+
+              if (!response.ok) {
+                jiraInfos.push({
+                  id: jiraId,
+                  type: 'Unknown',
+                  error: `Failed to fetch (HTTP ${response.status})`
+                });
+                continue;
+              }
+
+              const data = await response.json();
+              const fields = data.fields;
+
+              jiraInfos.push({
+                id: jiraId,
+                type: fields.issuetype?.name || 'Unknown',
+                summary: fields.summary || 'N/A',
+                assignee: fields.assignee ? fields.assignee.displayName : 
'None',
+                status: fields.status ? fields.status.name : 'Unknown',
+                affected: fields.versions ? fields.versions.map(v => v.name) : 
[]
+              });
+            } catch (error) {
+              console.error(`Error fetching ${jiraId}:`, error);
+              jiraInfos.push({
+                id: jiraId,
+                type: 'Unknown',
+                error: error.message
+              });
+            }
+          }
+
+          // Format comment
+          let commentBody = '## JIRA Issue Information\n\n';
+
+          for (const info of jiraInfos) {
+            if (info.error) {
+              commentBody += `=== ${info.type} ${info.id} ===\n`;
+              commentBody += `Error: ${info.error}\n\n`;
+            } else {
+              commentBody += `=== ${info.type} ${info.id} ===\n`;
+              commentBody += `Summary: ${info.summary}\n`;
+              commentBody += `Assignee: ${info.assignee}\n`;
+              commentBody += `Status: ${info.status}\n`;
+              commentBody += `Affected: ${JSON.stringify(info.affected)}\n\n`;
+            }
+          }
+
+          commentBody += '---\n*This comment was automatically generated by 
GitHub Actions*';
+
+          // Check if there's an existing comment from this action
+          const comments = await github.rest.issues.listComments({
+            owner: context.repo.owner,
+            repo: context.repo.repo,
+            issue_number: prNumber
+          });
+
+          const botComment = comments.data.find(comment =>
+            comment.user.type === 'Bot' &&
+            (comment.body.includes('## JIRA Issue Information') || 
comment.body.includes('## ⚠️ Pull Request Title Validation'))
+          );
+
+          if (botComment) {
+            await github.rest.issues.updateComment({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              comment_id: botComment.id,
+              body: commentBody
+            });
+            console.log('Updated existing comment');
+          } else {
+            await github.rest.issues.createComment({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              issue_number: prNumber,
+              body: commentBody
+            });
+            console.log('Created new comment');
+          }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to