status-github-bot/lib/github-helpers.js

166 lines
5.0 KiB
JavaScript

// Description:
// GtHub-related helpers
//
// Dependencies:
// github: "^13.1.0"
//
// Author:
// PombeirP
module.exports = {
getPullRequestReviewStates: _getPullRequestReviewStates,
getReviewApprovalState: _getReviewApprovalState,
getProjectCardForIssue: _getProjectCardForIssue,
getOrgProjectByName: _getOrgProjectByName,
getRepoProjectByName: _getRepoProjectByName,
getProjectColumnByName: _getProjectColumnByName
}
async function _getPullRequestReviewStates (github, prInfo) {
let finalReviewsMap = new Map()
const ghreviews = await github.paginate(
github.pullRequests.getReviews({ ...prInfo, per_page: 100 }),
res => res.data)
for (const review of ghreviews) {
switch (review.state) {
case 'APPROVED':
case 'CHANGES_REQUESTED':
case 'PENDING':
finalReviewsMap.set(review.user.id, review.state)
break
}
}
return Array.from(finalReviewsMap.values())
}
async function _getReviewApprovalState (github, robot, prInfo, testedPullRequestLabelName) {
// Get detailed pull request
const pullRequestPayload = await github.pullRequests.get(prInfo)
const pullRequest = pullRequestPayload.data
if (pullRequest.mergeable !== null && pullRequest.mergeable !== undefined && !pullRequest.mergeable) {
robot.log.debug(`pullRequest.mergeable is ${pullRequest.mergeable}, considering as failed`)
return 'failed'
}
let state
switch (pullRequest.mergeable_state) {
case 'clean':
if (testedPullRequestLabelName !== null && pullRequest.labels.find(label => label.name === testedPullRequestLabelName)) {
robot.log.debug(`Pull request is labeled '${testedPullRequestLabelName}', ignoring`)
return null
}
state = 'approved'
break
case 'dirty':
state = 'failed'
break
}
robot.log.debug(`pullRequest.mergeable_state is ${pullRequest.mergeable_state}, considering state as ${state}`)
if (state !== 'approved') {
return state
}
const threshold = 2 // Minimum number of approvers
const finalReviews = await _getPullRequestReviewStates(github, prInfo)
robot.log.debug(finalReviews)
const approvedReviews = finalReviews.filter(reviewState => reviewState === 'APPROVED')
if (approvedReviews.length >= threshold) {
const reviewsWithChangesRequested = finalReviews.filter(reviewState => reviewState === 'CHANGES_REQUESTED')
if (reviewsWithChangesRequested.length === 0) {
return 'approved'
}
return 'changes_requested'
}
return 'awaiting_reviewers'
}
async function _getProjectCardForIssue (github, columnId, issueUrl) {
const ghcardsPayload = await github.projects.getProjectCards({column_id: columnId})
const ghcard = ghcardsPayload.data.find(c => c.content_url === issueUrl)
return ghcard
}
async function _getOrgProjectByName (github, robot, orgName, projectName, botName) {
if (!projectName) {
return null
}
try {
// Fetch org projects
// TODO: The org project and project column info should be cached
// in order to improve performance and reduce roundtrips
const ghprojectsPayload = await github.projects.getOrgProjects({
org: orgName,
state: 'open'
})
const project = ghprojectsPayload.data.find(p => p.name === projectName)
if (!project) {
robot.log.error(`${botName} - Couldn't find project ${projectName} in ${orgName} org`)
return null
}
robot.log.debug(`${botName} - Fetched ${project.name} project (${project.id})`)
return project
} catch (err) {
robot.log.error(`${botName} - Couldn't fetch the github projects for org`, orgName, err)
return null
}
}
async function _getRepoProjectByName (github, robot, repoInfo, projectName, botName) {
if (!projectName) {
return null
}
try {
const ghprojectsPayload = await github.projects.getRepoProjects({ ...repoInfo, state: 'open' })
const project = ghprojectsPayload.data.find(p => p.name === projectName)
if (!project) {
robot.log.error(`${botName} - Couldn't find project ${projectName} in repo ${repoInfo.owner}/${repoInfo.repo}`)
return null
}
robot.log.debug(`${botName} - Fetched ${project.name} project (${project.id})`)
return project
} catch (err) {
robot.log.error(`${botName} - Couldn't fetch the github projects for repo: ${err}`, repoInfo)
return null
}
}
async function _getProjectColumnByName (github, robot, project, columnName, botName) {
if (!project) {
return null
}
if (!columnName) {
return null
}
try {
const ghcolumnsPayload = await github.projects.getProjectColumns({ project_id: project.id })
const column = ghcolumnsPayload.data.find(c => c.name === columnName)
if (!column) {
robot.log.error(`${botName} - Couldn't find ${columnName} column in project ${project.name}`)
return null
}
robot.log.debug(`${botName} - Fetched ${column.name} column (${column.id})`)
return column
} catch (err) {
robot.log.error(`${botName} - Couldn't fetch the github columns for project: ${err}`, project.id)
return null
}
}