-
Notifications
You must be signed in to change notification settings - Fork 128
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Add comment to issues and pull requests included in the release
- Loading branch information
Showing
11 changed files
with
492 additions
and
19 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
const HOME_URL = 'https://github.com/semantic-release/semantic-release'; | ||
const linkify = releaseInfo => | ||
`${releaseInfo.url ? `[${releaseInfo.name}](${releaseInfo.url})` : `\`${releaseInfo.name}\``}`; | ||
|
||
module.exports = (issue, releaseInfos, nextRelease) => | ||
`:tada: This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${ | ||
nextRelease.version | ||
} :tada:${ | ||
releaseInfos.length > 0 | ||
? `\n\nThe release is available on${ | ||
releaseInfos.length === 1 | ||
? ` ${linkify(releaseInfos[0])}` | ||
: `:\n${releaseInfos.map(releaseInfo => `- ${linkify(releaseInfo)}`).join('\n')}` | ||
}` | ||
: '' | ||
} | ||
Your **[semantic-release](${HOME_URL})** bot :package::rocket:`; |
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 |
---|---|---|
@@ -1,8 +1,9 @@ | ||
const {castArray} = require('lodash'); | ||
|
||
module.exports = ({githubUrl, githubApiPathPrefix, assets}) => ({ | ||
module.exports = ({githubUrl, githubApiPathPrefix, assets, successComment}) => ({ | ||
githubToken: process.env.GH_TOKEN || process.env.GITHUB_TOKEN, | ||
githubUrl: githubUrl || process.env.GH_URL || process.env.GITHUB_URL, | ||
githubApiPathPrefix: githubApiPathPrefix || process.env.GH_PREFIX || process.env.GITHUB_PREFIX || '', | ||
assets: assets ? castArray(assets) : assets, | ||
successComment, | ||
}); |
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,62 @@ | ||
const {uniqBy, template} = require('lodash'); | ||
const parseGithubUrl = require('parse-github-url'); | ||
const pReduce = require('p-reduce'); | ||
const AggregateError = require('aggregate-error'); | ||
const issueParser = require('issue-parser')('github'); | ||
const debug = require('debug')('semantic-release:github'); | ||
const resolveConfig = require('./resolve-config'); | ||
const getClient = require('./get-client'); | ||
const getSuccessComment = require('./get-success-comment'); | ||
|
||
module.exports = async ( | ||
pluginConfig, | ||
{options: {branch, repositoryUrl}, lastRelease, commits, nextRelease, releases, logger} | ||
) => { | ||
const {githubToken, githubUrl, githubApiPathPrefix, successComment} = resolveConfig(pluginConfig); | ||
const {name: repo, owner} = parseGithubUrl(repositoryUrl); | ||
const github = getClient(githubToken, githubUrl, githubApiPathPrefix); | ||
const releaseInfos = releases.filter(release => Boolean(release.name)); | ||
|
||
// Search for PRs associated with any commit in the release | ||
const {data: {items: prs}} = await github.search.issues({ | ||
q: `${commits.map(commit => commit.hash).join('+')}+repo:${owner}/${repo}+type:pr`, | ||
}); | ||
|
||
debug('found pull requests: %O', prs.map(pr => pr.number)); | ||
|
||
// Parse the release commits message and PRs body to find resolved issues/PRs via comment keyworkds | ||
const issues = uniqBy( | ||
[...prs.map(pr => pr.body), ...commits.map(commit => commit.message)] | ||
.reduce((issues, message) => { | ||
return message | ||
? issues.concat(issueParser(message).actions.map(action => ({number: parseInt(action.issue, 10)}))) | ||
: issues; | ||
}, []) | ||
.filter(issue => !prs.find(pr => pr.number === issue.number)), | ||
'number' | ||
); | ||
|
||
debug('found issues via comments: %O', issues); | ||
|
||
const errors = []; | ||
|
||
// Make requests serially to avoid hitting the rate limit (https://developer.github.com/v3/guides/best-practices-for-integrators/#dealing-with-abuse-rate-limits) | ||
await pReduce([...prs, ...issues], async (_, issue) => { | ||
const body = successComment | ||
? template(successComment)({branch, lastRelease, commits, nextRelease, releases, issue}) | ||
: getSuccessComment(issue, releaseInfos, nextRelease); | ||
try { | ||
const comment = {owner, repo, number: issue.number, body}; | ||
debug('create comment: %O', comment); | ||
const {data: {html_url: url}} = await github.issues.createComment(comment); | ||
logger.log('Added comment to issue #%d: %s', issue.number, url); | ||
} catch (err) { | ||
errors.push(err); | ||
logger.error('Failed to add a comment to the issue #%d.', issue.number); | ||
// Don't throw right away and continue to update other issues | ||
} | ||
}); | ||
if (errors.length > 0) { | ||
throw new AggregateError(errors); | ||
} | ||
}; |
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,94 @@ | ||
import test from 'ava'; | ||
import getSuccessComment from '../lib/get-success-comment'; | ||
|
||
const HOME_URL = 'https://github.com/semantic-release/semantic-release'; | ||
|
||
test('Comment for issue with multiple releases', t => { | ||
const issue = {number: 1}; | ||
const releaseInfos = [ | ||
{name: 'GitHub release', url: 'https://github.com/release'}, | ||
{name: 'npm release', url: 'https://npm.com/release'}, | ||
]; | ||
const nextRelease = {version: '1.0.0'}; | ||
const comment = getSuccessComment(issue, releaseInfos, nextRelease); | ||
|
||
t.is( | ||
comment, | ||
`:tada: This issue has been resolved in version 1.0.0 :tada: | ||
The release is available on: | ||
- [GitHub release](https://github.com/release) | ||
- [npm release](https://npm.com/release) | ||
Your **[semantic-release](${HOME_URL})** bot :package::rocket:` | ||
); | ||
}); | ||
|
||
test('Comment for PR with multiple releases', t => { | ||
const issue = {number: 1, pull_request: {}}; // eslint-disable-line camelcase | ||
const releaseInfos = [ | ||
{name: 'GitHub release', url: 'https://github.com/release'}, | ||
{name: 'npm release', url: 'https://npm.com/release'}, | ||
]; | ||
const nextRelease = {version: '1.0.0'}; | ||
const comment = getSuccessComment(issue, releaseInfos, nextRelease); | ||
|
||
t.is( | ||
comment, | ||
`:tada: This PR is included in version 1.0.0 :tada: | ||
The release is available on: | ||
- [GitHub release](https://github.com/release) | ||
- [npm release](https://npm.com/release) | ||
Your **[semantic-release](${HOME_URL})** bot :package::rocket:` | ||
); | ||
}); | ||
|
||
test('Comment with missing release URL', t => { | ||
const issue = {number: 1}; | ||
const releaseInfos = [{name: 'GitHub release', url: 'https://github.com/release'}, {name: 'npm release'}]; | ||
const nextRelease = {version: '1.0.0'}; | ||
const comment = getSuccessComment(issue, releaseInfos, nextRelease); | ||
|
||
t.is( | ||
comment, | ||
`:tada: This issue has been resolved in version 1.0.0 :tada: | ||
The release is available on: | ||
- [GitHub release](https://github.com/release) | ||
- \`npm release\` | ||
Your **[semantic-release](${HOME_URL})** bot :package::rocket:` | ||
); | ||
}); | ||
|
||
test('Comment with one release', t => { | ||
const issue = {number: 1}; | ||
const releaseInfos = [{name: 'GitHub release', url: 'https://github.com/release'}]; | ||
const nextRelease = {version: '1.0.0'}; | ||
const comment = getSuccessComment(issue, releaseInfos, nextRelease); | ||
|
||
t.is( | ||
comment, | ||
`:tada: This issue has been resolved in version 1.0.0 :tada: | ||
The release is available on [GitHub release](https://github.com/release) | ||
Your **[semantic-release](${HOME_URL})** bot :package::rocket:` | ||
); | ||
}); | ||
|
||
test('Comment with no release object', t => { | ||
const issue = {number: 1}; | ||
const releaseInfos = []; | ||
const nextRelease = {version: '1.0.0'}; | ||
const comment = getSuccessComment(issue, releaseInfos, nextRelease); | ||
|
||
t.is( | ||
comment, | ||
`:tada: This issue has been resolved in version 1.0.0 :tada: | ||
Your **[semantic-release](${HOME_URL})** bot :package::rocket:` | ||
); | ||
}); |
Oops, something went wrong.