Metal Toad has been building applications and cloud environments for some of the most well-known global brands for over a decade. Learn more > >

How to Integrate GitHub PRs with Bamboo CI

Bamboo / GitHub integration isn't perfect – perhaps because Atlassian wants to steer you towards Bitbucket (their GitHub competitor). Out of the box, there are several headaches. Below, I'll cover these, and how to solve each one:

Status API integration

The GitHub Status API allows external services to mark commits with an error, failure, pending, or success state, which is then reflected in pull requests involving those commits. As an example, one common use is for continuous integration services to mark commits as passing or failing builds using status. The target_url would be the full URL to the build output, and the description would be the high level summary of what happened with the build.

Fortunately, this one is easy: Just install the GitHub Status plugin from the Atlassian marketplace. This will give you the reassuring green button when merging your PRs (assuming the tests passed)!

Automatic branch plan creation

Out-of-the-box, Bamboo won't automatically create a new branch plan for PR's. (There's an option to automatically build all remote branches, but that isn't really what we wanted.) To solve this, a custom webhook is needed for glue (code is below).

Change detection

Unless you're willing to rely on polling (yuck), it's also necessary to have a custom webhook to trigger change detection. In order to make managed webhooks simpler, we've combined this into a single API service with the branch plan creation.

Shared credentials

Shared credentials cannot be used with GitHub. This makes rotating the GitHub password almost impossible. A possible workaround might be making bulk updates in Postgres; I'll post an update if we figure this out.

The webhook code

We host this as a Lambda function in AWS, and have it connected to an API Gateway. Replace the API key with a random value, and replace the hostname with your Bamboo server URL. Many thanks to Jon Duell for writing and testing this with me!

'use strict';
 
// source code for AWS Lambda function
// Runs https://api.ci.EXAMPLE.com/buildplan/{bamboo_planKey}/githubevent
// This notifies Bamboo of new pushes and pull requests
 
const https = require('https');
 
exports.handler = (event, context, callback) => {
 
    const done = (status, body) => callback(null, {
        statusCode: status,
        body: JSON.stringify(body),
        headers: {
            'Content-Type': 'application/json',
        },
    });
 
    if (event.queryStringParameters["api-key"] !== '999999999999999999999999') {
      done(403, "Unauthorized: missing or invalid api-key.");
      return;
    }
 
    var body = JSON.parse(event.body);
 
    /************* Process Push events *************/
    if (event.headers["X-GitHub-Event"] === "push") {
        var options = {
            hostname: 'ci.EXAMPLE.com',
            path: "/rest/triggers/1.0/remote/changeDetection" +
                "?planKey=" + encodeURIComponent(event.pathParameters.bamboo_planKey) +
                "&skipBranches=false",
            headers: {
                "X-Atlassian-Token": "no-check",
                "Content-Type": "application/json"
            },
            method: 'POST'
        };
 
        var req = https.request(options, (response) => {
            response.on('data', (chunk) => {
                done(response.statusCode, chunk.toString());
            })
        });
        req.on('error', (e) => { done(500, e); });
        req.end();
        return;
 
    }
 
    /************* Process PR events *************/
    if (event.headers["X-GitHub-Event"] === "pull_request"
        && (body.action === "opened" || body.action === "reopened")) {
 
        var pr = body.number;
        var pr_branch = body.pull_request.head.ref;
        var plan_key = event.pathParameters.bamboo_planKey;
        options = {
            method: 'put',
            host: process.env.bamboo_url,
            path: '/rest/api/latest/plan/' + plan_key + '/branch/' + pr_branch + '.json?vcsBranch=' + pr_branch,
            auth: process.env.bamboo_user + ':' + process.env.bamboo_pass,
            options: {
                'vscBranch': '/refs/heads/' + pr_branch,
                'os_authType': 'basic'
            }
        };
 
        req = https.request(options, (response) => {
            response.on('data', (chunk) => {
                done(response.statusCode, JSON.parse(chunk.toString()));
            });
        });
        req.on('error', (e) => { done(500, e); });
        req.end();
        return;
    }
 
    /************* Catch-all *************/
    done(200, "ok. Nothing to do.");
    return;
};

GitHub.com

In your project settings, add the webhook:

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <cpp>, <java>, <php>. The supported tag styles are: <foo>, [foo].
  • Web page addresses and email addresses turn into links automatically.
  • Lines and paragraphs break automatically.

Ready to get started?