blob: 0d5469cfb06aea486e65a6ca3dc159f12ea28573 [file] [log] [blame]
import * as github from '@actions/github'
import * as core from '@actions/core'
import * as rest from '@octokit/rest'
import {Context} from '@actions/github/lib/context'
function getRequiredEnv(key: string): string {
const value = process.env[key]
if (value === undefined) {
const message = `${key} was not defined.`
throw new Error(message)
}
return value
}
async function getPullRequest(
octokit: github.GitHub,
context: Context,
owner: string,
repo: string
): Promise<rest.PullsGetResponse> {
const pullRequestNumber = context.payload.pull_request
? context.payload.pull_request.number
: null
if (pullRequestNumber === null) {
throw Error(`Could not find PR number in context payload.`)
}
core.info(`pullRequestNumber: ${pullRequestNumber}`)
const pullRequest = await octokit.pulls.get({
owner,
repo,
// eslint-disable-next-line @typescript-eslint/camelcase
pull_number: pullRequestNumber
})
return pullRequest.data
}
function getPullRequestLabels(pullRequest: rest.PullsGetResponse): string[] {
const labelNames = pullRequest
? pullRequest.labels.map(label => label.name)
: []
return labelNames
}
async function getReviews(
octokit: github.GitHub,
owner: string,
repo: string,
number: number,
getComitters: boolean
): Promise<[rest.PullsListReviewsResponseItem[], string[], string[]]> {
const reviews = await octokit.pulls.listReviews({
owner,
repo,
// eslint-disable-next-line @typescript-eslint/camelcase
pull_number: number
})
const reviewers = reviews ? reviews.data.map(review => review.user.login) : []
const committers: string[] = []
if (getComitters) {
for (const reviewer of reviewers) {
if (!committers.includes(reviewer)) {
const p = await octokit.repos.getCollaboratorPermissionLevel({
owner,
repo,
username: reviewer
})
const permission = p.data.permission
core.info(`\nChecking: "${reviewer}" permissions: ${permission}.\n`)
if (permission === 'admin' || permission === 'write') {
committers.push(reviewer)
}
}
}
}
return [reviews.data, reviewers, committers]
}
function processReviews(
reviews: rest.PullsListReviewsResponseItem[],
reviewers: string[],
committers: string[],
requireCommittersApproval: boolean
): boolean {
let isApproved = false
const reviewStates: {[user: string]: string} = {}
for (const review of reviews) {
if (review.state === 'APPROVED' || review.state === 'CHANGES_REQUESTED') {
if (requireCommittersApproval && committers.includes(review.user.login)) {
reviewStates[review.user.login] = review.state
} else if (!requireCommittersApproval) {
reviewStates[review.user.login] = review.state
}
}
}
core.info(`User reviews:`)
for (const user in reviewStates) {
core.info(`User "${user}" : "${reviewStates[user]}"`)
}
for (const user in reviewStates) {
if (reviewStates[user] === 'APPROVED') {
isApproved = true
break
}
}
for (const user in reviewStates) {
if (reviewStates[user] === 'REQUEST_CHANGES') {
isApproved = false
break
}
}
return isApproved
}
async function setLabel(
octokit: github.GitHub,
owner: string,
repo: string,
pullRequestNumber: number,
label: string
): Promise<void> {
await octokit.issues.addLabels({
// eslint-disable-next-line @typescript-eslint/camelcase
issue_number: pullRequestNumber,
labels: [label],
owner,
repo
})
}
async function removeLabel(
octokit: github.GitHub,
owner: string,
repo: string,
pullRequestNumber: number,
label: string
): Promise<void> {
await octokit.issues.removeLabel({
// eslint-disable-next-line @typescript-eslint/camelcase
issue_number: pullRequestNumber,
name: label,
owner,
repo
})
}
async function printDebug(
item: object | string | boolean,
description: string
): Promise<void> {
const itemJson = JSON.stringify(item)
core.info(`\n ######### ${description} ######### \n: ${itemJson}\n\n`)
}
async function run(): Promise<void> {
const token = core.getInput('token', {required: true})
const userLabel = core.getInput('label', {required: true})
const requireCommittersApproval = core.getInput(
'require_committers_approval',
{
required: true
}
)
const octokit = new github.GitHub(token)
const context = github.context
const repository = getRequiredEnv('GITHUB_REPOSITORY')
const eventName = getRequiredEnv('GITHUB_EVENT_NAME')
const [owner, repo] = repository.split('/')
if (eventName !== 'pull_request_review') {
throw Error(
`This action is only useful in "pull_request_review" triggered runs and you used it in "${eventName}"`
)
}
// PULL REQUEST
const pullRequest = await getPullRequest(octokit, context, owner, repo)
// LABELS
const labelNames = getPullRequestLabels(pullRequest)
// REVIEWS
const [reviews, reviewers, committers] = await getReviews(
octokit,
owner,
repo,
pullRequest.number,
requireCommittersApproval === 'true'
)
const isApproved = processReviews(
reviews,
reviewers,
committers,
requireCommittersApproval === 'true'
)
// HANDLE LABEL
if (isApproved && !labelNames.includes(userLabel)) {
setLabel(octokit, owner, repo, pullRequest.number, userLabel)
} else if (!isApproved && labelNames.includes(userLabel)) {
removeLabel(octokit, owner, repo, pullRequest.number, userLabel)
}
}
run()
.then(() =>
core.info(
'\n############### Set Label When Approved complete ##################\n'
)
)
.catch(e => core.setFailed(e.message))