The target audience of this post is primarily developers charged with maintaining a site on Pantheon, and who would like to offload regression testing to a machine, in this case Jenkins. The test framework used is Behat but the approach should be translatable to your framework of choice.

The ultimate goal is to have Jenkins execute Behat tests, against the appropriate Pantheon environment, whenever application code for that environment is updated.

Primary components

  • The php script that Pantheon's webhook service (Quicksilver) will execute.
  • A secrets.json file to store credentials (not in source control).
  • The Jenkins job.
  • An entry in pantheon.yml that tells quicksilver what script to execute and when.

 

PHP webhook

First, create the webhook and confirm it can successfully trigger a job on the Jenkins server. Start by downloading a copy of Pantheon's example. The following modifications in jenkins_integration.php might be necessary

<?php

curl_setopt($curl, CURLOPT_POSTFIELDS, array(
  'token' => $secrets->token,
));

to

<?php

curl_setopt($curl, CURLOPT_POSTFIELDS, array(
  'token' => $secrets['token'],
));

because $secrets has already been json_decode()'ed. Good opportunity for a PR :) 

Also, this is not required, but useful if triggering different Jenkins jobs per environment.

$secrets = _get_secrets('secrets.json');
$secrets = $secrets['test-build'];

Its purpose is explained in the next section.

Locally put this in docroot/private/scripts/jenkins_integration.php

secrets.json

{
  "slack_url": "",
  "test-build": {
    "jenkins_url": "",
    "token": "",
    "username": "",
    "api_token": ""
  }
}

 

If Quicksilver is already being used there might be other config in secrets.json besides jenkins integration, like "slack_url": "", for Slack notifications. Be careful not to overwrite!

"test-build": {  is an entry for a multi-dev environment. All the config specific for that multi-dev goes in here. The idea being that it's possible different environments need to use different jobs, or different Jenkins servers.

Point "jenkins_url": at the specific job you want triggered, e.g. https://myjenkins.com/job/behat_tests_job_name/build

"token": Grants access to trigger behat_tests_job_name. This is defined in the Jenkins job configuration. A random 32 char alphanumeric string should suffice.

"username": the Jenkins user recorded as triggering the build.

"api_token": grants general access to Jenkins for "username":. If you have admin privileges in Jenkins the api token for a user can be viewed at https://your_jenkins.com/securityRealm/user/some_user_name/configure. Ensure the user has sufficient permission to read the job you want to build.

Put the file in docroot/files/private/secrets.json for now.

Test by

  • $ cd docroot
  • $ php private/scripts/jenkins_integration.php

It will likely complain No secrets file found. Aborting!, because $_SERVER['HOME'] in _get_secrets() is not accurate. Change this to secrets.json absolute path to confirm credentials are accurate, and then after change it back.

Once a Build queued response is displayed, login to Jenkins dashboard, and confirm a new build was actually created.

When ready to test live upload secrets.json to the environment via sftp (credentials are in Pantheon dashboard > Site > Environment tab > Connection Info) and

sftp> cd files
sftp> mkdir private // Skip this if files/private already exists
sftp> cd private
sftp> put secrets.json

 

The Jenkins job

Create a Freestyle project. The Git plugin is useful for connecting jobs with repos. Connect to the one containing the Behat tests. This is setup under the 'Source Code Management' section of the job configuration.

An example setup with tests and Drupal application in the same repo might have the tests located at docroot/private/tests/behat. On the Jenkins server install composer and npm globally to manage test dependencies.

Under the 'Build Triggers' section select the 'Trigger builds remotely' checkbox and define an Authentication Token. Make it long!

Next is a series of shell steps. Keeping most of Behat's dependencies contained to the Behat directory minimizes the amount of cruft installed globally on the Jenkins server which might be running a diverse number of jobs. Because of this the shell steps typically start with

$ cd private/tests/behat

Update dependencies

  • Execute shell 1: $ composer install
  • Execute shell 2: $ npm install

Start phantomjs (or browser of choice)

  • Execute shell 3: $ nohup node_modules/phantomjs/bin/phantomjs --webdriver=8643 &

Run Behat

  • Execute shell 4: $ bin/behat --strict &> behat_tests.txt

The Behat output is redirected to behat_tests.txt  and sent to Slack for logging. Alternatively leave it to stdout and consult the console output of the job in the Jenkins dashboard. Note the browser, browser driver, and domain configuration for Behat are defined in behat.yml and out of scope for this discussion.

pantheon.yml

Now that the script is able to trigger the job, and the job is able to execute the Behat tests, all that's left is the final glue instructing the Quicksilver service what to do. In docroot/pantheon.yml include

workflows:
  sync_code:
    after:
        - type: webphp
          description: Integrate With Jenkins
          script: private/scripts/jenkins_integration.php

Now after the code has been sync'ed to the Pantheon repo (a commit is pushed), Quicksilver will execute the webhook script, which will trigger the Jenkins job to kickoff testing.

Can test, and debug, this by pushing empty commits to the multi-dev branch

$ git commit --allow-empty

and watching Quicksilver output with 

terminus workflow:watch

That's pretty much it. All that's left is to outline the mission critical features of your site, translate those into Behat tests, and have Jenkins run them. Now your QA team can focus on novel ways to break the application while Jenkins helps with the tedious search for regressions. Happy automating!

Tags