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
Alternative method
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
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
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
.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,911