To avoid copy-pasting build configurations in TeamCity and keep them DRY you should use configuration templates and build parameters. It’s easy when you’re working with Java or .NET build scripting tools such as Ant, Maven or MSBuild because TeamCity have a first class support for those and you can pass build parameters around with no effort. But same task may become more difficult in other development areas.
CI in Frontend
In frontend development our primary continuous integration tools for now is Bash and Node.js scripts. In TeamCity we use command line runner to execute our scripts. There we can access all build parameters (including configuration) through percent references:
%project.name%. It’s that simple, but mostly uncomfortable and unreliable because of textarea editor without syntax highlighting and linting:
We’ve also tried to build simple CLI applications with arguments and options (see coa module, for example). Then our shell runners began to look like this:
node my-script.js --name=%project.name% --my-service-url=%myService.url%
Much better, but it still daunting to write this again and again and maintain unnecessary CLI interface in every single script.
Making It Work with Node.js
And recently we’ve implemented a very simple npm module that provides seamless access to TeamCity build properties.
For every build TeamCity creates a temporary
.properties file on the agent file system with all build properties stored in it and puts absolute path to this file in
TEAMCITY_BUILD_PROPERTIES_FILE environment variable. Then we can parse that with node-properties and export for further use. And this is basically how it works!
npm install --save teamcity-properties
I also encourage you to commit your
node_modules into your VCS to simplify deployment of your app and tools.
var tcProps = require('teamcity-properties'); // not fail-safe var agentName = tcProps['agent.name']; // throws if no such property var projectName = tcProps.get('myCompany.project.name');
Pretty straight forward. The only caveat is that it’s very important to understand the difference between three types of build parameters and when they should be used. To recall, I’ll just quote the documentation:
Environment variables (defined using
env.prefix) are passed into the spawned build process as environment.
System properties (defined using
system.prefix) are passed into the build scripts of the supported runners (e.g. Ant, MSBuild) as build-tool specific variables.
Configuration parameters (no prefix) are not passed into the build and are only meant to share settings within a build configuration. They are the primary means for customizing a build configuration which is based on a template or uses a meta-runner.
So here we are simply making Node.js scripts understand TeamCity system properties, that’s all. All your custom parameters that you want to use inside your build scripts should be defined in System Properties section:
And accessed without
var myServiceUrl = tcProps.get('myService.url'); // http://example.org
You can read more about configuration parameters in TeamCity docs.
This solution allowed us to move all of our Node.js deployment scripts and tools from TeamCity into separate repository and get rid of complex command line interfaces and other superfluous interlayers.
In the next post I’ll tell you about accessing build parameters from Bash scripts.
Please share your thoughts and suggestions in the comments below.
Thanks for reading! Go now and automate all the things! Have a nice day!