CommandBox + TravisCI

with Dominic Watson

Presentation recording @ https://experts.adobeconnect.com/_a204547676/p354vjluqa9/

+
++

About me, briefly

Technical Lead at Pixl8 Interactive (PresideCMS)

My commute

(I work from home a lot)

My family

(I'll stop there)

Greta (4), Will (9), Sharron (age undisclosed, taking the pic)

Travis CI

Super easy CI

for

In a nutshell

A (free for open source) service that spins up environments (OSX/Linux) with your source code on so that you can do stuff with it.

 

Stuff includes:
 

  • Running tests
  • Packaging
  • Deploying
  • Notifying on completion

How it flows

1. Sign up with GitHub

2. Enable your repo

3. Configure your build

...with a .travis.yml file in the root of your project

language: java
rvm:
- 1.9.3
sudo: required
before_install:
- echo "deb http://downloads.ortussolutions.com/debs/noarch /" > commandbox.list
- sudo mv commandbox.list /etc/apt/sources.list.d/commandbox.list
- sudo apt-get update
install:
- sudo apt-get install -q -y --force-yes commandbox zip
- gem install s3_website
script:
- "./build.sh"
env:
  global:
  - secure: Ht3aOvyR/EZN...
  - secure: fvjEs8B0hDMK...

4. Push your code

my-project> git push

5. See the build run

.travis.yml

  • yml = Yet another Markup Language
  • .travis.yml made up of logical "sections":
    • Environment (can be a matrix of environments)
    • Lifecycle points
    • Deployment

.travis.yml: Environment

  • Operating system (OSX/Ubuntu)
  • Available runtimes (OpenJDK, Ruby, etc)
  • Datasources & engines
  • Environment variables (can be encrypted)
  • Multiple variables to create a matrix of environments

.travis.yml: Build lifecycle

  • before_install
  • install
  • before_script
  • script
  • after_success/after_failure
  • before_deploy
  • deploy
  • after_deploy
  • after_script

.travis.yml: Deployments

  • Lots of out of box options:
    • S3
    • Heroku
    • Ruby Gems
    • NPM
    • ... lots more!
deploy:
  provider: s3
  access_key_id: ...
  secret_access_key:
    secure: ...
  bucket: downloads.presidecms.com
  local-dir: support/build/artifacts
  upload-dir: presidecms
  skip_cleanup: true
  on:
    repo: pixl8/Preside-CMS
    all_branches: true

CommandBox

  • Run CFML code
  • Spin up a CFML server
  • Package management
    • Install project dependencies
    • etc.
  • More (custom commands, build your own!)

From the command line:

Installing CommandBox
in a Travis Linux Environment

Option 1

Using apt-get

sudo: required
before_install:
- echo "deb http://downloads.ortussolutions.com/debs/noarch /" > commandbox.list
- sudo mv commandbox.list /etc/apt/sources.list.d/commandbox.list
- sudo apt-get update
install:
- sudo apt-get install -q -y --force-yes commandbox zip

Pros: nice and clean

Cons: requires sudo == runs on old Travis CI infrastructure

Remedies: CommandBox to register repo with Travis CI. This would mean we could do:

addons:
  apt:
    sources:
    - commandbox

Option 2

Download binaries

before_install:
- mkdir /tmp/bin
- export PATH=$PATH:/tmp/bin
install:
- curl --location 'https://www.ortussolutions.com/parent/download/commandbox/type/bin' -o /tmp/box.zip
- unzip /tmp/box.zip -d /tmp/bin

Pros: clean enough, runs on new infrastructure

Cons: Not really, but binary download may move???

Running tests

box.json

{
    "name":"Your application name",
    "slug":"your-application-slug",
    "version":"1.0.0",
    "author":"Dominic Watson",
    "private":false,
    "license":[
        {
            "type":"LGPL-2.1",
            "url":"http://opensource.org/licenses/LGPL-2.1"
        }
    ],
    "engines":[
        {
            "type":"lucee",
            "version":">=4.5.x"
        }
    ],
    "devDependencies":{
        "testbox":"2.0.0"
    },
    "installPaths":{
        "testbox":"tests/testbox"
    }
}

.travis.yml

language: java
before_install:
- mkdir /tmp/bin
- export PATH=$PATH:/tmp/bin
install:
- curl --location 'https://www.ortussolutions.com/parent/download/commandbox/type/bin' -o /tmp/box.zip
- unzip /tmp/box.zip -d /tmp/bin
- box install
script:
- "./test"

./test

#!/bin/bash

cd `dirname $0`/tests
CWD="`pwd`"

box $CWD/runtests.cfm

exitcode=$(<.exitcode)
rm -f .exitcode

exit $exitcode

/tests folder

/Application.cfc

component {
	this.name = "tests" & Hash( GetCurrentTemplatePath() );

	this.mappings[ "/testbox" ] = ExpandPath( "./testbox" );
	this.mappings[ "/tests"   ] = ExpandPath( "./tests"   );

	public void function onRequest( required string requestedTemplate ) {
		include template=arguments.requestedTemplate;
	}
}

/runtests.cfm

<cfscript>
	function exitCode( required numeric code ) {
		var exitcodeFile = GetDirectoryFromPath( GetCurrentTemplatePath() ) & "/.exitcode";
		FileWrite( exitcodeFile, code );
	}

	try {

		reporter = cgi.server_protocol == "CLI/1.0" ? "text" : "simple";
		testbox  = new testbox.system.TestBox( options={}, reporter=reporter, directory={
			  recurse  = true
			, mapping  = "tests"
			, filter   = function( required path ){ return true; }
		} );
		echo( Trim( testbox.run() ) );
		resultObject = testbox.getResult();
		errors       = resultObject.getTotalFail() + resultObject.getTotalError();

		exitCode( errors ? 1 : 0 );

	} catch ( any e ) {

		echo( "An error occurred running the tests. Message: [#e.message#], Detail: [#e.detail#]" );
		exitCode( 1 );

	}
</cfscript>

Live coding demo!

Method 2

box start
.. wait for server to start
box testbox run
language: java
env:
  matrix:
# currently the only way to do a multi-dimensional env matrix
    - PLATFORM=railo40 TESTFRAMEWORK=mxunit
    - PLATFORM=railo41 TESTFRAMEWORK=mxunit
    - PLATFORM=railo42 TESTFRAMEWORK=mxunit
    - PLATFORM=lucee451 TESTFRAMEWORK=mxunit
    - PLATFORM=lucee5_beta TESTFRAMEWORK=mxunit
    - PLATFORM=acf10-linux64 TESTFRAMEWORK=mxunit
    - PLATFORM=acf902-linux64 TESTFRAMEWORK=mxunit
    - PLATFORM=railo40 TESTFRAMEWORK=testbox
    - PLATFORM=railo41 TESTFRAMEWORK=testbox
    - PLATFORM=railo42 TESTFRAMEWORK=testbox
    - PLATFORM=lucee5_beta TESTFRAMEWORK=testbox
    - PLATFORM=lucee451 TESTFRAMEWORK=testbox
    - PLATFORM=acf10-linux64 TESTFRAMEWORK=testbox

install: ant -Dtest.framework=$TESTFRAMEWORK -Dsource=remote -Dwork.dir=$HOME/work -Dbuild.dir=$TRAVIS_BUILD_DIR -Dplatform=$PLATFORM install-ci-deps
script: ant -Dtest.framework=$TESTFRAMEWORK -Dsource=remote -Dwork.dir=$HOME/work -Dbuild.dir=$TRAVIS_BUILD_DIR -Dplatform=$PLATFORM test-ci

Building dependencies

As simple as "box install"

{
    "name":"PresideCMS",
    "version":"10.2.4",
    "author":"Pixl8 Interactive",
    "createPackageDirectory":true,
    "packageDirectory":"preside",
 
// ... etc.

    "dependencies":{
        "sticker":"github:pixl8/sticker#v1.0.2",
        "coldbox-standalone-3.8.2":"http://integration.stg.ortussolutions.com/artifacts/ortussolutions/coldbox/3.8.2/coldbox-standalone-3.8.2.zip",
        "presidecmseditor":"github:pixl8/Preside-Editor"
    },
    "devDependencies":{
        "testbox":"2.2.0"
    },
    "installPaths":{
        "sticker":"system/externals/sticker",
        "coldbox-standalone-3.8.2":"system/externals/coldbox-standalone-3.8.2",
        "testbox":"support/tests/testbox",
        "presidecmseditor":"system/assets/ckeditor"
    }
}

Build looks like this...

Other rad shizzle

Lucee/Preside Docs build

language: java
rvm:
- 1.9.3
sudo: required
before_install:
- echo "deb http://downloads.ortussolutions.com/debs/noarch /" > commandbox.list
- sudo mv commandbox.list /etc/apt/sources.list.d/commandbox.list
- sudo apt-get update
install:
- sudo apt-get install -q -y --force-yes commandbox zip
- gem install s3_website
script:
- "./build.sh"
env:
  global:
  - secure: Ht3aOvyR/EZNkySTQafQZ3qWxLh1FTDCHDWLT5J0icUYmAl0s9hnMuFfmCMTh8UHsmm+8TSYzyqA1+86NTogvaN9qKoXQNwBAe5SmNcxsK6+6YCxtJ/H7zRI9G1zpua7A+0iaJDF1ySW1aJ3FhdMZnSktA6UQ0lGbeX4bJSTn9n2dYoSKXm7ygVZ0Vzi/iX/c8Qg6Mll/+wWBCa0jnPOeY3CpxmL3kTXnQyzZkk/fv1GVx+fKmOnTbhO7Ftacrde4BlQpgJT4O0MiqTtVzA5gVn7Z3B1Cgaky0M30ftHZgaQkoFOlXn16paFDV7mYJWORDc/yiujtgt0GoWPWPnVZLuK9MYzwvHg9JzCeyRkoetwhLyoQbTDVliM+uEzwZPrlJX6tAVYs+3X+7Lc0oCnjYsR31qV801lj2exFqzJhIgLaATKElM8XtNcL7gzRup0q0UTy9rCgPuav6XilDNSFnoLM/eQ7JOe8i1SBsXjXfhEtb5uHr8k4HjLxQMObE60yUJFlE8RnAPArRen0DZwDixM0MlPwAfdk9/RYf7s0kwEonL/lzYUHvpaq1CSCZnq/y8tkTpY+9teiKeDRW4/PCFWbGlVjCnAWMv3yzxEpMmKNF8V/8aij+CCGgigDzgIthRfXsoYJCLm/os+bEq25SpWuTBIN95VAsU6ZEsFOSc=
  - secure: fvjEs8B0hDMKXDx8WT9w5WGZlBw3ZUxQAU/vAubBZr+HXt8QtWR3KpMaE001Ffy6aRPBEDqo4cLVs9GVkWzcQy2TK5IoZs/QSfb9utvrhtozcNKeBIHW4MhJfWUH5mAZlDb8ZFRKeBjTZ3enzV/aa4Z6Rrhm8ADow2wvlM59JsXpO7uGBe5fqL+ZSzlKc+Gj/QAD3eTLn6cLFMyhkzobgtaxNapCp+atNP0MBf2p0hgX5kVATUnaDGmsxbLdDRP9c/eEsIzkZOm7qTapq1CCLlTbRdS5N50uRt+FLyV3ksh4CfNvwT3/eLFDKuyCPpGSrwNYkvQ/QLWW6N6Lhxu78EzchHE4WR0mg5NBYfCIPQ2A7ALbA9QOvq3/lHwbUtSg0cK7HJWf6JDKI/Oz66wnINfsG3H0gD+HhKK0TSy9wnnYRdEZulu7JZigwBlXEMO5ENaWQmqFpZpVw6Ift+NtjpQlfYILW2tPMSVbhDAKGZ5TgUmIiWgtVpPOgZWG0Crs7a+nOfWvcxPrgFS5Ee89/v3bwvNlibul/Z+Rd6rI9DePJsEK1AKGPQuCM1FVENqM3MuG+qNUD2nDOTabr4hsKsvDc/c1NdBsEKUXMZLLN2HkPkNvBMAPVgBJm0e6915iZTv4sP4o7T2Ss9RkHOX0lZG51MRFHt1uo27ZcRY9XSk=

build.sh

#!/bin/bash

cd `dirname $0`
CWD="`pwd`"
echo "Building documentation with Lucee :)"

box $CWD/build.cfm
if [ -f .exitcode ]; then
  exitcode=$(<.exitcode)
  rm -f .exitcode
  echo "Exiting build, documentation build failed."
  exit $exitcode
fi

echo "Building complete"
if [ $TRAVIS_BRANCH == 'master' ] && [ $TRAVIS_PULL_REQUEST == 'false' ] ; then
  echo "Zipping up docs for offline download..."
  cd builds/html
  zip -q -r lucee-docs.zip *
  cd ../../
  echo "Zipped."
  echo "Preparing dash artifacts..."
  cp -r builds/html builds/artifacts
  mkdir builds/artifacts/dash
  cp builds/dash/lucee.xml builds/artifacts/dash/
  cd builds/dash
  tar -czf ../../builds/artifacts/dash/lucee.tgz lucee.docset
  cd ../../
  echo "Prepared."
  echo "Syncing with S3..."
  s3_website push
  echo "All done :)"
fi

build.cfm

<cfprocessingdirective suppressWhitespace="true" />
<cfsetting requesttimeout="1200" />
<cfscript>
	function exitCode( required numeric code ) {
		var exitcodeFile = GetDirectoryFromPath( GetCurrentTemplatePath() ) & "/.exitcode";
		FileWrite( exitcodeFile, code );
	}

	try {
		savecontent variable="suppressingwhitespacehere" {
			new api.build.BuildRunner().buildAll();
		}
	} catch ( any e ) {
		exitCode( 1 );

		newline = Chr( 10 );
		echo( "" & newline );
		echo( "Documentation build error" & newline );
		echo( "-------------------------" & newline );
		echo( "" & newline );
		echo( "[#e.type#] error occurred while building the docs. Message: [#e.message#]. Detail: [#e.detail#]." & newline );
		if ( ( e.tagContext ?: [] ).len() ) {
			echo( "" & newline );
			echo( "Stacktrace:" & newline );
			for( var tracePoint in e.tagContext ) {
				echo( "    " & tracepoint.template & " (line #tracepoint.line#)" & newline );
			}
		}
	}
</cfscript>

Pull requests

Persistant build numbers

https://github.com/pixl8/Preside-CMS

build.number.remote.url=http://downloads.presidecms.com/presidecms/build.number

##############################
# MANUALLY UPDATE EACH VERSION
preside.version=10.4.0
##############################

ANT build.properties file

ANT build.xml

Persistant build numbers

https://github.com/pixl8/Preside-CMS

ANT build.xml

<property name="artifacts.dir" value="./artifacts" />
<property name="build.number.file" value="${artifacts.dir}/build.number" />

<target name="incrementBuildNumber">
	<get src="${build.number.remote.url}" dest="${build.number.file}" ignoreerrors="true" />
	<propertyfile file="${build.number.file}" comment="Build Number for ANT. Edit not!">
		<entry key="build.number"
				type="int"
				operation="+"
				pattern="00000"
				default="1" />
	</propertyfile>

	<property file="${build.number.file}" />
</target>

Persistant build numbers

https://github.com/pixl8/Preside-CMS

.travis.yml

deploy:
  provider: s3
  access_key_id: AKIAJ3PTU73I...
  secret_access_key:
    secure: quj9MxIlfx43l9S6CXcchbpsKd43D...
  bucket: downloads.presidecms.com
  local-dir: support/build/artifacts
  upload-dir: presidecms
  skip_cleanup: true
  on:
    repo: pixl8/Preside-CMS
    all_branches: true

Using CommandBox with Travis CI

By Dominic Watson

Using CommandBox with Travis CI

In this talk, I'll show a couple of cases where I'm using CommandBox to drive builds and tests on Travis-CI. I'll cover: * Basics of Travis-CI * Installing CommandBox as part of travis build * What possibilities that opens up (running tests, building docs, etc.)

  • 1,822