diff --git a/lib/push-jenkins-update.js b/lib/push-jenkins-update.js new file mode 100644 index 00000000..a29205b6 --- /dev/null +++ b/lib/push-jenkins-update.js @@ -0,0 +1,46 @@ +'use strict' + +const githubClient = require('./github-client') + +function pushJenkinsUpdate (options, build) { + const statusOpts = extendWithBuildData(options, build) + const createGhStatus = createGhStatusFn(statusOpts) + + createGhStatus(build.status, build.message) +} + +function createGhStatusFn (options) { + const prInfo = prInfoStr(options) + + return (state, message) => { + githubClient.statuses.create({ + user: options.owner, + repo: options.repoName, + sha: options.sha, + target_url: options.url, + context: options.context, + state: state, + description: message + }, (err, res) => { + if (err) { + return console.error(`! ${prInfo} Error while updating Jenkins / GitHub PR status`, err) + } + console.log(`* ${prInfo} Jenkins / Github PR status updated to '${state}'`) + }) + } +} + +function extendWithBuildData (options, build) { + return Object.assign({ + sha: build.commit, + url: build.url, + context: build.identifier + }, options) +} + +function prInfoStr (options) { + const shortSha = options.sha.substr(0, 7) + return `${options.owner}/${options.repoName}/${shortSha}` +} + +module.exports = pushJenkinsUpdate diff --git a/package.json b/package.json index b6ed255c..c1e6cc35 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,11 @@ }, "devDependencies": { "eventsource": "^0.2.1", + "nock": "^8.0.0", "nodemon": "^1.9.1", "request": "^2.72.0", "standard": "^6.0.7", + "supertest": "^1.2.0", "tap": "^5.7.1" } } diff --git a/scripts/node-jenkins-status.js b/scripts/node-jenkins-status.js new file mode 100644 index 00000000..c5be6949 --- /dev/null +++ b/scripts/node-jenkins-status.js @@ -0,0 +1,14 @@ +'use strict' + +const pushJenkinsUpdate = require('../lib/push-jenkins-update') + +module.exports = function (app) { + app.post('/node/jenkins', (req, res) => { + pushJenkinsUpdate({ + owner: 'nodejs', + repoName: 'node' + }, req.body) + + res.status(201).end() + }) +} diff --git a/test/_fixtures/error-payload.json b/test/_fixtures/error-payload.json new file mode 100644 index 00000000..e280f4be --- /dev/null +++ b/test/_fixtures/error-payload.json @@ -0,0 +1,7 @@ +{ + "identifier": "test/arm", + "status": "failure", + "message": "tests failed", + "commit": "8a5fec2a6bade91e544a30314d7cf21f8a200de1", + "url": "https://ci.nodejs.org/job/node-test-commit-arm/3087/" +} diff --git a/test/_fixtures/pending-payload.json b/test/_fixtures/pending-payload.json new file mode 100644 index 00000000..2f9600f7 --- /dev/null +++ b/test/_fixtures/pending-payload.json @@ -0,0 +1,7 @@ +{ + "identifier": "test/linux", + "status": "pending", + "message": "checking for errors", + "commit": "8a5fec2a6bade91e544a30314d7cf21f8a200de1", + "url": "https://ci.nodejs.org/job/node-test-commit-linux/3176/" +} diff --git a/test/_fixtures/success-payload.json b/test/_fixtures/success-payload.json new file mode 100644 index 00000000..d4a2d355 --- /dev/null +++ b/test/_fixtures/success-payload.json @@ -0,0 +1,7 @@ +{ + "identifier": "test/osx", + "status": "success", + "message": "tests passed", + "commit": "8a5fec2a6bade91e544a30314d7cf21f8a200de1", + "url": "https://ci.nodejs.org/job/node-test-commit-osx/3157/" +} diff --git a/test/push-jenkins-update.test.js b/test/push-jenkins-update.test.js new file mode 100644 index 00000000..b9639268 --- /dev/null +++ b/test/push-jenkins-update.test.js @@ -0,0 +1,62 @@ +'use strict' + +const tap = require('tap') +const fs = require('fs') +const path = require('path') +const url = require('url') +const nock = require('nock') +const supertest = require('supertest') + +const app = require('../app') + +tap.test('Sends POST requests to https://api.github.com/repos/nodejs/node/statuses/', (t) => { + const fixture = readFixture('success-payload.json') + const scope = nock('https://api.github.com') + .filteringPath(ignoreQueryParams) + .post('/repos/nodejs/node/statuses/8a5fec2a6bade91e544a30314d7cf21f8a200de1') + .reply(201) + + t.plan(1) + t.tearDown(() => scope.done()) + + supertest(app) + .post('/node/jenkins') + .send(fixture) + .expect(201) + .end((err, res) => { + t.equal(err, null) + }) +}) + +tap.test('Forwards payload provided in incoming POST to GitHub status API', (t) => { + const fixture = readFixture('success-payload.json') + const scope = nock('https://api.github.com') + .filteringPath(ignoreQueryParams) + .post('/repos/nodejs/node/statuses/8a5fec2a6bade91e544a30314d7cf21f8a200de1', { + state: 'success', + context: 'test/osx', + description: 'tests passed', + target_url: 'https://ci.nodejs.org/job/node-test-commit-osx/3157/' + }) + .reply(201) + + t.plan(1) + t.tearDown(() => scope.done()) + + supertest(app) + .post('/node/jenkins') + .send(fixture) + .expect(201) + .end((err, res) => { + t.equal(err, null) + }) +}) + +function ignoreQueryParams (pathAndQuery) { + return url.parse(pathAndQuery, true).pathname +} + +function readFixture (fixtureName) { + const content = fs.readFileSync(path.join(__dirname, '_fixtures', fixtureName)).toString() + return JSON.parse(content) +}