Repository navigation
Update and refactor pr trigger permissions #8815
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: gh-pages
Are you sure you want to change the base?
Changes from all commits
5b1727a
812c9b9
3a3da9c
830112c
9cd9241
2ee294a
77d3999
b03f2c4
50a218d
4785794
0905c0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,54 +1,182 @@ | ||
| // Import modules | ||
| const fs = require('fs'); | ||
| const postIssueComment = require('../utils/post-issue-comment'); | ||
|
|
||
| // Global variables | ||
| var github; | ||
| var context; | ||
| /** ************************************** | ||
| ** HELPER FUNCTIONS | ||
| *************************************** */ | ||
|
|
||
| async function main({ g, c }) { | ||
| github = g; | ||
| context = c; | ||
| /** | ||
| * Returns resulting Object from the GitHub API downloadArtifact call. | ||
| * This function is a wrapper for the downloadArtifact method in the | ||
| * octokit/rest.js client. | ||
| * https://octokit.github.io/rest.js/v18#actions-download-artifact | ||
| * @param {Object} packages.github - The octokit/rest.js client | ||
| * @param {Object} packages.context - The context of the workflow run | ||
| * @param {Object} artifact - The artifact | ||
| * @returns {Object} The resulting Object | ||
| */ | ||
| async function _downloadArtifact({github, context}, artifact) { | ||
| const { owner, repo } = context.repo; | ||
| const { id: artifact_id } = artifact; | ||
| return await github.rest.actions.downloadArtifact({ | ||
| owner, | ||
| repo, | ||
| artifact_id, | ||
| archive_format: 'zip', | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the resulting Object from the GitHub API listWorkflowRunArtifacts | ||
| * call. | ||
| * This function is a wrapper for the listWorkflowRunArtifacts method in the | ||
| * octokit/rest.js client. | ||
| * https://octokit.github.io/rest.js/v18#actions-list-workflow-run-artifacts | ||
| * @param {Object} packages.github - The octokit/rest.js client | ||
| * @param {Object} packages.context - The context of the workflow run | ||
| * @returns {Object} The resulting Object | ||
| */ | ||
| async function _listWorkflowRunArtifacts({ github, context }) { | ||
| const { owner, repo } = context.repo; | ||
| const { id: run_id } = context.payload.workflow_run; | ||
| return await github.rest.actions.listWorkflowRunArtifacts({ | ||
| owner, | ||
| repo, | ||
| run_id, | ||
| }); | ||
| } | ||
|
|
||
| // Retrieve body of context.payload and search for GitHub keywords followed by | ||
| // '#' + number. Exclude any matches that are in a comment within the PR body | ||
| /** | ||
| * Returns PR data containing the body, PR number, and owner | ||
| * by retrieving data from context.payload | ||
| * @param {Object} packages.context - The context of the workflow run | ||
| * @param {Object} packages.core - The @actions/core package | ||
| * @returns {Object} An object containing the pull request body, number and owner. | ||
| */ | ||
| function _retrievePRBody({ context, core }) { | ||
| const prBody = context.payload.pull_request.body; | ||
| const prNumber = context.payload.pull_request.number; | ||
| const prOwner = context.payload.pull_request.user.login; | ||
|
|
||
| core.info(`Found PR #${prNumber} authored by ${prOwner}`); | ||
|
|
||
|
|
||
| return { body: prBody, number: prNumber, owner: prOwner }; | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Returns the resulting Object from the GitHub API get issue call. | ||
| * This function servers as a wrapper for the get issue method in the | ||
| * octokit.rest.js client. | ||
| * https://octokit.github.io/rest.js/v20#issues-get | ||
| * @param {Object} packages.github - The octokit/rest.js client | ||
| * @param {Object} packages.context - The context of the workflow run | ||
| * @param {string} issueNum - The issue number to be looked up | ||
| * @returns | ||
| */ | ||
| async function _checkIssueExists({ github, context }, issueNum) { | ||
| return await github.rest.issues.get({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: issueNum, | ||
| }); | ||
| } | ||
|
|
||
| /** ************************************** | ||
| ** MAIN FUNCTIONS | ||
| *************************************** */ | ||
|
|
||
| /** | ||
| * Returns a PR comment if issue is not linked or linked issue could not be found. | ||
| * Otherwise, returns empty PR comment, indicating that the issue has been properly linked. | ||
| * @param {Object} packages.github - The octokit/rest.js client | ||
| * @param {Object} packages.context - The context of the workflow run | ||
| * @param {Object} packages.core - The @actions/core package | ||
| * @returns | ||
| */ | ||
| async function checkForLinkedIssue({ github, context, core }) { | ||
| const pr = _retrievePRBody({ context, core }); | ||
|
|
||
| // Search for GitHub keywords followed by | ||
| // '#' + number. Exclude any matches that are in a comment within the PR body. | ||
| const regex = /(?!<!--)(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s*#(\d+)(?![^<]*-->)/gi; | ||
| const match = prBody.match(regex); | ||
| let prComment; | ||
| const match = pr.body.match(regex); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @castillios ! Thank you for your patience as I reviewed this. Your code is looking great. I tested it and mostly saw successes, the artifact made, and the comment made on the PR when appropriate. There was one thing. It looks like this was already an issue in the code before, but in testing this, I noticed that when you submit with a blank PR message, it fails altogether.
I will link the fails and include the error message I am seeing:
To fix this, something like this might work: Maybe we assume people will put something in that PR, especially since there is a template that auto populates, so it would't really come up. Let me know what you think there.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @meghan-oconnell this is a good point, it's probably rare someone would submit a blank PR but the code should handle it if it does happen. |
||
|
|
||
| let prComment = ""; | ||
|
|
||
| if (!match) { | ||
| console.log('PR does not have a properly linked issue. Posting comment...'); | ||
| prComment = `@${prOwner}, this Pull Request is not linked to a valid issue. Above, on the first line of your PR, please link the number of the issue that you worked on using the format of 'Fixes #' + issue number, for example: **_Fixes #9876_**\n\nNote: Do **_not_** use the number of this PR.`; | ||
| } | ||
|
|
||
| else { | ||
| console.log(match[0]); | ||
| core.info('PR does not have a properly linked issue. Posting comment...'); | ||
| prComment = `@${pr.owner}, this Pull Request is not linked to a valid issue. Above, on the first line of your PR, please link the number of the issue that you worked on using the format of 'Fixes #' + issue number, for example: **_Fixes #9876_**\n\nNote: Do **_not_** use the number of this PR.`; | ||
| } else { | ||
| core.info(match[0]); | ||
| let [ keyword, linkNumber ] = match[0].replaceAll('#','').split(' '); | ||
| console.log(`Found a keyword: \'${keyword}\'. Checking for legitimate linked issue...`); | ||
|
|
||
| core.info(`Found a keyword: \'${keyword}\'. Checking for legitimate linked issue...`); | ||
|
|
||
| // Check if the linked issue exists in repo | ||
| // https://octokit.github.io/rest.js/v20#issues-get | ||
| try { | ||
| await github.rest.issues.get({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: linkNumber, | ||
| }); | ||
| console.log(`Found an issue: \'#${linkNumber}\' in repo. Reference is a legitimate linked issue.`); | ||
| await _checkIssueExists({ github, context }, linkNumber); | ||
| core.info(`Found an issue: \'#${linkNumber}\' in repo. Reference is a legitimate linked issue.`); | ||
| } | ||
| catch (error) { | ||
| console.log(`Couldn\'t find issue: \'#${linkNumber}\' in repo. Posting comment...`); | ||
| prComment = `@${prOwner}, the issue number referenced above as "**${keyword} #${linkNumber}**" is not found. Please replace with a valid issue number.`; | ||
| core.info(`Couldn\'t find issue: \'#${linkNumber}\' in repo. Posting comment...`); | ||
| prComment = `@${pr.owner}, the issue number referenced above as "**${keyword} #${linkNumber}**" is not found. Please replace with a valid issue number.`; | ||
| } | ||
| } | ||
|
|
||
| // If the prComment was given text, then post the comment to the PR | ||
| if (prComment) { | ||
| return JSON.stringify({ prNumber: pr.number, prComment}); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the file path of the downloaded artifact. | ||
| * The function downloads the artifact and stores it in a pre-determined | ||
| * location. | ||
| * @param {Object} packages.github - The octokit/rest.js client | ||
| * @param {Object} packages.context - The context of the workflow run | ||
| * @param {Object} packages.core - The @actions/core package | ||
| * @param {string} artifactName - The name of the artifact | ||
| * @returns {string} The file path of the downloaded artifact | ||
| */ | ||
| async function downloadPRCommentArtifact({ github, context }, artifactName) { | ||
| const artifacts = await _listWorkflowRunArtifacts({ github, context }); | ||
| const match = artifacts.data.artifacts.filter( | ||
| ({ name }) => name === artifactName | ||
| )[0]; | ||
|
|
||
| const download = await _downloadArtifact({github, context }, match); | ||
|
|
||
| const filepath = `${process.env.GITHUB_WORKSPACE}/${artifactName}.zip`; | ||
| fs.writeFileSync(filepath, Buffer.from(download.data)); | ||
| return filepath; | ||
| } | ||
|
|
||
| /** | ||
| * Calls postIssueComment to post a comment to the PR on GitHub | ||
| * This function us a wrapper for the postIssueComment method | ||
| * and will only be called if prComment contains text. | ||
| * @param {Object} packages.github - The octokit/rest.js client | ||
| * @param {Object} packages.context - The context of the workflow run | ||
| * @param {string} filepath - The path of the file | ||
| */ | ||
|
|
||
| async function postPRComment({ github, context, core }, filepath) { | ||
| const data = JSON.parse(fs.readFileSync(filepath, 'utf8')); | ||
| const { prNumber, prComment } = data; | ||
|
|
||
| if (prComment){ | ||
| core.info(`Posting PR comment...`) | ||
| postIssueComment(prNumber, prComment, github, context); | ||
| core.info(`Posted comment to PR #${prNumber}:`) | ||
| core.info(JSON.stringify(prComment)); | ||
| } else { | ||
| core.info(`No comment posted. Issue is properly linked.`) | ||
| } | ||
| } | ||
|
|
||
| module.exports = main; | ||
| module.exports = { | ||
| downloadPRCommentArtifact, | ||
| checkForLinkedIssue, | ||
| postPRComment, | ||
| }; | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @castillios! This work looks really good. I did have one question about the event actions we'd want to consider here and on line 2-4:
Do we want to include "reopened" as well?
Here are the docs I was looking at where I got this idea: https://docs.github.com/en/rest/using-the-rest-api/github-event-types?apiVersion=2026-03-10#event-payload-object-for-issuesevent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@meghan-oconnell I do think it would be a good idea to include reopened too.