Skip to content
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

feat: friendly error messages, add review comment output #374

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -89,3 +89,4 @@ body-check | Result of match for PR Body against configured regex.
branch-check | Result of check to ensure PR head is not protected branch.
title-check | Result of check to ensure PR title is formatted per [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)
watch-files-check | Result of check for watched files having been modified. True if no modifications found to watched files.
review-comment | The pull request review comment that the action will attempt to create/update.
2 changes: 2 additions & 0 deletions action.yml
Expand Up @@ -141,6 +141,8 @@ outputs:
description: 'Result of check to ensure PR title is formatted per [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)'
watch-files-check:
description: 'Result of check for watched files having been modified. True if no modifications found to watched files.'
review-comment:
description: 'Pull request review comment that the action will attempt to create/update.'
runs:
using: 'node16'
main: 'dist/index.js'
102 changes: 78 additions & 24 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

127 changes: 88 additions & 39 deletions src/main.ts
Expand Up @@ -16,6 +16,18 @@ type PullRequestReview = {
state: string
}

const errors = {
findExistingReview:
'An error was encountered when searching for a pull request review comment previously left by this action.',
checkingTeamMember:
'An error was encountered when checking whether the PR author is a team member.',
updatingReview:
'An error was encountered when updating a pull request review comment previously left by this action.',
creatingReview:
'An error was encountered when creating a pull request review comment.',
closingPullRequest: 'An error was encountered when closing the pull request.'
}

const repoToken = core.getInput('repo-token')
const ignoreAuthors = core.getMultilineInput('ignore-authors')
const ignoreTeamMembers = core.getBooleanInput('ignore-team-members')
Expand Down Expand Up @@ -168,13 +180,15 @@ async function run(): Promise<void> {
let reviewBody = ''
if (commentsToLeave.length > 0)
reviewBody = [baseComment, ...commentsToLeave].join('\n\n')
core.setOutput('review-comment', reviewBody)
await updateReview(
{owner: pr.owner, repo: pr.repo, pull_number: pr.number},
reviewBody
)
// Finally close PR if warranted
if (shouldClosePr) await closePullRequest(pr.number)
} else {
core.setOutput('review-comment', '')
await updateReview(
{owner: pr.owner, repo: pr.repo, pull_number: pr.number},
''
Expand All @@ -185,11 +199,15 @@ async function run(): Promise<void> {
}
}
async function closePullRequest(number: number) {
await client.rest.pulls.update({
...context.repo,
pull_number: number,
state: 'closed'
})
try {
await client.rest.pulls.update({
...context.repo,
pull_number: number,
state: 'closed'
})
} catch (error) {
if (error instanceof Error) throw new Error(errors.closingPullRequest)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd want to somehow log the original error regardless.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new commit just dropped - still haven't smoke tested, buyer beware

}
}
function escapeChecks(checkResult: boolean, message: string) {
core.info(message)
Expand All @@ -198,27 +216,38 @@ function escapeChecks(checkResult: boolean, message: string) {
core.setOutput('issue-label-check', checkResult)
core.setOutput('title-check', checkResult)
core.setOutput('watched-files-check', checkResult)
core.setOutput('review-comment', '')
}
async function listFiles(pullRequest: {
owner: string
repo: string
pull_number: number
}) {
const {data: files} = await client.rest.pulls.listFiles(pullRequest)
return files
try {
const {data: files} = await client.rest.pulls.listFiles(pullRequest)
return files
} catch (error) {
if (error instanceof Error) throw new Error(errors.findExistingReview)
return []
}
}
async function findExistingReview(pullRequest: {
owner: string
repo: string
pull_number: number
}): Promise<PullRequestReview | null> {
let review
const {data: reviews} = await client.rest.pulls.listReviews(pullRequest)
review = reviews.find(innerReview => {
return (innerReview?.user?.login ?? '') === 'github-actions[bot]'
})
if (review === undefined) review = null
return review
try {
let review
const {data: reviews} = await client.rest.pulls.listReviews(pullRequest)
review = reviews.find(innerReview => {
return (innerReview?.user?.login ?? '') === 'github-actions[bot]'
})
if (review === undefined) review = null
return review
} catch (error) {
if (error instanceof Error) throw new Error(errors.findExistingReview)
return null
}
}
async function updateReview(
pullRequest: {owner: string; repo: string; pull_number: number},
Expand All @@ -231,39 +260,59 @@ async function updateReview(
if (body === review?.body) return
// if no existing review, body non blank, create a review
if (review === null && body !== '') {
await client.rest.pulls.createReview({
...pullRequest,
body,
event: 'COMMENT'
})
return
try {
await client.rest.pulls.createReview({
...pullRequest,
body,
event: 'COMMENT'
})
} catch (error) {
if (error instanceof Error) throw new Error(errors.creatingReview)
return
}
}
// if body blank and review exists, update it to show passed
if (review !== null && body === '') {
await client.rest.pulls.updateReview({
...pullRequest,
review_id: review.id,
body: 'PR Compliance Checks Passed!'
})
return
try {
await client.rest.pulls.updateReview({
...pullRequest,
review_id: review.id,
body: 'PR Compliance Checks Passed!'
})
} catch (error) {
if (error instanceof Error) throw new Error(errors.updatingReview)
return
}
}
// if body non-blank and review exists, update it
if (review !== null && body !== review?.body) {
await client.rest.pulls.updateReview({
...pullRequest,
review_id: review.id,
body
})
return
try {
await client.rest.pulls.updateReview({
...pullRequest,
review_id: review.id,
body
})
} catch (error) {
if (error instanceof Error) throw new Error(errors.updatingReview)
return
}
}
}
async function userIsTeamMember(login: string, owner: string) {
async function userIsTeamMember(
login: string,
owner: string
): Promise<boolean> {
if (login === owner) return true
const {data: userOrgs} = await client.request('GET /users/{user}/orgs', {
user: login
})
return userOrgs.some((userOrg: {login: string}) => {
return userOrg.login === owner
})
try {
const {data: userOrgs} = await client.request('GET /users/{user}/orgs', {
user: login
})
return userOrgs.some((userOrg: {login: string}) => {
return userOrg.login === owner
})
} catch (error) {
if (error instanceof Error) throw new Error(errors.checkingTeamMember)
return false
}
}
run()