My Pantheon + Jenkins Process

Here is a rough outline of my Pantheon + Jenkins process. I like my code in BitBucket. I also like Pantheon (check them out). The Pantheon workflow is all about being the source of truth for your code. This is fine, and actually I dig it because it promotes good practices. However, I, and my company, have many projects in BitBucket already, and am using Jenkins more and more for some Continuous Integration functions. We want to keep using BitBucket as our source of truth and our existing workflows, but also want to use Pantheon.

Already, this is problematic and managing it on a developer by developer process is going to be prone to error. You have to push branches to two remotes and deal with, probably, some ugly merging and potentially other issues.

What I want to do is to push to BitBucket and use a commit hook to trigger Jenkins to deploy our code to Pantheon automatically. I use Pantheon Multidev (at work) and this process assumes that the Mutlidev env already exists. It will not create it for you (yet).

The BitBucket commit hook

How this is going to work is we are going to setup a POST hook with BitBucket. We will set the POST url to be a PHP script (our receiver script) in our Jenkins server (or just setup a small ec2 instance to host it if you don't want to install PHP on your Jenkins server). The POST payload will include the list of modified branches. I have called my receiver script jenk.php because, heh. I have setup some environment variables with my Jenkins username and access token so that I can make API requests to the Jenkins server.

The POST hook url looks something like: http://jenkins.server/jenk.php?project=&token=BUILD-PROJECT

Replace with the name of your project, and replace BUILD-PROJECT with your build token.

jenk.php

This is the receiver script. It gets data from your commit, and submits a build request to Jenkins. Easy peasy.

 'jenkins.server/job',
    'username' => getenv('JENKINS_USERNAME'),
    'access_token' => getenv('JENKINS_ACCESS_TOKEN')
);

if (!empty($payload['commits'])) {
    foreach ($payload['commits'] as $commit) {
        if (!empty($commit['branch'])) {

            $url = 'http://' . $jenkins['username'] . ':' . $jenkins['access_token'] . '@' . $jenkins['endpoint'] . '/' . $project . '/buildWithParameters?token=' . $token . '&BRANCH_TO_BUILD=' . $commit['branch'] . '&delay=0sec';

            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, 1);
            $result = curl_exec($ch);
            curl_close($ch);
        }
    }
}

The Jenkins + Pantheon Deploy Configuration

I have configured a parameterized build with Jenkins. The single defined parameter is "BRANCH_TO_BUILD".

Under Source Code Management I have added the two git remotes, one for BitBucket called "origin" and one for Pantheon, called of course, "pantheon". In the "branches to build" section I added in remotes/origin/$BRANCH_TO_BUILD .

Under Build Triggers I used "BUILD-PROJECT".

Under Build I added an "execute shell" task to checkout our branch from origin so that we can push to the pantheon remote - git checkout remotes/origin/$BRANCH_TO_BUILD

Finally, I added a "Git Publisher" post build task configured to push the "$BRANCH_TO_BUILD" to the "pantheon" remote.

To summarize, the process is make code changes, commit, push to BitBucket remote, hook is fired, and a POST is sent to our receiver script, which sends a POST to Jenkins with the $BRANCH_TO_BUILD parameter, and if the build passes, the branch is pushed to Pantheon. If everything worked you will see Pantheon converging your app in the dashboard! If it fails, well, check your console output from the build.

And that's it. We can continue using our regular, non Pantheon, workflow, with Pantheon. The process and workflow stays consistent!