Skip to content

Commit cf21a4f

Browse files
committed
Add support for polling Travis CI builds by SHA.
Compared to the nodejs.org repo, Travis doesn't pick up newly created PR's from nodejs/readable-stream. Strangely enough it does pick up commits and builds them though. Therefore these changes adds support for polling Travis builds by commit SHAs rather than PR #id alone. The bot tries to do a smart choice about which polling to use by checking if Travis has had *any* builds triggered by PR's lately, if not if starts to poll by SHA.
1 parent 0088b5e commit cf21a4f

File tree

2 files changed

+100
-6
lines changed

2 files changed

+100
-6
lines changed

lib/pollTravis.js

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,33 @@ githubClient.authenticate({
2222
token: process.env.GITHUB_TOKEN
2323
})
2424

25-
function pollAndComment (owner, repoName, prId, checkNumber) {
25+
function pollThenComment (owner, repoName, prId) {
26+
const prInfo = prInfoStr({ owner, repoName, prId })
27+
28+
// we have to figure out what type of Travis polling we should perform,
29+
// either by PR #id (as for nodejs.org) or commit sha (for readable-stream)
30+
travisClient.repos(owner, repoName).builds.get((err, res) => {
31+
if (err) {
32+
return console.error(`! ${prInfo} Error while retrieving initial builds`, err.stack)
33+
}
34+
35+
const hasAnyPrBuilds = res.builds.some((build) => build.pull_request)
36+
37+
if (hasAnyPrBuilds) {
38+
pollByPrThenComment(owner, repoName, prId)
39+
} else {
40+
pollByCommitThenComment(owner, repoName, prId)
41+
}
42+
})
43+
}
44+
45+
/**
46+
* When Travis CI picks up our PR's, we can poll and match builds
47+
* by their related PR #id.
48+
*
49+
* That's the case for nodejs.org.
50+
*/
51+
function pollByPrThenComment (owner, repoName, prId, checkNumber) {
2652
checkNumber = checkNumber || 1
2753

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

59-
setTimeout(pollAndComment, 30 * 1000, owner, repoName, prId, checkNumber + 1)
85+
setTimeout(pollByPrThenComment, 30 * 1000, owner, repoName, prId, checkNumber + 1)
86+
})
87+
}
88+
89+
/**
90+
* When Travis CI *doesn't* pick up our PR's, we have to poll and match builds
91+
* by the last commit SHA of the related PR.
92+
*
93+
* This is the case for readable-stream.
94+
*/
95+
function pollByCommitThenComment (owner, repoName, prId) {
96+
const prInfo = prInfoStr({ owner, repoName, prId })
97+
98+
githubClient.pullRequests.getCommits({
99+
user: owner,
100+
repo: repoName,
101+
number: prId
102+
}, (err, commitMetas) => {
103+
if (err) {
104+
return console.error(`! ${prInfo} Got error when retrieving GitHub commits for PR`, err.stack)
105+
}
106+
107+
const lastSha = commitMetas.pop().sha
108+
pollTravisBuildBySha({ owner, repoName, prId, lastSha })
109+
console.log(`* ${prInfo} Started polling Travis for build by commit ${lastSha.substr(0, 7)}`)
110+
})
111+
}
112+
113+
function pollTravisBuildBySha (options, checkNumber) {
114+
const createGhComment = createGhCommentFn(options)
115+
const prInfo = prInfoStr(options)
116+
const shaToMatch = options.lastSha
117+
118+
checkNumber = checkNumber || 1
119+
120+
if (checkNumber > 100) {
121+
console.warn(`* ${prInfo} Was not able to find matching build for PR, stopping poll now :(`)
122+
return
123+
}
124+
125+
travisClient.repos(options.owner, options.repoName).builds.get((err, res) => {
126+
if (err) {
127+
return console.error(`! ${prInfo} Got error when retrieving Travis builds`, err.stack)
128+
}
129+
130+
const matchingCommit = res.commits.find((commit) => commit.sha === shaToMatch)
131+
if (!matchingCommit) {
132+
console.warn(`! ${prInfo} Travis hasn't picked up last commit yet, will do check #${checkNumber + 1} in 30 seconds`)
133+
return setTimeout(pollTravisBuildBySha, 30 * 1000, options, checkNumber + 1)
134+
}
135+
136+
const lastBuildForCommit = res.builds.find((build) => build.commit_id === matchingCommit.id)
137+
if (lastBuildForCommit) {
138+
const lastState = lastBuildForCommit.state
139+
140+
if (lastState === 'passed') {
141+
return createGhComment(`[Travis build passed](https://travis-ci.org/${options.owner}/${options.repoName}/builds/${lastBuildForCommit.id}) :+1:`)
142+
} else if (lastState === 'failed') {
143+
return createGhComment(`[Travis build failed](https://travis-ci.org/${options.owner}/${options.repoName}/builds/${lastBuildForCommit.id}) :-1:`)
144+
} else if (~['created', 'started'].indexOf(lastState)) {
145+
console.log(`* ${prInfo} "${lastState}" build found, will do check #${checkNumber + 1} in 30 seconds`)
146+
} else {
147+
return console.log(`* ${prInfo} Unknown build state: "${lastState}", stopping polling`)
148+
}
149+
} else {
150+
console.warn(`! ${prInfo} Was not able to find matching build by last commit, will do check #${checkNumber + 1} in 30 seconds`)
151+
}
152+
153+
setTimeout(pollTravisBuildBySha, 30 * 1000, options, checkNumber + 1)
60154
})
61155
}
62156

@@ -71,7 +165,7 @@ function createGhCommentFn (options) {
71165
body: message
72166
}, (err, res) => {
73167
if (err) {
74-
return console.error(`! ${prInfo} Error from GitHub`, err.stack)
168+
return console.error(`! ${prInfo} Error while creating GitHub comment`, err.stack)
75169
}
76170
console.log(`* ${prInfo} Github comment created`)
77171
})
@@ -82,4 +176,4 @@ function prInfoStr (options) {
82176
return `${options.owner}/${options.repoName}/#${options.prId}`
83177
}
84178

85-
exports.pollAndComment = pollAndComment
179+
exports.pollThenComment = pollThenComment

server.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ app.all('/hooks/github', (req, res) => {
1616
const repo = req.body.repository
1717

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

2222
res.end()
2323
})
2424

2525
// to trigger polling manually
2626
app.get('/pr/:owner/:repo/:prId', (req, res) => {
27-
pollTravis.pollAndComment(req.params.owner, req.params.repo, parseInt(req.params.prId))
27+
pollTravis.pollThenComment(req.params.owner, req.params.repo, parseInt(req.params.prId))
2828
res.end()
2929
})
3030

0 commit comments

Comments
 (0)