From 8620727708a4c1232439ae5200448492b260b3ee Mon Sep 17 00:00:00 2001 From: Daniel Infante Vargas Date: Wed, 5 Aug 2020 16:10:18 -0500 Subject: [PATCH 1/6] Check issue body --- README.md | 60 ++++++++----- action.yml | 16 ++-- package.json | 4 +- src/index.js | 206 +++++++++++++++++++++++++-------------------- test/index.test.js | 46 ++++++++++ 5 files changed, 209 insertions(+), 123 deletions(-) create mode 100644 test/index.test.js diff --git a/README.md b/README.md index 1c271aa..af74ce4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Verify Linked Issue Action -A GitHub action that verifies your pull request contains a reference to an issue. + +A GitHub action that verifies your pull request contains a reference to an issue. On a PR that does not include a linked issue or reference to an issue in the body, the check should fail and a comment will be added to the PR. @@ -8,24 +9,28 @@ On a PR that does not include a linked issue or reference to an issue in the bod ## Installation ### As a part of an existing workflow -``` yaml + +```yaml - name: Verify Linked Issue - uses: hattan/verify-linked-issue-action@v1.1.1 + uses: hattan/verify-linked-issue-action@v1.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` Pleasure ensure the following types on the pull_request triggers: + ```yaml - pull_request: - types: [edited, synchronize, opened, reopened] +pull_request: + types: [edited, synchronize, opened, reopened] ``` ### As a separate workflow -* Ensure you have the folder .github/workflows -* In .github/workflows, place the [pr_verify_linked_issue.yml](example/pr_verify_linked_issue.yml) workflow. + +- Ensure you have the folder .github/workflows +- In .github/workflows, place the [pr_verify_linked_issue.yml](example/pr_verify_linked_issue.yml) workflow. ### Inputs + (Optional) The action will add the following text to a PR when verification fails. 'Build Error! No Linked Issue found. Please link an issue or mention it in the body using #' @@ -33,41 +38,54 @@ You can customize this message by providing an optional 'message' input with the ```yaml - name: Verify Linked Issue - uses: hattan/verify-linked-issue-action@v1.1.1 + uses: hattan/verify-linked-issue-action@v1.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - message: 'Error! This is a custom error' - ``` + message: "Error! This is a custom error" +``` + +You can also provide an alternative ACCESS_TOKEN instead of the GITHUB_TOKEN, in case you need access to different repos, for example. + +```yaml +- name: Verify Linked Issue + uses: hattan/verify-linked-issue-action@v1.2.0 + env: + ACCESS_TOKEN: ${{ secrets.SECRET_NAME }} + with: + message: "Error! This is a custom error" +``` ### File Templates + If you want a more complex message, consider using a static template file. (Support for dynamic templates will be coming soon!) There are two options when using template files: -* Option 1) Default File Path: Add a file to .github called VERIFY_PR_COMMENT_TEMPLATE.md. The content of this file will be used as the fail comment in the PR. -* Option 2) Speciy a filename input with the path to a template file. +- Option 1) Default File Path: Add a file to .github called VERIFY_PR_COMMENT_TEMPLATE.md. The content of this file will be used as the fail comment in the PR. +- Option 2) Speciy a filename input with the path to a template file. + ```yaml - name: Verify Linked Issue - uses: hattan/verify-linked-issue-action@v1.1.1 + uses: hattan/verify-linked-issue-action@v1.2.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - filename: 'example/templates/fail_comment.txt' + filename: "example/templates/fail_comment.txt" ``` ## Trying it out -* Create a new pull request and take care to not include a linked item or mention an issue. -* The build should fail. -* Edit the PR body and add a reference to a valid issue (e.g. #123 ) +- Create a new pull request and take care to not include a linked item or mention an issue. +- The build should fail. +- Edit the PR body and add a reference to a valid issue (e.g. #123 ) ![Failed Build log](images/failed1.png "Failed Build log") + ## Known Issues -* There must be a space after the issue number (ie "#12 " not "#12".) This is due to the way the RegEx is structured and will be resolved in a future release. -* The Issue reference by # needs to be in the body, we don't currently look in the title. That is a future enhancement. - -v1 +- There must be a space after the issue number (ie "#12 " not "#12".) This is due to the way the RegEx is structured and will be resolved in a future release. +- The Issue reference by # needs to be in the body, we don't currently look in the title. That is a future enhancement. +v1 diff --git a/action.yml b/action.yml index 3f6236e..129ac1a 100644 --- a/action.yml +++ b/action.yml @@ -1,13 +1,15 @@ -name: 'Verify Linked Issue' -description: 'A GitHub action that verifies a pull request contains a reference to an issue.' +name: "Verify Linked Issue" +description: "A GitHub action that verifies a pull request contains a reference to an issue." branding: - icon: check-square - color: green + icon: check-square + color: green inputs: message: description: The text to use as the PR comen for when for pull requests with no linked issues. + required: false filename: - description: The name of the file to use as the comment template for pull requests with no linked issues. + description: The name of the file to use as the comment template for pull requests with no linked issues. + required: false runs: - using: 'node12' - main: 'src/index.js' \ No newline at end of file + using: "node12" + main: "src/index.js" diff --git a/package.json b/package.json index 24661f4..2a61e9b 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "verify-linked-issue-action", - "version": "1.0.0", + "version": "1.2.0", "description": "A GitHub action that verifies your pull request contains a reference to an issue.", "main": "index.js", "directories": { "example": "example" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "GITHUB_TOKEN='' node test/index.test.js" }, "repository": { "type": "git", diff --git a/src/index.js b/src/index.js index 2d30cb7..cc8909c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,119 +1,92 @@ - -const core = require('@actions/core') -const { Toolkit } = require('actions-toolkit') - -Toolkit.run(async tools => { - try { - if(!tools.context.payload.pull_request){ - tools.log.warn('Not a pull request skipping verification!'); +const core = require("@actions/core"); +const { Toolkit } = require("actions-toolkit"); + +Toolkit.run( + async (tools) => { + try { + if (!tools.context.payload.pull_request) { + tools.log.warn("Not a pull request skipping verification!"); return; - } + } - tools.log.debug('Starting Linked Issue Verification!'); - await verifyLinkedIssue(tools); - - } catch (err) { - tools.log.error(`Error verifying linked issue.`) - tools.log.error(err) - - if (err.errors) tools.log.error(err.errors) - const errorMessage = "Error verifying linked issue." - core.setFailed(errorMessage + '\n\n' + err.message) - tools.exit.failure() + await verifyLinkedIssue(tools); + } catch (err) { + tools.log.error(`Error verifying linked issue.`); + tools.log.error(err); + + if (err.errors) tools.log.error(err.errors); + const errorMessage = "Error verifying linked issue."; + core.setFailed(errorMessage + "\n\n" + err.message); + tools.exit.failure(); + } + }, + { + secrets: ["GITHUB_TOKEN"], + token: process.env.ACCESS_TOKEN || process.env.GITHUB_TOKEN, } -}, { - secrets: ['GITHUB_TOKEN'] -}); +); async function verifyLinkedIssue(tools) { const context = tools.context, - github = tools.github, - log = tools.log; + github = tools.github, + log = tools.log; - let linkedIssue = await checkBodyForValidIssue(context, github, log); + const linkedIssue = await checkBodyForValidIssue(context, github, log); - if (!linkedIssue) { - linkedIssue = await checkEventsListForConnectedEvent(context, github, log); - } - - if(linkedIssue){ - log.success("Success! Linked Issue Found!"); - } - else{ - await createMissingIssueComment(context, github, log, tools); - log.error("No Linked Issue Found!"); - core.setFailed("No Linked Issue Found!"); - tools.exit.failure() + if (linkedIssue) { + log.success("Success! Valid linked issue found!"); + } else { + await createMissingIssueComment(context, github, log, tools); + core.setFailed("No valid linked issue found!"); + tools.exit.failure(); } } -async function checkBodyForValidIssue(context, github, log){ +async function checkBodyForValidIssue(context, github, log) { let body = context.payload.pull_request.body; - log.debug(`Checking PR Body: "${body}"`) - const re = /#(.*?)[\s]/g; - const matches = body.match(re); - log.debug(`regex matches: ${matches}`) - if(matches){ - for(let i=0,len=matches.length;i { - if (item.event == "connected"){ - log.debug(`Found connected event.`); - return true; + return checkIssue(issue.data, log); } - }); + } catch (error) { + log.error(error); + log.debug(`#${issueNumber} is not a valid issue.`); + } } + return false; } -async function createMissingIssueComment(context,github, log, tools ) { - const defaultMessage = 'Build Error! No Linked Issue found. Please link an issue or mention it in the body using #'; - let messageBody = core.getInput('message'); - if(!messageBody){ - let filename = core.getInput('filename'); - if(!filename){ - filename = '.github/VERIFY_PR_COMMENT_TEMPLATE.md'; +async function createMissingIssueComment(context, github, log, tools) { + const defaultMessage = + "No valid linked issue found. Check the Lint PR action for details."; + let messageBody = core.getInput("message"); + if (!messageBody) { + let filename = core.getInput("filename"); + if (!filename) { + filename = ".github/VERIFY_PR_COMMENT_TEMPLATE.md"; } - try{ + try { const file = tools.getFile(filename); - if(file){ + if (file) { messageBody = file; - } - else{ + } else { messageBody = defaultMessage; } - } - catch{ + } catch { messageBody = defaultMessage; } } @@ -123,8 +96,55 @@ async function createMissingIssueComment(context,github, log, tools ) { issue_number: context.payload.pull_request.number, owner: context.repo.owner, repo: context.repo.repo, - body: messageBody + body: messageBody, }); } +function extractIssues(body) { + const re = /((\S+)\/(\S+))?#(\d+)\b/g; + let match = re.exec(body); + let matches = []; + + while (match !== null) { + matches.push({ + owner: match[2], + repo: match[3], + issueNumber: match[4], + }); + + match = re.exec(body); + } + + return matches; +} + +async function getIssue(github, owner, repo, issueNumber) { + return await github.issues.get({ + owner, + repo, + issue_number: issueNumber, + }); +} + +function checkIssue(issue, log) { + if (!issue.body) { + log.error("The issue body is empty."); + return false; + } + + if (issue.labels.length === 0) { + log.error("The issue is not labeled."); + return false; + } + + if (!issue.milestone) { + log.error("The issue is not linked to a milestone."); + return false; + } + + log.debug(`Issue #${issue.number} is valid!`); + + return true; +} +module.exports = { extractIssues, checkIssue }; diff --git a/test/index.test.js b/test/index.test.js new file mode 100644 index 0000000..49bc3ac --- /dev/null +++ b/test/index.test.js @@ -0,0 +1,46 @@ +const assert = require("assert"); + +const { extractIssues, checkIssue } = require("../src/index"); + +assert.deepStrictEqual(extractIssues("Closes owner/repo#944")[0], { + owner: "owner", + repo: "repo", + issueNumber: "944", +}); + +assert.deepStrictEqual(extractIssues("Closes owner/repo#944\n")[0], { + owner: "owner", + repo: "repo", + issueNumber: "944", +}); + +assert.deepStrictEqual(extractIssues("Closes owner/repo#944 ")[0], { + owner: "owner", + repo: "repo", + issueNumber: "944", +}); + +assert.deepStrictEqual( + extractIssues("Closes owner_with_underscore/repo-with-dash#944")[0], + { + owner: "owner_with_underscore", + repo: "repo-with-dash", + issueNumber: "944", + } +); + +assert.deepStrictEqual(extractIssues("Closes #944")[0], { + owner: undefined, + repo: undefined, + issueNumber: "944", +}); + +assert(!checkIssue({ body: "", labels: [], milestone: null }, console)); + +assert(!checkIssue({ body: "Body", labels: [], milestone: null }, console)); + +assert(!checkIssue({ body: "Body", labels: [{}], milestone: null }, console)); + +assert( + checkIssue({ body: "Body", labels: [{}], milestone: {}, number: 5 }, console) +); From b28b4fb3380dcea9727e09d6d8969d7e1d5aa97a Mon Sep 17 00:00:00 2001 From: daninfpj Date: Wed, 5 Aug 2020 20:32:39 -0500 Subject: [PATCH 2/6] Update README.md Always include GITHUB_TOKEN, as the workflow will check for it. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index af74ce4..da3030d 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ You can also provide an alternative ACCESS_TOKEN instead of the GITHUB_TOKEN, in - name: Verify Linked Issue uses: hattan/verify-linked-issue-action@v1.2.0 env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ACCESS_TOKEN: ${{ secrets.SECRET_NAME }} with: message: "Error! This is a custom error" From d96d465a1c4c3d0424d91854bd80895f272e9460 Mon Sep 17 00:00:00 2001 From: Daniel Infante Vargas Date: Wed, 5 Aug 2020 20:39:53 -0500 Subject: [PATCH 3/6] Fix matches loop --- src/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index cc8909c..6256f02 100644 --- a/src/index.js +++ b/src/index.js @@ -45,19 +45,21 @@ async function verifyLinkedIssue(tools) { async function checkBodyForValidIssue(context, github, log) { let body = context.payload.pull_request.body; log.debug(`Checking PR Body: "${body}"…`); + const matches = extractIssues(body); - for (match in extractIssues(body)) { - log.debug(`Regex matches: ${match}`); + for (i in matches) { + const match = matches[i]; + log.debug(`Regex match: ${match}`); const issueNumber = match.issueNumber; const owner = match.owner || context.repo.owner; const repo = match.repo || context.repo.repo; - log.debug(`Verfiying match is a valid issue #${issueNumber}…`); + log.debug(`Verfiying #${issueNumber} is a valid issue…`); try { let issue = await getIssue(github, owner, repo, issueNumber); if (issue) { - log.debug(`Found issue #${issueNumber} in PR Body.`); + log.debug(`Found issue #${issueNumber} from PR Body.`); return checkIssue(issue.data, log); } From e046670ddb6044e1f6908c688aa21536c204d382 Mon Sep 17 00:00:00 2001 From: Daniel Infante Vargas Date: Wed, 5 Aug 2020 20:48:52 -0500 Subject: [PATCH 4/6] Bump to 1.2.1 --- README.md | 8 ++++---- package.json | 2 +- src/index.js | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index da3030d..d1f8f0f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ On a PR that does not include a linked issue or reference to an issue in the bod ```yaml - name: Verify Linked Issue - uses: hattan/verify-linked-issue-action@v1.2.0 + uses: Fondeadora/verify-linked-issue-action@v1.2.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` @@ -38,7 +38,7 @@ You can customize this message by providing an optional 'message' input with the ```yaml - name: Verify Linked Issue - uses: hattan/verify-linked-issue-action@v1.2.0 + uses: Fondeadora/verify-linked-issue-action@v1.2.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -49,7 +49,7 @@ You can also provide an alternative ACCESS_TOKEN instead of the GITHUB_TOKEN, in ```yaml - name: Verify Linked Issue - uses: hattan/verify-linked-issue-action@v1.2.0 + uses: Fondeadora/verify-linked-issue-action@v1.2.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ACCESS_TOKEN: ${{ secrets.SECRET_NAME }} @@ -68,7 +68,7 @@ There are two options when using template files: ```yaml - name: Verify Linked Issue - uses: hattan/verify-linked-issue-action@v1.2.0 + uses: Fondeadora/verify-linked-issue-action@v1.2.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/package.json b/package.json index 2a61e9b..a672947 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "verify-linked-issue-action", - "version": "1.2.0", + "version": "1.2.1", "description": "A GitHub action that verifies your pull request contains a reference to an issue.", "main": "index.js", "directories": { diff --git a/src/index.js b/src/index.js index 6256f02..f0da592 100644 --- a/src/index.js +++ b/src/index.js @@ -49,7 +49,6 @@ async function checkBodyForValidIssue(context, github, log) { for (i in matches) { const match = matches[i]; - log.debug(`Regex match: ${match}`); const issueNumber = match.issueNumber; const owner = match.owner || context.repo.owner; From 1a7e899b27272796fdfbda2b752ca267750d594a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Dec 2020 18:03:24 +0000 Subject: [PATCH 5/6] Bump @actions/core from 1.2.4 to 1.2.6 Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.2.4 to 1.2.6. - [Release notes](https://github.com/actions/toolkit/releases) - [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md) - [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core) Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index be82eb7..12b1eb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "verify-linked-issue-action", - "version": "1.0.0", + "version": "1.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { "@actions/core": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz", - "integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz", + "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==" }, "@actions/github": { "version": "2.2.0", diff --git a/package.json b/package.json index a672947..ad0cf60 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "homepage": "https://github.com/hattan/verify-linked-issue-action#readme", "dependencies": { - "@actions/core": "^1.2.4", + "@actions/core": "^1.2.6", "@actions/github": "^2.2.0", "actions-toolkit": "^4.0.0" } From 6c370de97965f7404de7ab5fb51e5b6463c42b7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Dec 2020 18:03:29 +0000 Subject: [PATCH 6/6] Bump node-fetch from 2.6.0 to 2.6.1 Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1. - [Release notes](https://github.com/bitinn/node-fetch/releases) - [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1) Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index be82eb7..07b6b02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "verify-linked-issue-action", - "version": "1.0.0", + "version": "1.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -683,9 +683,9 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "npm-run-path": { "version": "2.0.2",