Skip to content

Add support for polling Travis CI builds by SHA #12

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

Merged
merged 1 commit into from
Apr 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 98 additions & 4 deletions lib/pollTravis.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,33 @@ githubClient.authenticate({
token: process.env.GITHUB_TOKEN
})

function pollAndComment (owner, repoName, prId, checkNumber) {
function pollThenComment (owner, repoName, prId) {
const prInfo = prInfoStr({ owner, repoName, prId })

// we have to figure out what type of Travis polling we should perform,
// either by PR #id (as for nodejs.org) or commit sha (for readable-stream)
travisClient.repos(owner, repoName).builds.get((err, res) => {
if (err) {
return console.error(`! ${prInfo} Error while retrieving initial builds`, err.stack)
}

const hasAnyPrBuilds = res.builds.some((build) => build.pull_request)

if (hasAnyPrBuilds) {
pollByPrThenComment(owner, repoName, prId)
} else {
pollByCommitThenComment(owner, repoName, prId)
}
})
}

/**
* When Travis CI picks up our PR's, we can poll and match builds
* by their related PR #id.
*
* That's the case for nodejs.org.
*/
function pollByPrThenComment (owner, repoName, prId, checkNumber) {
checkNumber = checkNumber || 1

const prInfo = prInfoStr({ owner, repoName, prId })
Expand Down Expand Up @@ -56,7 +82,75 @@ function pollAndComment (owner, repoName, prId, checkNumber) {
console.warn(`! ${prInfo} Was not able to find matching build, will do check #${checkNumber + 1} in 30 seconds`)
}

setTimeout(pollAndComment, 30 * 1000, owner, repoName, prId, checkNumber + 1)
setTimeout(pollByPrThenComment, 30 * 1000, owner, repoName, prId, checkNumber + 1)
})
}

/**
* When Travis CI *doesn't* pick up our PR's, we have to poll and match builds
* by the last commit SHA of the related PR.
*
* This is the case for readable-stream.
*/
function pollByCommitThenComment (owner, repoName, prId) {
const prInfo = prInfoStr({ owner, repoName, prId })

githubClient.pullRequests.getCommits({
user: owner,
repo: repoName,
number: prId
}, (err, commitMetas) => {
if (err) {
return console.error(`! ${prInfo} Got error when retrieving GitHub commits for PR`, err.stack)
}

const lastSha = commitMetas.pop().sha
pollTravisBuildBySha({ owner, repoName, prId, lastSha })
console.log(`* ${prInfo} Started polling Travis for build by commit ${lastSha.substr(0, 7)}`)
})
}

function pollTravisBuildBySha (options, checkNumber) {
const createGhComment = createGhCommentFn(options)
const prInfo = prInfoStr(options)
const shaToMatch = options.lastSha

checkNumber = checkNumber || 1

if (checkNumber > 100) {
console.warn(`* ${prInfo} Was not able to find matching build for PR, stopping poll now :(`)
return
}

travisClient.repos(options.owner, options.repoName).builds.get((err, res) => {
if (err) {
return console.error(`! ${prInfo} Got error when retrieving Travis builds`, err.stack)
}

const matchingCommit = res.commits.find((commit) => commit.sha === shaToMatch)
if (!matchingCommit) {
console.warn(`! ${prInfo} Travis hasn't picked up last commit yet, will do check #${checkNumber + 1} in 30 seconds`)
return setTimeout(pollTravisBuildBySha, 30 * 1000, options, checkNumber + 1)
}

const lastBuildForCommit = res.builds.find((build) => build.commit_id === matchingCommit.id)
if (lastBuildForCommit) {
const lastState = lastBuildForCommit.state

if (lastState === 'passed') {
return createGhComment(`[Travis build passed](https://travis-ci.org/${options.owner}/${options.repoName}/builds/${lastBuildForCommit.id}) :+1:`)
} else if (lastState === 'failed') {
return createGhComment(`[Travis build failed](https://travis-ci.org/${options.owner}/${options.repoName}/builds/${lastBuildForCommit.id}) :-1:`)
} else if (~['created', 'started'].indexOf(lastState)) {
console.log(`* ${prInfo} "${lastState}" build found, will do check #${checkNumber + 1} in 30 seconds`)
} else {
return console.log(`* ${prInfo} Unknown build state: "${lastState}", stopping polling`)
}
} else {
console.warn(`! ${prInfo} Was not able to find matching build by last commit, will do check #${checkNumber + 1} in 30 seconds`)
}

setTimeout(pollTravisBuildBySha, 30 * 1000, options, checkNumber + 1)
})
}

Expand All @@ -71,7 +165,7 @@ function createGhCommentFn (options) {
body: message
}, (err, res) => {
if (err) {
return console.error(`! ${prInfo} Error from GitHub`, err.stack)
return console.error(`! ${prInfo} Error while creating GitHub comment`, err.stack)
}
console.log(`* ${prInfo} Github comment created`)
})
Expand All @@ -82,4 +176,4 @@ function prInfoStr (options) {
return `${options.owner}/${options.repoName}/#${options.prId}`
}

exports.pollAndComment = pollAndComment
exports.pollThenComment = pollThenComment
4 changes: 2 additions & 2 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ app.all('/hooks/github', (req, res) => {
const repo = req.body.repository

console.log(`* ${repo.owner.login}/${repo.name}/#${req.body.number} Opened, starting build checks!`)
pollTravis.pollAndComment(repo.owner.login, repo.name, parseInt(req.body.number))
pollTravis.pollThenComment(repo.owner.login, repo.name, parseInt(req.body.number))
}

res.end()
})

// to trigger polling manually
app.get('/pr/:owner/:repo/:prId', (req, res) => {
pollTravis.pollAndComment(req.params.owner, req.params.repo, parseInt(req.params.prId))
pollTravis.pollThenComment(req.params.owner, req.params.repo, parseInt(req.params.prId))
res.end()
})

Expand Down