-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add link issues check (#74)
- Loading branch information
1 parent
61a8de8
commit 6fc59f7
Showing
15 changed files
with
833 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Merge base into PR branch whenever updated | ||
const getConnectedIssueForPR = require('./utils/getConnectedIssueForPR') | ||
const addConnectedPRToIssue = require('./utils/addConnectedPRToIssue') | ||
const removeConnectedPRFromIssue = require('./utils/removeConnectedPRFromIssue') | ||
|
||
const repoWhitelist = [ | ||
'testrepo', | ||
'rex-web', | ||
'testing-stuff' | ||
] | ||
|
||
const name = 'has issue link' | ||
|
||
module.exports = (robot) => { | ||
const logger = robot.log.child({name: 'link-issues-check'}) | ||
robot.on([ | ||
'pull_request.opened', | ||
'pull_request.edited', | ||
'pull_request.synchronize' | ||
], checkPR) | ||
|
||
async function checkPR (context) { | ||
const pullRequest = context.payload.pull_request | ||
if (!repoWhitelist.includes(context.payload.repository.name)) { | ||
return | ||
} | ||
logger.info(`checking pr ${pullRequest.number}`) | ||
|
||
const check = await context.github.checks.create(context.repo({ | ||
name, | ||
head_sha: context.payload.pull_request.head.sha, | ||
status: 'in_progress', | ||
output: {title: name, summary: 'processing'} | ||
})) | ||
|
||
const linkedIssueParams = getConnectedIssueForPR(pullRequest) | ||
const linkedIssue = linkedIssueParams && await context.github.issues.get(linkedIssueParams) | ||
.then(response => response.data) | ||
.catch(() => null) | ||
|
||
logger.info(`pr ${pullRequest.number} ${linkedIssue ? 'passed' : 'failed'}`) | ||
|
||
await context.github.checks.update(context.repo({ | ||
check_run_id: check.data.id, | ||
status: 'completed', | ||
conclusion: linkedIssue ? 'success' : 'failure', | ||
output: linkedIssue | ||
? { | ||
title: 'all is as it should be', | ||
summary: 'good job linking to that issue! :+1:' | ||
} | ||
: { | ||
title: 'please add an issue reference', | ||
summary: 'please add a link to the issue this PR is for to the PR description', | ||
text: 'for example `for: openstax/cool-repo#5`. `for: <github or zenhub url>` also work' | ||
} | ||
})) | ||
|
||
if (context.payload.action === 'edited' && context.payload.changes.body) { | ||
const previousIssueParams = getConnectedIssueForPR({...pullRequest, body: context.payload.changes.body.from}) | ||
const previousIssue = previousIssueParams && await context.github.issues.get(previousIssueParams) | ||
.then(response => response.data) | ||
.catch(() => null) | ||
|
||
if (previousIssue && (!linkedIssue || previousIssue.number !== linkedIssue.number)) { | ||
await removeConnectedPRFromIssue(context.github, previousIssueParams, previousIssue, pullRequest) | ||
} | ||
} | ||
|
||
if (linkedIssue) { | ||
await addConnectedPRToIssue(context.github, linkedIssueParams, linkedIssue, pullRequest) | ||
} | ||
}; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
const {prBlockRegex} = require('./connectedPRRegexes') | ||
const getConnectedPRsForIssue = require('./getConnectedPRsForIssue') | ||
|
||
/* | ||
* @argument context.github | ||
* @argument IssueData | ||
* @argument PullRequestData | ||
* | ||
* @returns Promise<void> | ||
*/ | ||
module.exports = (github, issueParams, issue, pullRequest) => { | ||
const prs = getConnectedPRsForIssue(issue) | ||
|
||
const pullNumber = pullRequest.number | ||
const repo = pullRequest.base.repo.name | ||
const owner = pullRequest.base.repo.owner.login | ||
|
||
const existing = prs.find(pr => Number(pr.pull_number) === pullNumber && pr.repo === repo && pr.owner === owner) | ||
|
||
if (existing) { | ||
return | ||
} | ||
|
||
const newLink = `\n- [ ] ${owner}/${repo}#${pullNumber}` | ||
const blockMatch = issue.body.match(new RegExp(prBlockRegex, 'i')) | ||
const newBody = blockMatch | ||
? issue.body.replace(blockMatch[0], blockMatch[0] + newLink) | ||
: issue.body + '\n\npull requests:' + newLink | ||
|
||
return github.issues.update({ | ||
...issueParams, | ||
body: newBody | ||
}) | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const { | ||
githubRefGroups, githubPullRequestLinkGroups, zenhubLinkGroups, | ||
githubRef, githubPullRequestLink, zenhubLink, | ||
whitespace, beginningOfStringOrNewline | ||
} = require('./regexes') | ||
|
||
const anyLink = `((${githubRef})|(${githubPullRequestLink})|(${zenhubLink}))` | ||
|
||
const listPrefix = '\\n\\- \\[( |x)\\] ' | ||
const prBlockRegex = `${beginningOfStringOrNewline}#* ?\\*{0,2}pull requests:?\\*{0,2}:?(${whitespace}*${listPrefix}${anyLink})*` | ||
|
||
const anyLinkGroups = [ | ||
githubRefGroups, | ||
githubPullRequestLinkGroups, | ||
zenhubLinkGroups | ||
] | ||
|
||
module.exports = { | ||
listPrefix, | ||
anyLinkGroups, | ||
anyLink, | ||
prBlockRegex | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const { | ||
beginningOfStringOrWhitespace, endOfStringOrWhitespace, | ||
githubRefGroups, githubIssueLinkGroups, zenhubLinkGroups | ||
} = require('./regexes') | ||
|
||
const targetRegexes = [ | ||
`${beginningOfStringOrWhitespace}for: ${githubRefGroups}${endOfStringOrWhitespace}`, | ||
`${beginningOfStringOrWhitespace}for: ${githubIssueLinkGroups}${endOfStringOrWhitespace}`, | ||
`${beginningOfStringOrWhitespace}for: ${zenhubLinkGroups}${endOfStringOrWhitespace}` | ||
] | ||
|
||
/* | ||
* @argument PullRequestData | ||
* | ||
* @returns IssueParams | null | ||
*/ | ||
module.exports = (pullRequest) => { | ||
const target = targetRegexes.reduce((result, regex) => result || pullRequest.body.match(new RegExp(regex, 'i')), null) | ||
|
||
if (target && target.groups) { | ||
const {number, ...params} = target.groups | ||
return {...params, issue_number: number} | ||
} | ||
|
||
return null | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
const {anyLink, anyLinkGroups, prBlockRegex} = require('./connectedPRRegexes') | ||
|
||
/* | ||
* @argument IssueData | ||
* | ||
* @returns PullRequestParams | null | ||
*/ | ||
module.exports = (issue) => { | ||
const blockMatch = issue.body.match(new RegExp(prBlockRegex, 'i')) | ||
const links = blockMatch && blockMatch[0].match(new RegExp(anyLink, 'gi')) | ||
|
||
return (links || []).map(link => { | ||
const result = anyLinkGroups.reduce((result, regex) => result || link.match(new RegExp(regex, 'i')), null) | ||
return result ? result.groups : null | ||
}) | ||
.filter(params => !!params) | ||
.map(({number, ...params}) => ({...params, pull_number: number})) | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
const whitespace = '(\\s|\\r)' | ||
const beginningOfStringOrNewline = '^(.*[\\n\\r]+)*' | ||
const beginningOfStringOrWhitespace = '^(.*[\\s\\r]+)*' | ||
const endOfStringOrWhitespace = '([\\s\\r]+.*)*$' | ||
|
||
/* eslint-disable-next-line */ | ||
const githubRefGroups = '(?<owner>[a-z\-]+)\/(?<repo>[a-z\-]+)#(?<number>[0-9]+)' | ||
/* eslint-disable-next-line */ | ||
const githubIssueLinkGroups = 'https:\/\/github.com\/(?<owner>[a-z\-]+)\/(?<repo>[a-z\-]+)\/issues\/(?<number>[0-9]+)' | ||
/* eslint-disable-next-line */ | ||
const githubPullRequestLinkGroups = 'https:\/\/github.com\/(?<owner>[a-z\-]+)\/(?<repo>[a-z\-]+)\/pulls\/(?<number>[0-9]+)' | ||
/* eslint-disable-next-line */ | ||
const zenhubLinkGroups = 'https:\/\/app.zenhub.com\/workspaces\/[0-9a-z\-]+\/issues\/(?<owner>[a-z\-]+)\/(?<repo>[a-z\-]+)\/(?<number>[0-9]+)' | ||
|
||
/* eslint-disable-next-line */ | ||
const githubRef = '[a-z\-]+\/[a-z\-]+#[0-9]+' | ||
/* eslint-disable-next-line */ | ||
const githubPullRequestLink = 'https:\/\/github.com\/[a-z\-]+\/[a-z\-]+\/pulls\/[0-9]+' | ||
/* eslint-disable-next-line */ | ||
const zenhubLink = 'https:\/\/app.zenhub.com\/workspaces\/[0-9a-z\-]+\/issues\/[a-z\-]+\/[a-z\-]+\/[0-9]+' | ||
|
||
module.exports = { | ||
whitespace, | ||
beginningOfStringOrNewline, | ||
beginningOfStringOrWhitespace, | ||
endOfStringOrWhitespace, | ||
githubRefGroups, | ||
githubIssueLinkGroups, | ||
githubPullRequestLinkGroups, | ||
zenhubLinkGroups, | ||
githubRef, | ||
githubPullRequestLink, | ||
zenhubLink | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
const {anyLink, listPrefix, anyLinkGroups, prBlockRegex} = require('./connectedPRRegexes') | ||
|
||
/* | ||
* @argument context.github | ||
* @argument IssueData | ||
* @argument PullRequestData | ||
* | ||
* @returns Promise<void> | ||
*/ | ||
module.exports = (github, issueParams, issue, pullRequest) => { | ||
const pullNumber = pullRequest.number | ||
const repo = pullRequest.base.repo.name | ||
const owner = pullRequest.base.repo.owner.login | ||
|
||
const blockMatch = issue.body.match(new RegExp(prBlockRegex, 'i')) | ||
|
||
if (!blockMatch) { | ||
return | ||
} | ||
|
||
const lines = blockMatch[0].match(new RegExp(listPrefix + anyLink, 'gi')) | ||
|
||
const linesToRemove = lines.filter(line => { | ||
const match = anyLinkGroups.reduce((result, regex) => result || line.match(new RegExp(regex, 'i')), null) | ||
const params = match && match.groups | ||
return params && Number(params.number) === pullNumber && params.repo === repo && params.owner === owner | ||
}) | ||
|
||
if (!linesToRemove.length) { | ||
return | ||
} | ||
|
||
const newPRBlock = linesToRemove.reduce((result, line) => result.replace(line, ''), blockMatch[0]) | ||
const newBody = issue.body.replace(blockMatch[0], newPRBlock) | ||
|
||
return github.issues.update({ | ||
...issueParams, | ||
body: newBody | ||
}) | ||
} |
Oops, something went wrong.