2018-02-07 16:59:55 +00:00
|
|
|
// Description:
|
|
|
|
// Script that listens for PRs moving into the 'TO TEST' column
|
|
|
|
// and triggers a Jenkins build.
|
|
|
|
//
|
|
|
|
// Dependencies:
|
|
|
|
// github: "^13.1.0"
|
|
|
|
// jenkins: "^0.20.1"
|
2019-01-31 15:07:54 +00:00
|
|
|
// probot-config: "^1.0.0"
|
2018-02-07 16:59:55 +00:00
|
|
|
//
|
|
|
|
// Author:
|
|
|
|
// PombeirP
|
|
|
|
|
2018-02-08 12:09:59 +00:00
|
|
|
const getConfig = require('probot-config')
|
2018-02-07 16:59:55 +00:00
|
|
|
const jenkins = require('jenkins')({ baseUrl: process.env.JENKINS_URL, crumbIssuer: true, promisify: true })
|
|
|
|
const HashMap = require('hashmap')
|
|
|
|
|
2018-02-08 12:09:59 +00:00
|
|
|
const defaultConfig = require('../lib/config')
|
|
|
|
const gitHubHelpers = require('../lib/github-helpers')
|
|
|
|
|
2018-02-13 14:51:00 +00:00
|
|
|
const botName = 'trigger-automation-test-build'
|
2018-02-07 16:59:55 +00:00
|
|
|
const pendingPullRequests = new HashMap()
|
|
|
|
|
|
|
|
module.exports = (robot) => {
|
|
|
|
if (!process.env.JENKINS_URL) {
|
2018-02-13 14:51:00 +00:00
|
|
|
robot.log.info(`${botName} - Jenkins is not configured, not loading script`)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-02-08 12:09:59 +00:00
|
|
|
setInterval(checkPendingPullRequests, 5 * 1000 * 60, robot)
|
|
|
|
registerForRelevantCardEvents(robot)
|
2018-02-07 16:59:55 +00:00
|
|
|
}
|
|
|
|
|
2018-02-08 12:09:59 +00:00
|
|
|
function registerForRelevantCardEvents (robot) {
|
2018-02-09 21:50:23 +00:00
|
|
|
robot.on(['project_card.created', 'project_card.moved'], context => processChangedProjectCard(robot, context))
|
2018-02-07 16:59:55 +00:00
|
|
|
}
|
|
|
|
|
2018-02-08 12:09:59 +00:00
|
|
|
async function processChangedProjectCard (robot, context) {
|
2018-02-09 21:50:23 +00:00
|
|
|
const { github, payload } = context
|
|
|
|
const repo = payload.repository
|
|
|
|
if (!repo) {
|
2018-02-13 14:51:00 +00:00
|
|
|
robot.log.debug(`${botName} - Repository info is not present in payload, ignoring`)
|
2018-02-09 21:50:23 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-22 11:26:05 +00:00
|
|
|
const repoInfo = context.repo()
|
2018-02-08 12:09:59 +00:00
|
|
|
const config = await getConfig(context, 'github-bot.yml', defaultConfig(robot, '.github/github-bot.yml'))
|
|
|
|
const projectBoardConfig = config ? config['project-board'] : null
|
|
|
|
const automatedTestsConfig = config ? config['automated-tests'] : null
|
|
|
|
if (!projectBoardConfig || !automatedTestsConfig) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-02-07 16:59:55 +00:00
|
|
|
if (payload.project_card.note) {
|
2018-02-13 14:51:00 +00:00
|
|
|
robot.log.trace(`${botName} - Card is a note, ignoring`)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const projectBoardName = projectBoardConfig['name']
|
2019-02-25 12:27:13 +00:00
|
|
|
const kickoffColumnName = automatedTestsConfig['kickoff-column-name']
|
2018-02-07 16:59:55 +00:00
|
|
|
|
2018-02-08 12:09:59 +00:00
|
|
|
if (repo.full_name !== automatedTestsConfig['repo-full-name']) {
|
2018-02-13 14:51:00 +00:00
|
|
|
robot.log.trace(`${botName} - Pull request project doesn't match watched repo, exiting`, repo.full_name, automatedTestsConfig['repo-full-name'])
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-02-25 12:27:13 +00:00
|
|
|
let targetKickoffColumn
|
2018-02-07 16:59:55 +00:00
|
|
|
try {
|
2019-01-31 14:50:23 +00:00
|
|
|
const columnPayload = await github.projects.getColumn({ column_id: payload.project_card.column_id })
|
2018-02-07 16:59:55 +00:00
|
|
|
|
2019-02-25 12:27:13 +00:00
|
|
|
if (columnPayload.data.name !== kickoffColumnName) {
|
|
|
|
robot.log.trace(`${botName} - Card column name doesn't match watched column name, exiting`, columnPayload.data.name, kickoffColumnName)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-02-25 12:27:13 +00:00
|
|
|
targetKickoffColumn = columnPayload.data
|
2018-02-07 16:59:55 +00:00
|
|
|
} catch (error) {
|
2018-02-13 14:51:00 +00:00
|
|
|
robot.log.warn(`${botName} - Error while fetching project column`, payload.project_card.column_id, error)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const last = (a, index) => {
|
|
|
|
return a[a.length + index]
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2019-02-25 12:27:13 +00:00
|
|
|
const projectId = last(targetKickoffColumn.project_url.split('/'), -1)
|
2019-01-31 14:50:23 +00:00
|
|
|
const projectPayload = await github.projects.get({ project_id: projectId })
|
2018-02-16 20:35:49 +00:00
|
|
|
const project = projectPayload.data
|
2018-02-07 16:59:55 +00:00
|
|
|
if (project.name !== projectBoardName) {
|
2018-03-14 13:24:11 +00:00
|
|
|
robot.log.trace(`${botName} - Project board name doesn't match watched project board, exiting`, project.name, projectBoardName)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
} catch (error) {
|
2018-02-13 14:51:00 +00:00
|
|
|
robot.log.warn(`${botName} - Error while fetching project column`, payload.project_card.column_id, error)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const prNumber = last(payload.project_card.content_url.split('/'), -1)
|
2018-02-08 12:09:59 +00:00
|
|
|
const fullJobName = automatedTestsConfig['job-full-name']
|
2018-02-07 16:59:55 +00:00
|
|
|
|
2019-01-10 12:57:39 +00:00
|
|
|
await processPullRequest(context, robot, { ...repoInfo, number: prNumber }, fullJobName)
|
2018-02-07 16:59:55 +00:00
|
|
|
}
|
|
|
|
|
2019-01-10 12:57:39 +00:00
|
|
|
async function processPullRequest (context, robot, prInfo, fullJobName) {
|
|
|
|
const { github } = context
|
|
|
|
|
2018-02-07 16:59:55 +00:00
|
|
|
// Remove the PR from the pending PR list, if it is there
|
2018-02-16 20:35:49 +00:00
|
|
|
pendingPullRequests.delete(prInfo.number)
|
2018-02-07 16:59:55 +00:00
|
|
|
|
|
|
|
try {
|
2019-01-10 12:57:39 +00:00
|
|
|
// Get detailed pull request
|
|
|
|
const pullRequestPayload = await github.pullRequests.get(prInfo)
|
|
|
|
const pullRequest = pullRequestPayload.data
|
|
|
|
if (!pullRequest) {
|
|
|
|
robot.log.warn(`${botName} - Could not find PR`, prInfo)
|
|
|
|
return
|
|
|
|
}
|
2019-02-25 20:54:43 +00:00
|
|
|
if (pullRequest.state === 'closed') {
|
|
|
|
robot.log.info(`${botName} - PR is closed, discarded`, prInfo)
|
|
|
|
return
|
|
|
|
}
|
2019-01-10 12:57:39 +00:00
|
|
|
|
|
|
|
const statusContext = 'jenkins/prs/android-e2e'
|
|
|
|
const currentStatus = await gitHubHelpers.getPullRequestCurrentStatusForContext(context, statusContext, pullRequest)
|
2018-02-07 16:59:55 +00:00
|
|
|
|
2019-01-10 12:57:39 +00:00
|
|
|
switch (currentStatus) {
|
2019-02-25 20:54:43 +00:00
|
|
|
case undefined:
|
2019-01-10 12:57:39 +00:00
|
|
|
case 'pending':
|
2019-02-25 20:54:43 +00:00
|
|
|
case 'failure':
|
2018-02-16 20:35:49 +00:00
|
|
|
pendingPullRequests.set(prInfo.number, { github: github, prInfo, fullJobName: fullJobName })
|
2019-01-10 12:57:39 +00:00
|
|
|
robot.log.debug(`${botName} - Status for ${statusContext} is '${currentStatus}', adding to backlog to check periodically`, prInfo)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
2019-01-10 12:57:39 +00:00
|
|
|
case 'error':
|
|
|
|
robot.log.debug(`${botName} - Status for ${statusContext} is '${currentStatus}', exiting`, prInfo)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
2019-01-10 12:57:39 +00:00
|
|
|
case 'success':
|
|
|
|
robot.log.debug(`${botName} - Status for ${statusContext} is '${currentStatus}', proceeding`, prInfo)
|
2018-02-07 16:59:55 +00:00
|
|
|
break
|
|
|
|
default:
|
2019-01-10 12:57:39 +00:00
|
|
|
robot.log.warn(`${botName} - Status for ${statusContext} is '${currentStatus}', ignoring`, prInfo)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2018-02-16 20:35:49 +00:00
|
|
|
robot.log.error(`Couldn't calculate the PR approval state: ${err}`, prInfo)
|
2018-02-07 16:59:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2018-02-16 20:35:49 +00:00
|
|
|
const args = { parameters: { pr_id: prInfo.number, apk: `--apk=${prInfo.number}.apk` } }
|
2018-02-07 16:59:55 +00:00
|
|
|
|
|
|
|
if (process.env.DRY_RUN) {
|
2018-02-16 20:35:49 +00:00
|
|
|
robot.log(`${botName} - Would start ${fullJobName} job in Jenkins`, prInfo, args)
|
2018-02-07 16:59:55 +00:00
|
|
|
} else {
|
2018-02-16 20:35:49 +00:00
|
|
|
robot.log(`${botName} - Starting ${fullJobName} job in Jenkins`, prInfo, args)
|
2018-02-07 16:59:55 +00:00
|
|
|
const buildId = await jenkins.job.build(fullJobName, args)
|
2018-02-16 20:35:49 +00:00
|
|
|
robot.log(`${botName} - Started job in Jenkins`, prInfo, buildId)
|
2018-02-07 16:59:55 +00:00
|
|
|
}
|
|
|
|
} catch (error) {
|
2018-02-16 20:35:49 +00:00
|
|
|
robot.log.error(`${botName} - Error while triggering Jenkins build. Will retry later`, prInfo, error)
|
2018-02-07 16:59:55 +00:00
|
|
|
|
2018-02-16 20:35:49 +00:00
|
|
|
pendingPullRequests.set(prInfo.number, { github: github, prInfo: prInfo, fullJobName: fullJobName })
|
2018-02-07 16:59:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function checkPendingPullRequests (robot) {
|
|
|
|
const _pendingPullRequests = pendingPullRequests.clone()
|
|
|
|
|
2018-02-13 14:51:00 +00:00
|
|
|
robot.log.trace(`${botName} - Processing ${_pendingPullRequests.size} pending PRs`)
|
2018-02-07 16:59:55 +00:00
|
|
|
|
2018-02-16 20:35:49 +00:00
|
|
|
for (const { github, prInfo, fullJobName } of _pendingPullRequests.values()) {
|
|
|
|
await processPullRequest(github, robot, prInfo, fullJobName)
|
2018-02-07 16:59:55 +00:00
|
|
|
}
|
|
|
|
|
2018-02-13 14:51:00 +00:00
|
|
|
robot.log.trace(`${botName} - Finished processing ${_pendingPullRequests.size} pending PRs`)
|
2018-02-07 16:59:55 +00:00
|
|
|
}
|