AWS

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


Filed under:

Like this article? Check out our top 5 Git tips and tricks

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:

  1. Status API integration
  2. Automatic branch plan creation
  3. Change detection
  4. Shared credentials
  5. The webhook code

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 AWS Lambda function, 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:

Similar posts

Get notified on new marketing insights

Be the first to know about new B2B SaaS Marketing insights to build or refine your marketing function with the tools and knowledge of today’s industry.