Skip to content
Open
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
100 changes: 56 additions & 44 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
module.exports = function () {
var express = require('express');
var expressValidator = require('express-validator');
var bodyParser = require('body-parser');
var Request = require('request');
var basicAuth = require('basic-auth-connect');
var app = express();

app.use('/media', express.static(__dirname + '/media'));
app.use(bodyParser.json());
app.use(expressValidator());
app.set('view engine', 'ejs');
app.enable('trust proxy');

app.get('/', function (req, res) {
Request({
url: 'https://' + process.env.BITBUCKET_USERNAME + ':' + process.env.BITBUCKET_PASSWORD + '@api.bitbucket.org/2.0/users/chesleybrown',
Expand All @@ -24,54 +26,64 @@ module.exports = function () {
});
});
});

app.post('/pull-request/:codeshipProjectUuid/:codeshipProjectId', basicAuth(function (username, password) {
return (username === process.env.BITBUCKET_USERNAME && password === process.env.BITBUCKET_PASSWORD);
}), function (req, res) {
if (Object.keys(req.body).length === 0) {
res.status(400).end();
return;
}

// verify we have the information we need
if (!req.body.pullrequest_created) {
res.status(400).end();
return;
}
var pullRequest = req.body.pullrequest_created;

if (!pullRequest.id || typeof(pullRequest.description) !== 'string' || !(pullRequest.source && pullRequest.source.branch && pullRequest.source.branch.name) || !(pullRequest.source && pullRequest.source.repository && pullRequest.source.repository.full_name)) {
res.status(400).end();
return;
}

var errors, pullRequest;

// verify params
req.checkParams('codeshipProjectUuid', 'Invalid codeship project UUID').notEmpty().isUUID();
req.checkParams('codeshipProjectId', 'Invalid codeship project ID').notEmpty().isInt();

// verify body
req.checkBody('pullrequest.id', 'Unexpected BitBucket API response: pullrequest.id property is required').notEmpty().isInt();
req.checkBody('pullrequest.source.branch.name', 'Unexpected BitBucket API response: pullrequest.source.branch.name property is required').notEmpty();
req.checkBody('pullrequest.source.repository.full_name', 'Unexpected BitBucket API response: pullrequest.source.repository.full_name is required').notEmpty();

errors = req.validationErrors();
if (errors) {
console.error('Invalid codeship payload:', errors);
res.status(400).send(errors);
return;
}

pullRequest = req.body.pullrequest;

// if it doesn't already have Codeship status at the start of the description, let's add it
if (pullRequest.description.indexOf('[ ![Codeship Status') !== 0) {
var widget = '[ ![Codeship Status for ' + pullRequest.source.repository.full_name + '](https://codeship.io/projects/' + req.param('codeshipProjectUuid') +'/status?branch=' + pullRequest.source.branch.name + ')](https://codeship.io/projects/' + req.param('codeshipProjectId') + ')';
pullRequest.description = widget + '\r\n\r\n' + pullRequest.description;

Request({
url: 'https://' + process.env.BITBUCKET_USERNAME + ':' + process.env.BITBUCKET_PASSWORD + '@api.bitbucket.org/2.0/repositories/' + pullRequest.source.repository.full_name + '/pullrequests/' + pullRequest.id,
method: 'PUT',
json: pullRequest
}, function (err, response, body) {
if (err) {
res.status(500).end();
return;
}

if (response.body && response.body.error) {
res.status(500).end();
return;
}

res.status(204).end();
});
}
else {
if (pullRequest.description.indexOf('[ ![Codeship Status') === 0) {
console.log('Codeship status already in description');
res.status(204).end();
return;
}

var widget = '[ ![Codeship Status for ' + pullRequest.source.repository.full_name +
'](https://codeship.io/projects/' + req.params.codeshipProjectUuid +'/status?branch=' +
pullRequest.source.branch.name + ')](https://codeship.io/projects/' + req.params.codeshipProjectId + ')';
pullRequest.description = widget + '\r\n\r\n' + pullRequest.description;

Request({
url: 'https://' + process.env.BITBUCKET_USERNAME + ':' + process.env.BITBUCKET_PASSWORD +
'@api.bitbucket.org/2.0/repositories/' + pullRequest.source.repository.full_name + '/pullrequests/' + pullRequest.id,
method: 'PUT',
json: pullRequest
}, function (err, response, body) {
if (err) {
console.error('Error while adding codeship status to pull request:', err);
res.status(500).end();
return;
}

if (response.body && response.body.error) {
console.error('Unexpected error while adding codeship status to pull request:', request.body.error);
res.status(500).end();
return;
}

console.log('Successfully added codeship status to description');
res.status(204).end();
});
});

return app;
};
77 changes: 39 additions & 38 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,40 +1,41 @@
{
"name": "bitbucket-codeship-status",
"version": "2.0.0",
"description": "Small app that will automatically update newly created pull requests in Bitbucket with the branch's Codeship build status.",
"main": "app.js",
"scripts": {
"test": "mocha",
"watch": "mocha -w"
},
"engines": {
"node": "0.10.33"
},
"repository": {
"type": "git",
"url": "https://github.com/chesleybrown/bitbucket-codeship-status.git"
},
"keywords": [
"bitbucket",
"codeship"
],
"author": "Chesley",
"license": "MIT",
"bugs": {
"url": "https://github.com/chesleybrown/bitbucket-codeship-status/issues"
},
"homepage": "https://github.com/chesleybrown/bitbucket-codeship-status",
"dependencies": {
"body-parser": "^1.9.2",
"basic-auth-connect": "1.0.0",
"express": "^4.10.1",
"request": "^2.47.0",
"ejs": "1.0.0"
},
"devDependencies": {
"mocha": "^2.0.1",
"chai": "^1.9.2",
"supertest": "^0.15.0",
"mockery": "^1.4.0"
}
"name": "bitbucket-codeship-status",
"version": "2.0.0",
"description": "Small app that will automatically update newly created pull requests in Bitbucket with the branch's Codeship build status.",
"main": "app.js",
"scripts": {
"test": "mocha",
"watch": "mocha -w"
},
"engines": {
"node": "0.10.33"
},
"repository": {
"type": "git",
"url": "https://github.com/chesleybrown/bitbucket-codeship-status.git"
},
"keywords": [
"bitbucket",
"codeship"
],
"author": "Chesley",
"license": "MIT",
"bugs": {
"url": "https://github.com/chesleybrown/bitbucket-codeship-status/issues"
},
"homepage": "https://github.com/chesleybrown/bitbucket-codeship-status",
"dependencies": {
"basic-auth-connect": "1.0.0",
"body-parser": "^1.9.2",
"ejs": "1.0.0",
"express": "^4.10.1",
"express-validator": "^2.18.0",
"request": "^2.47.0"
},
"devDependencies": {
"mocha": "^2.0.1",
"chai": "^1.9.2",
"supertest": "^0.15.0",
"mockery": "^1.4.0"
}
}
2 changes: 1 addition & 1 deletion test/pull-request-created.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"pullrequest_created": {
"pullrequest": {
"description": "Added description",
"links": {
"decline": {
Expand Down
44 changes: 22 additions & 22 deletions test/pull-request.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var mockery = require('mockery');

describe('Pull Request', function () {
var app, response, Request;

before(function () {
mockery.enable({
warnOnReplace: false,
Expand All @@ -13,18 +13,18 @@ describe('Pull Request', function () {
mockery.registerMock('request', function (opt, callback) {
return Request(opt, callback);
});

app = require('../app')();
});

describe('when ENV setup', function () {
before(function () {
process.env.BITBUCKET_USERNAME = 'username';
process.env.BITBUCKET_PASSWORD = 'password';
});
describe('and WRONG correct credentials', function () {
describe('and pull request data is valid, but description is empty', function () {

describe('and WRONG credentials', function () {
describe('and pull request data is valid', function () {
before(function (done) {
request(app)
.post('/pull-request/ee1399cc-b740-43da-812f-d17901f9efa7/52132')
Expand All @@ -36,15 +36,15 @@ describe('Pull Request', function () {
})
;
});

it('should respond with access denied', function () {
expect(response.status).to.equal(401);
expect(response.body).to.be.empty;
});
});
});
describe('and correct credentials', function () {

describe('and CORRECT credentials', function () {
describe('when pull request data posted to valid url', function () {
describe('and no pull request data', function () {
before(function (done) {
Expand All @@ -57,13 +57,13 @@ describe('Pull Request', function () {
})
;
});

it('should respond with invalid request', function () {
expect(response.status).to.equal(400);
expect(response.body).to.be.empty;
expect(response.body).to.be.instanceof(Array);
});
});

describe('and pull request data missing required information', function () {
before(function (done) {
request(app)
Expand All @@ -76,21 +76,21 @@ describe('Pull Request', function () {
})
;
});

it('should respond with invalid request', function () {
expect(response.status).to.equal(400);
expect(response.body).to.be.empty;
expect(response.body).to.be.instanceof(Array);
});
});

describe('and pull request data is valid, but description is empty', function () {
before(function (done) {
Request = function (opt, callback) {
callback(null, {statusCode: 201});
};

var pullRequestCreated = require('./pull-request-created');
pullRequestCreated.pullrequest_created.description = '';
pullRequestCreated.pullrequest.description = '';
request(app)
.post('/pull-request/ee1399cc-b740-43da-812f-d17901f9efa7/52132')
.auth('username', 'password')
Expand All @@ -101,19 +101,19 @@ describe('Pull Request', function () {
})
;
});

it('should respond with success', function () {
expect(response.status).to.equal(204);
expect(response.body).to.be.empty;
});
});

describe('and pull request data is valid', function () {
before(function (done) {
Request = function (opt, callback) {
callback(null, {statusCode: 201});
};

request(app)
.post('/pull-request/ee1399cc-b740-43da-812f-d17901f9efa7/52132')
.auth('username', 'password')
Expand All @@ -124,15 +124,15 @@ describe('Pull Request', function () {
})
;
});

it('should respond with success', function () {
expect(response.status).to.equal(204);
expect(response.body).to.be.empty;
});
});
});
});

after(function () {
mockery.disable();
delete process.env.BITBUCKET_USERNAME;
Expand Down