diff --git a/communicate-on-pull-request-merged/README.md b/communicate-on-pull-request-merged/README.md index 94fce5c..2f2303c 100755 --- a/communicate-on-pull-request-merged/README.md +++ b/communicate-on-pull-request-merged/README.md @@ -11,7 +11,7 @@ steps: - uses: fastlane/github-action/communicate-on-pull-request-merged@latest with: repo-token: ${{ secrets.GITHUB_TOKEN }} - pr-comment: "Hey @${{ github.event.pull_request.user.login }} :wave: Thank you for your contribution!" + pr-comment: "Hey :wave: Thank you for your contribution!" ``` # License diff --git a/communicate-on-pull-request-merged/__tests__/action-opened.json b/communicate-on-pull-request-merged/__tests__/action-opened.json deleted file mode 100644 index 59258f2..0000000 --- a/communicate-on-pull-request-merged/__tests__/action-opened.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "pull_request": { - "number": 10, - "body": "This is a pr description.", - "merged": false - }, - "action": "opened" -} \ No newline at end of file diff --git a/communicate-on-pull-request-merged/__tests__/issue.json b/communicate-on-pull-request-merged/__tests__/issue.json deleted file mode 100644 index 335af13..0000000 --- a/communicate-on-pull-request-merged/__tests__/issue.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "issue": { - "number": 10, - "body": "This is an issue description." - }, - "action": "opened" -} \ No newline at end of file diff --git a/communicate-on-pull-request-merged/__tests__/main.test.ts b/communicate-on-pull-request-merged/__tests__/main.test.ts index 2330fef..2add295 100755 --- a/communicate-on-pull-request-merged/__tests__/main.test.ts +++ b/communicate-on-pull-request-merged/__tests__/main.test.ts @@ -1,72 +1,94 @@ const path = require('path'); const nock = require('nock'); -const validScenarios = [ - { - response: 'pull-request-closed.json' - } -]; - const invalidScenarios = [ { - response: 'issue.json' + scenario_name: 'no-push-event', + event_name: 'pull_request', + sha: '', + commits: '', + pulls: '' }, { - response: 'action-opened.json' + scenario_name: 'no-commit-sha', + event_name: 'push', + sha: '', + commits: '', + pulls: '' }, { - response: 'pull-request-closed-but-not-merged' + scenario_name: 'no-merge-changes', + event_name: 'push', + sha: '123abc', + commits: '', + pulls: '' + }, + { + scenario_name: 'no-pull-request-for-a-given-commit', + event_name: 'push', + sha: '123abc', + commits: JSON.parse( + '{"parents": [{"url": "0", "sha": "0"}, {"url": "1", "sha": "1"}]}' + ), + pulls: '' } ]; describe('action test suite', () => { - for (const scenario of validScenarios) { - it(`It posts a comment on a merged issue for (${scenario.response})`, async () => { - process.env['INPUT_REPO-TOKEN'] = 'token'; - process.env['INPUT_PR-COMMENT'] = 'message'; - process.env['INPUT_PR-LABEL-TO-ADD'] = 'label-to-add'; - process.env['INPUT_PR-LABEL-TO-REMOVE'] = 'label-to-remove'; + it(`It posts a comment on a merged pull request, adds and removes the labels`, async () => { + process.env['INPUT_REPO-TOKEN'] = 'token'; + process.env['INPUT_PR-COMMENT'] = 'message'; + process.env['INPUT_PR-LABEL-TO-ADD'] = 'label-to-add'; + process.env['INPUT_PR-LABEL-TO-REMOVE'] = 'label-to-remove'; - process.env['GITHUB_REPOSITORY'] = 'foo/bar'; - process.env['GITHUB_EVENT_PATH'] = path.join( - __dirname, - scenario.response - ); + process.env['GITHUB_EVENT_NAME'] = 'push'; + process.env['GITHUB_REPOSITORY'] = 'foo/bar'; + process.env['GITHUB_SHA'] = 'abc123'; - const api = nock('https://api.github.com') - .persist() - .post( - '/repos/foo/bar/pulls/10/reviews', - '{"body":"message","event":"COMMENT"}' + const api = nock('https://api.github.com') + .persist() + .get('/repos/foo/bar/git/commits/abc123') + .reply( + 200, + JSON.parse( + '{"parents": [{"url": "hello-0", "sha": "1acc"}, {"url": "hello-1", "sha": "2acc"}]}' ) - .reply(200) - .get('/repos/foo/bar/issues/10/labels') - .reply(200, JSON.parse('[]')) - .post('/repos/foo/bar/issues/10/labels', '{"labels":["label-to-add"]}') - .reply(200); + ) + .get('/repos/foo/bar/commits/abc123/pulls') + .reply(200, JSON.parse('[{"number": 10, "state": "closed"}]')) + .post( + '/repos/foo/bar/pulls/10/reviews', + '{"body":"message","event":"COMMENT"}' + ) + .reply(200) + .get('/repos/foo/bar/issues/10/labels') + .reply(200, JSON.parse('[]')) + .post('/repos/foo/bar/issues/10/labels', '{"labels":["label-to-add"]}') + .reply(200); - const main = require('../src/main'); - await main.run(); + const main = require('../src/main'); + await main.run(); - expect(api.isDone()).toBeTruthy(); - }); - } + expect(api.isDone()).toBeTruthy(); + }); for (const scenario of invalidScenarios) { - it(`It does not post a comment on a closed pull request for (${scenario.response})`, async () => { + it(`It does not post a comment on a closed pull request for (${scenario.scenario_name})`, async () => { process.env['INPUT_REPO-TOKEN'] = 'token'; process.env['INPUT_PR-COMMENT'] = 'message'; process.env['INPUT_PR-LABEL-TO-ADD'] = 'label-to-add'; process.env['INPUT_PR-LABEL-TO-REMOVE'] = 'label-to-remove'; + process.env['GITHUB_EVENT_NAME'] = scenario.event_name; process.env['GITHUB_REPOSITORY'] = 'foo/bar'; - process.env['GITHUB_EVENT_PATH'] = path.join( - __dirname, - scenario.response - ); + process.env['GITHUB_SHA'] = scenario.sha; const api = nock('https://api.github.com') .persist() + .get(`/repos/foo/bar/git/commits/${scenario.sha}`) + .reply(200, `${scenario.commits}`) + .get(`/repos/foo/bar/commits/${scenario.sha}/pulls`) + .reply(200, `${scenario.pulls}`) .post( '/repos/foo/bar/pulls/10/reviews', '{"body":"message","event":"COMMENT"}' diff --git a/communicate-on-pull-request-merged/__tests__/pull-request-closed-but-not-merged.json b/communicate-on-pull-request-merged/__tests__/pull-request-closed-but-not-merged.json deleted file mode 100644 index e3e9cd2..0000000 --- a/communicate-on-pull-request-merged/__tests__/pull-request-closed-but-not-merged.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "pull_request": { - "number": 10, - "body": "This is a pr description.", - "merged": false - }, - "action": "closed" -} \ No newline at end of file diff --git a/communicate-on-pull-request-merged/__tests__/pull-request-closed.json b/communicate-on-pull-request-merged/__tests__/pull-request-closed.json deleted file mode 100644 index acc0f54..0000000 --- a/communicate-on-pull-request-merged/__tests__/pull-request-closed.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "pull_request": { - "number": 10, - "body": "This is a pr description.", - "merged": true - }, - "action": "closed" -} \ No newline at end of file diff --git a/communicate-on-pull-request-merged/action.yml b/communicate-on-pull-request-merged/action.yml index 2826c36..ff52b73 100755 --- a/communicate-on-pull-request-merged/action.yml +++ b/communicate-on-pull-request-merged/action.yml @@ -7,13 +7,14 @@ inputs: required: true pr-comment: description: 'A comment to post on a pull request when code changes are merged' - required: true + required: false + # The default value is taken (see the source code of the action) when no `pr-comment` is provided pr-label-to-add: description: 'The label to apply when a pull request is merged' default: 'status: included-in-next-release' pr-label-to-remove: - description: 'The label to remove when a pull request is merged' - default: 'status: needs-attention' + description: 'The label to remove when a pull request is merged' + default: 'status: needs-attention' runs: using: 'docker' image: 'Dockerfile' diff --git a/communicate-on-pull-request-merged/lib/main.js b/communicate-on-pull-request-merged/lib/main.js index e205be2..29ea123 100644 --- a/communicate-on-pull-request-merged/lib/main.js +++ b/communicate-on-pull-request-merged/lib/main.js @@ -20,21 +20,22 @@ const github = __importStar(require("@actions/github")); function run() { return __awaiter(this, void 0, void 0, function* () { try { - const isPullRequest = !!github.context.payload.pull_request; - if (!isPullRequest) { - console.log('The event that triggered this action was not a pull request, exiting'); - return; - } - if (github.context.payload.action !== 'closed') { - console.log('No pull request was closed, exiting'); + if (github.context.eventName !== 'push') { + console.log('The event that triggered this action was not a push, exiting'); return; } const repoToken = core.getInput('repo-token', { required: true }); const client = new github.GitHub(repoToken); - const prNumber = github.context.payload.pull_request.number; - const merged = github.context.payload.pull_request['merged']; - if (!merged) { - console.log('No pull request was merged, exiting'); + const commit = yield getCommit(client, github.context.sha); + if (!isMergeCommit(commit)) { + console.log('No merge commit, exiting'); + return; + } + const { data: [pullRequest] } = yield getPullRequests(client, github.context.sha); + const prNumber = pullRequest.number; + const closed = pullRequest.state == 'closed'; + if (!closed) { + console.log('No pull request was closed, exiting'); return; } const labelToRemove = core.getInput('pr-label-to-remove'); @@ -43,7 +44,11 @@ function run() { yield removeLabel(client, prNumber, labelToRemove); } yield addLabels(client, prNumber, [core.getInput('pr-label-to-add')]); - yield addComment(client, prNumber, core.getInput('pr-comment', { required: true })); + var comment = core.getInput('pr-comment', { required: false }); + if (comment.length == 0) { + comment = defaultPrComment(pullRequest.user.login); + } + yield addComment(client, prNumber, comment); } catch (error) { core.setFailed(error.message); @@ -51,6 +56,27 @@ function run() { }); } exports.run = run; +function getCommit(client, commit_sha) { + return __awaiter(this, void 0, void 0, function* () { + return yield client.git.getCommit({ + owner: github.context.repo.owner, + repo: github.context.repo.repo, + commit_sha: commit_sha + }); + }); +} +function isMergeCommit(commit) { + return commit.data.parents.length > 1; +} +function getPullRequests(client, commit_sha) { + return __awaiter(this, void 0, void 0, function* () { + return yield client.repos.listPullRequestsAssociatedWithCommit({ + owner: github.context.repo.owner, + repo: github.context.repo.repo, + commit_sha: commit_sha + }); + }); +} function addComment(client, prNumber, comment) { return __awaiter(this, void 0, void 0, function* () { yield client.pulls.createReview({ @@ -98,4 +124,14 @@ function removeLabel(client, prNumber, label) { }); }); } +function defaultPrComment(prAuthor) { + return `Hey @${prAuthor} :wave: + + Thank you for your contribution to _fastlane_ and congrats on getting this pull request merged :tada: + The code change now lives in the \`master\` branch, however it wasn't released to [RubyGems](https://rubygems.org/gems/fastlane) yet. + We usually ship about once a week, and your PR will be included in the next one. + + Please let us know if this change requires an immediate release by adding a comment here :+1: + We'll notify you once we shipped a new release with your changes :rocket:`; +} run(); diff --git a/communicate-on-pull-request-merged/src/main.ts b/communicate-on-pull-request-merged/src/main.ts index 409ea2b..64943d1 100755 --- a/communicate-on-pull-request-merged/src/main.ts +++ b/communicate-on-pull-request-merged/src/main.ts @@ -3,26 +3,29 @@ import * as github from '@actions/github'; export async function run() { try { - const isPullRequest: boolean = !!github.context.payload.pull_request; - if (!isPullRequest) { + if (github.context.eventName !== 'push') { console.log( - 'The event that triggered this action was not a pull request, exiting' + 'The event that triggered this action was not a push, exiting' ); return; } - if (github.context.payload.action !== 'closed') { - console.log('No pull request was closed, exiting'); - return; - } - const repoToken = core.getInput('repo-token', {required: true}); const client: github.GitHub = new github.GitHub(repoToken); - const prNumber = github.context.payload.pull_request!.number; - const merged = github.context.payload.pull_request!['merged']; - if (!merged) { - console.log('No pull request was merged, exiting'); + const commit = await getCommit(client, github.context.sha); + if (!isMergeCommit(commit)) { + console.log('No merge commit, exiting'); + return; + } + + const { + data: [pullRequest] + } = await getPullRequests(client, github.context.sha); + const prNumber = pullRequest.number; + const closed = pullRequest.state == 'closed'; + if (!closed) { + console.log('No pull request was closed, exiting'); return; } @@ -37,16 +40,37 @@ export async function run() { } await addLabels(client, prNumber, [core.getInput('pr-label-to-add')]); - await addComment( - client, - prNumber, - core.getInput('pr-comment', {required: true}) - ); + + var comment = core.getInput('pr-comment', {required: false}); + if (comment.length == 0) { + comment = defaultPrComment(pullRequest.user.login); + } + await addComment(client, prNumber, comment); } catch (error) { core.setFailed(error.message); } } +async function getCommit(client: github.GitHub, commit_sha: string) { + return await client.git.getCommit({ + owner: github.context.repo.owner, + repo: github.context.repo.repo, + commit_sha: commit_sha + }); +} + +function isMergeCommit(commit): boolean { + return commit.data.parents.length > 1; +} + +async function getPullRequests(client: github.GitHub, commit_sha: string) { + return await client.repos.listPullRequestsAssociatedWithCommit({ + owner: github.context.repo.owner, + repo: github.context.repo.repo, + commit_sha: commit_sha + }); +} + async function addComment( client: github.GitHub, prNumber: number, @@ -107,4 +131,15 @@ async function removeLabel( }); } +function defaultPrComment(prAuthor: string): string { + return `Hey @${prAuthor} :wave: + + Thank you for your contribution to _fastlane_ and congrats on getting this pull request merged :tada: + The code change now lives in the \`master\` branch, however it wasn't released to [RubyGems](https://rubygems.org/gems/fastlane) yet. + We usually ship about once a week, and your PR will be included in the next one. + + Please let us know if this change requires an immediate release by adding a comment here :+1: + We'll notify you once we shipped a new release with your changes :rocket:`; +} + run();