Skip to content

Commit b1bcd6f

Browse files
authored
ARROW-18380: [Dev] Update dev_pr GitHub workflows to accept both GitHub issues and JIRA (#14731)
This might require to be done once the merge_arrow_pr.sh script is also updated. Updating the merge PR script is tracked on GitHub for the migration here: #14720 I have tested this new workflow on my fork with the following PRs and issues on my fork. Example of valid issue: raulcd#41 Example of issues without labels or assignees: raulcd#42 or raulcd#43 Example of issue with non existent GH issue: raulcd#45 Authored-by: Raúl Cumplido <raulcumplido@gmail.com> Signed-off-by: Alessandro Molina <amol@turbogears.org>
1 parent fde7b93 commit b1bcd6f

File tree

6 files changed

+205
-46
lines changed

6 files changed

+205
-46
lines changed

.github/workflows/dev_pr.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ concurrency:
3636
permissions:
3737
contents: read
3838
pull-requests: write
39+
issues: write
3940

4041
jobs:
4142
process:
@@ -66,7 +67,7 @@ jobs:
6667
const script = require(`${process.env.GITHUB_WORKSPACE}/.github/workflows/dev_pr/title_check.js`);
6768
script({github, context});
6869
69-
- name: Check Jira Issue
70+
- name: Check Issue
7071
if: |
7172
(github.event.action == 'opened' ||
7273
github.event.action == 'edited')
@@ -75,7 +76,7 @@ jobs:
7576
debug: true
7677
github-token: ${{ secrets.GITHUB_TOKEN }}
7778
script: |
78-
const script = require(`${process.env.GITHUB_WORKSPACE}/.github/workflows/dev_pr/jira_check.js`);
79+
const script = require(`${process.env.GITHUB_WORKSPACE}/.github/workflows/dev_pr/issue_check.js`);
7980
script({github, context});
8081
8182
- name: Assign GitHub labels

.github/workflows/dev_pr/helpers.js

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,33 @@
1818
const https = require('https');
1919

2020
/**
21-
* Given the title of a PullRequest return the ID of the JIRA issue
21+
* Given the title of a PullRequest return the Issue
22+
*
2223
* @param {String} title
23-
* @returns {String} the ID of the associated JIRA issue
24+
* @returns {Issue} or null if no issue detected.
25+
*
26+
* @typedef {Object} Issue
27+
* @property {string} kind - The kind of issue: minor, jira or github
28+
* @property {string} id - The id of the issue:
29+
* ARROW-XXXX, PARQUET-XXXX for jira
30+
* The numeric issue id for github
2431
*/
25-
function detectJIRAID(title) {
32+
function detectIssue(title) {
2633
if (!title) {
2734
return null;
2835
}
29-
const matched = /^(WIP:?\s*)?((ARROW|PARQUET)-\d+)/.exec(title);
30-
if (!matched) {
31-
return null;
36+
if (title.startsWith("MINOR: ")) {
37+
return {"kind": "minor"};
3238
}
33-
return matched[2];
34-
}
35-
36-
/**
37-
* Given the title of a PullRequest checks if it contains a JIRA issue ID
38-
* @param {String} title
39-
* @returns {Boolean} true if it starts with a JIRA ID or MINOR:
40-
*/
41-
function haveJIRAID(title) {
42-
if (!title) {
43-
return false;
39+
const matched_jira = /^(WIP:?\s*)?((ARROW|PARQUET)-\d+)/.exec(title);
40+
if (matched_jira) {
41+
return {"kind": "jira", "id": matched_jira[2]};
4442
}
45-
if (title.startsWith("MINOR: ")) {
46-
return true;
43+
const matched_gh = /^(WIP:?\s*)?GH-(\d+)/.exec(title);
44+
if (matched_gh) {
45+
return {"kind": "github", "id": matched_gh[2]};
4746
}
48-
return /^(WIP:?\s*)?(ARROW|PARQUET)-\d+/.test(title);
47+
return null;
4948
}
5049

5150
/**
@@ -69,8 +68,27 @@ async function getJiraInfo(jiraID) {
6968
});
7069
}
7170

71+
/**
72+
* Retrieves information about a GitHub issue.
73+
* @param {String} issueID
74+
* @returns {Object} the information about a GitHub issue.
75+
*/
76+
async function getGitHubInfo(github, context, issueID, pullRequestNumber) {
77+
try {
78+
const response = await github.issues.get({
79+
issue_number: issueID,
80+
owner: context.repo.owner,
81+
repo: context.repo.repo,
82+
})
83+
return response.data
84+
} catch (error) {
85+
console.log(`${error.name}: ${error.code}`);
86+
return false
87+
}
88+
}
89+
7290
module.exports = {
73-
detectJIRAID,
74-
haveJIRAID,
75-
getJiraInfo
91+
detectIssue,
92+
getJiraInfo,
93+
getGitHubInfo
7694
};

.github/workflows/dev_pr/jira_check.js renamed to .github/workflows/dev_pr/issue_check.js

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@
1717

1818
const helpers = require("./helpers.js");
1919

20+
/**
21+
* Performs checks on the JIRA Issue:
22+
* - The issue is started in JIRA.
23+
* - The issue contains components.
24+
*
25+
* @param {Object} github
26+
* @param {Object} context
27+
* @param {String} pullRequestNumber
28+
* @param {String} jiraID
29+
*/
2030
async function verifyJIRAIssue(github, context, pullRequestNumber, jiraID) {
2131
const ticketInfo = await helpers.getJiraInfo(jiraID);
2232
if(!ticketInfo["fields"]["components"].length) {
@@ -30,6 +40,13 @@ async function verifyJIRAIssue(github, context, pullRequestNumber, jiraID) {
3040
}
3141
}
3242

43+
/**
44+
* Adds a comment to add components on the JIRA ticket.
45+
*
46+
* @param {Object} github
47+
* @param {Object} context
48+
* @param {String} pullRequestNumber
49+
*/
3350
async function commentMissingComponents(github, context, pullRequestNumber) {
3451
const {data: comments} = await github.issues.listComments({
3552
owner: context.repo.owner,
@@ -54,6 +71,13 @@ async function commentMissingComponents(github, context, pullRequestNumber) {
5471
}
5572
}
5673

74+
/**
75+
* Adds a comment to start the ticket in JIRA.
76+
*
77+
* @param {Object} github
78+
* @param {Object} context
79+
* @param {String} pullRequestNumber
80+
*/
5781
async function commentNotStartedTicket(github, context, pullRequestNumber) {
5882
const {data: comments} = await github.issues.listComments({
5983
owner: context.repo.owner,
@@ -78,11 +102,72 @@ async function commentNotStartedTicket(github, context, pullRequestNumber) {
78102
}
79103
}
80104

105+
/**
106+
* Assigns the Github Issue to the PR creator.
107+
*
108+
* @param {Object} github
109+
* @param {Object} context
110+
* @param {String} pullRequestNumber
111+
* @param {Object} issueInfo
112+
*/
113+
async function assignGitHubIssue(github, context, pullRequestNumber, issueInfo) {
114+
await github.issues.addAssignees({
115+
owner: context.repo.owner,
116+
repo: context.repo.repo,
117+
issue_number: issueInfo.number,
118+
assignees: context.payload.pull_request.user.login
119+
});
120+
await github.issues.createComment({
121+
owner: context.repo.owner,
122+
repo: context.repo.repo,
123+
issue_number: pullRequestNumber,
124+
body: ":warning: GitHub issue #" + issueInfo.number + " **has been automatically assigned in GitHub** to PR creator."
125+
});
126+
}
127+
128+
/**
129+
* Performs checks on the GitHub Issue:
130+
* - The issue is assigned to someone. If not assign it gets automatically
131+
* assigned to the PR creator.
132+
* - The issue contains any label.
133+
*
134+
* @param {Object} github
135+
* @param {Object} context
136+
* @param {String} pullRequestNumber
137+
* @param {String} issueID
138+
*/
139+
async function verifyGitHubIssue(github, context, pullRequestNumber, issueID) {
140+
const issueInfo = await helpers.getGitHubInfo(github, context, issueID, pullRequestNumber);
141+
if (!issueInfo) {
142+
await github.issues.createComment({
143+
owner: context.repo.owner,
144+
repo: context.repo.repo,
145+
issue_number: pullRequestNumber,
146+
body: ":x: GitHub issue #" + issueID + " could not be retrieved."
147+
})
148+
}
149+
if (!issueInfo.assignees.length) {
150+
await assignGitHubIssue(github, context, pullRequestNumber, issueInfo);
151+
}
152+
if(!issueInfo.labels.filter((label) => label.name.startsWith("Component:")).length) {
153+
await github.issues.createComment({
154+
owner: context.repo.owner,
155+
repo: context.repo.repo,
156+
issue_number: pullRequestNumber,
157+
body: ":warning: GitHub issue #" + issueID + " **has no components**, please add labels for components."
158+
})
159+
}
160+
}
161+
81162
module.exports = async ({github, context}) => {
82163
const pullRequestNumber = context.payload.number;
83164
const title = context.payload.pull_request.title;
84-
const jiraID = helpers.detectJIRAID(title);
85-
if (jiraID) {
86-
await verifyJIRAIssue(github, context, pullRequestNumber, jiraID);
165+
const issue = helpers.detectIssue(title)
166+
if (issue){
167+
if (issue.kind == "jira") {
168+
await verifyJIRAIssue(github, context, pullRequestNumber, issue.id);
169+
} else if(issue.kind == "github") {
170+
await verifyGitHubIssue(github, context, pullRequestNumber, issue.id);
171+
}
87172
}
88173
};

.github/workflows/dev_pr/link.js

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,16 @@
1818
const helpers = require("./helpers.js");
1919

2020

21-
async function haveComment(github, context, pullRequestNumber, body) {
21+
/**
22+
* Checks whether message is present on Pull Request list of comments.
23+
*
24+
* @param {Object} github
25+
* @param {Object} context
26+
* @param {String} pullRequestNumber
27+
* @param {String} message
28+
* @returns {Boolean} true if message was found.
29+
*/
30+
async function haveComment(github, context, pullRequestNumber, message) {
2231
const options = {
2332
owner: context.repo.owner,
2433
repo: context.repo.repo,
@@ -27,7 +36,7 @@ async function haveComment(github, context, pullRequestNumber, body) {
2736
};
2837
while (true) {
2938
const response = await github.issues.listComments(options);
30-
if (response.data.some(comment => comment.body === body)) {
39+
if (response.data.some(comment => comment.body === message)) {
3140
return true;
3241
}
3342
if (!/;\s*rel="next"/.test(response.headers.link || "")) {
@@ -38,24 +47,64 @@ async function haveComment(github, context, pullRequestNumber, body) {
3847
return false;
3948
}
4049

50+
/**
51+
* Adds a comment on the Pull Request linking the JIRA issue.
52+
*
53+
* @param {Object} github
54+
* @param {Object} context
55+
* @param {String} pullRequestNumber
56+
* @param {String} jiraID
57+
*/
4158
async function commentJIRAURL(github, context, pullRequestNumber, jiraID) {
59+
const issueInfo = await helpers.getJiraInfo(jiraID);
4260
const jiraURL = `https://issues.apache.org/jira/browse/${jiraID}`;
4361
if (await haveComment(github, context, pullRequestNumber, jiraURL)) {
4462
return;
4563
}
46-
await github.issues.createComment({
47-
owner: context.repo.owner,
48-
repo: context.repo.repo,
49-
issue_number: pullRequestNumber,
50-
body: jiraURL
51-
});
64+
if (issueInfo){
65+
await github.issues.createComment({
66+
owner: context.repo.owner,
67+
repo: context.repo.repo,
68+
issue_number: pullRequestNumber,
69+
body: jiraURL
70+
});
71+
}
72+
}
73+
74+
/**
75+
* Adds a comment on the Pull Request linking the GitHub issue.
76+
*
77+
* @param {Object} github
78+
* @param {Object} context
79+
* @param {String} pullRequestNumber - String containing numeric id of PR
80+
* @param {String} issueID - String containing numeric id of the github issue
81+
*/
82+
async function commentGitHubURL(github, context, pullRequestNumber, issueID) {
83+
// Make the call to ensure issue exists before adding comment
84+
const issueInfo = await helpers.getGitHubInfo(github, context, issueID, pullRequestNumber);
85+
const message = "* Github Issue: #" + issueInfo.number
86+
if (await haveComment(github, context, pullRequestNumber, message)) {
87+
return;
88+
}
89+
if (issueInfo){
90+
await github.issues.createComment({
91+
owner: context.repo.owner,
92+
repo: context.repo.repo,
93+
issue_number: pullRequestNumber,
94+
body: message
95+
});
96+
}
5297
}
5398

5499
module.exports = async ({github, context}) => {
55100
const pullRequestNumber = context.payload.number;
56101
const title = context.payload.pull_request.title;
57-
const jiraID = helpers.detectJIRAID(title);
58-
if (jiraID) {
59-
await commentJIRAURL(github, context, pullRequestNumber, jiraID);
102+
const issue = helpers.detectIssue(title);
103+
if (issue){
104+
if (issue.kind == "jira") {
105+
await commentJIRAURL(github, context, pullRequestNumber, issue.id);
106+
} else if (issue.kind == "github") {
107+
await commentGitHubURL(github, context, pullRequestNumber, issue.id);
108+
}
60109
}
61110
};

.github/workflows/dev_pr/title_check.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
const fs = require("fs");
1919
const helpers = require("./helpers.js");
2020

21-
async function commentOpenJIRAIssue(github, context, pullRequestNumber) {
21+
async function commentOpenGitHubIssue(github, context, pullRequestNumber) {
2222
const {data: comments} = await github.issues.listComments({
2323
owner: context.repo.owner,
2424
repo: context.repo.repo,
@@ -41,7 +41,8 @@ async function commentOpenJIRAIssue(github, context, pullRequestNumber) {
4141
module.exports = async ({github, context}) => {
4242
const pullRequestNumber = context.payload.number;
4343
const title = context.payload.pull_request.title;
44-
if (!helpers.haveJIRAID(title)) {
45-
await commentOpenJIRAIssue(github, context, pullRequestNumber);
44+
const issue = helpers.detectIssue(title)
45+
if (!issue) {
46+
await commentOpenGitHubIssue(github, context, pullRequestNumber);
4647
}
4748
};

.github/workflows/dev_pr/title_check.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,23 @@
1919

2020
Thanks for opening a pull request!
2121

22-
If this is not a [minor PR](https://github.com/apache/arrow/blob/master/CONTRIBUTING.md#Minor-Fixes). Could you open an issue for this pull request on JIRA? https://issues.apache.org/jira/browse/ARROW
22+
If this is not a [minor PR](https://github.com/apache/arrow/blob/master/CONTRIBUTING.md#Minor-Fixes). Could you open an issue for this pull request on GitHub? https://github.com/apache/arrow/issues/new/choose
2323

24-
Opening JIRAs ahead of time contributes to the [Openness](http://theapacheway.com/open/#:~:text=Openness%20allows%20new%20users%20the,must%20happen%20in%20the%20open.) of the Apache Arrow project.
24+
Opening GitHub issues ahead of time contributes to the [Openness](http://theapacheway.com/open/#:~:text=Openness%20allows%20new%20users%20the,must%20happen%20in%20the%20open.) of the Apache Arrow project.
2525

26-
Then could you also rename pull request title in the following format?
26+
Then could you also rename the pull request title in the following format?
2727

28-
ARROW-${JIRA_ID}: [${COMPONENT}] ${SUMMARY}
28+
GH-${GITHUB_ISSUE_ID}: [${COMPONENT}] ${SUMMARY}
2929

3030
or
3131

3232
MINOR: [${COMPONENT}] ${SUMMARY}
3333

34+
In the case of old issues on JIRA the title also supports:
35+
36+
ARROW-${JIRA_ISSUE_ID}: [${COMPONENT}] ${SUMMARY}
37+
PARQUET-${JIRA_ISSUE_ID}: [${COMPONENT}] ${SUMMARY}
38+
3439
See also:
3540

3641
* [Other pull requests](https://github.com/apache/arrow/pulls/)

0 commit comments

Comments
 (0)