the design and implementation of cyber-dojo

At the excellent Agile on the Beach conference in Cornwall I did a presentation outlining some of the history, design and implementation of cyber-dojo. The video has just gone live on youtube.



Travis build pipeline

I've been working on the Travis build pipeline for cyber-dojo. The biggest gotcha I hit was that a failure in an after_success: section of a .travis.yml file does not fail the build. This was an issue because after successfully building and testing a docker image I wanted to do two things (and know if they failed):
  • push the docker image to dockerhub
  • trigger git repos dependent on the docker image so they in turn run their .travis.yml files.
I solved this by doing both steps at the end of the .travis.yml script: section.

... language: node_js script: - ... - curl -O https://raw.githubusercontent.com/cyber-dojo/ruby/master/push_and_trigger.sh - chmod +x push_and_trigger.sh - ./push_and_trigger.sh [DEPENDENT-REPO...]


push_and_trigger.sh looks like this:

#!/bin/bash set -e ... if [ "${TRAVIS_PULL_REQUEST}" == "false" ]; then BRANCH=${TRAVIS_BRANCH} else BRANCH=${TRAVIS_PULL_REQUEST_BRANCH} fi if [ "${BRANCH}" == "master" ]; then docker login --username "${DOCKER_USERNAME}" --password "${DOCKER_PASSWORD}" TAG_NAME=$(basename ${TRAVIS_REPO_SLUG}) docker push cyberdojo/${TAG_NAME} echo "PUSHED cyberdojo/${TAG_NAME} to dockerhub" npm install travis-ci script=trigger-build.js curl -O https://raw.githubusercontent.com/cyber-dojo/ruby/master/${script} node ${script} ${*} fi


trigger-build.js looks like this:

... var Travis = require('travis-ci'); var travis = new Travis({ version: '2.0.0', headers: { 'User-Agent': 'Travis/1.0' } }); var exit = function(call,error,response) { console.error('ERROR:travis.' + call + 'function(error,response) { ...'); console.error(' error:' + error); console.error('response:' + JSON.stringify(response, null, '\t')); process.exit(1); }; travis.authenticate({ github_token: process.env.GITHUB_TOKEN }, function(error,response) { var repos = process.argv.slice(2); if (error) { exit('authenticate({...}, ', error, response); } repos.forEach(function(repo) { var parts = repo.split('/'); var name = parts[0]; var tag = parts[1]; travis.repos(name, tag).builds.get(function(error,response) { if (error) { exit('repos(' + name + ',' + tag + ').builds.get(', error, response); } travis.requests.post({ build_id: response.builds[0].id }, function(error,response) { if (error) { exit('requests.post({...}, ', error, response); } console.log(repo + ':' + response.flash[0].notice); }); }); }); });

Note the [set -e] in push_and_trigger.sh and the [process.exit(1)] in trigger-build.js
Hope this proves useful!