3 minutes read
Originally published at medium.com/reachnow-tech on July 3rd, 2019.
In common with most producers of software moovel uses continuous integration and continuous deployment in order to merge changes to a shared mainline frequently and deploy those changes as often as possible. Our engineering squads (teams) are given a large amount of autonomy which includes ownership of the continuous integration and continuous deployment of their products.
Moovel’s principal continuous integration platform is TeamCity, which offers three main primitives for defining CI jobs: projects, build configurations and build steps. A project is a collection of build configurations (or sub-projects for organisation and inheritance) while a build configuration contains builds that are defined as zero (not very useful) or more build steps. All of this leads to two options for defining a job:
- Single build configuration with as many build steps as required.
- N build configurations each with one or more build steps.
Typically a job will fall into the second category, but within this favouring one approach or the other according to taste. At any rate, understanding the differences between these approaches is essential so in this post we’ll explore these in the context of our usage of TeamCity as well as provide a look into where we may go in the future.
Differences between build configurations & build steps
Some notable properties of build configurations:
- May have zero or more build steps
- May have zero or more VCS configurations. May have zero or more triggers (eg run on VCS change, completion of another build)
- Snapshot or artifact dependencies
- Agent requirements specifying agent configurations eg Linux or iOS.
- Other configurations such as the number of permitted test failures and GitHub status integrations.
- May inherit from a configuration template to avoid repetition (we use these extensively).
A build step focuses on actual build operations using one of a many of bundled runners covering a wide range of toolchains, with the most widely used one here being Command Line. (At moovel the vast majority of build steps are shell scripts executed in a docker container. Squads maintain a library of build images for various toolchains, meaning they are able to update things without changing the build agents themselves which would require the involvement of the DevOps team.)
Some notable properties of build steps:
- Every step uses the same VCS configurations (ie the ones in the containing build configuration).
- Steps always execute in the order that they are declared.
- Each step is implicitly triggered by the previous one.
- Steps always execute on the same build agent.
Use cases for multiple build configurations
We identified three cases where multiple build configurations are essential:
The job requires multiple agent pools
We deploy to our different AWS accounts by means of an agent pool in each of them, so a typical case is that a job uses one pool for executing tests, another for deploying to dev and yet another for deploying to prod. Since agent pools can only be scoped to build configurations each part of the chain must use its own one of these.
The job requires multiple triggers
Assuming you TeamCity project produces multiple artifacts and or does that recurring like daily/nightly or weekly you may need separate triggers. There are several options to configure triggers including quartz/cron like trigger.
The job requires multiple multiple repositories
Repositories that are open source do not contain moovel specific deployment information. Deployment scripts are stored in internal repositories. In order to have both, the open source and closed source repositories in one TeamCity project, one can use two build configurations:
- One build configuration for the open source code to test and build a deployment artifact
- Another build configuration referencing the closed source to deploy the deployment artifact
Use multiple build configurations only for scenarios that can not be done in a single build configuration. That includes the use case mentioned above and others that may not be covered here. Everything else should be configured with build steps within build configurations.
Since moovel introduced TeamCity in 2016 (after evaluating it alongside Bamboo, Travis & Jenkins) there has been a move towards CI/CD configuration as code, typically using YAML. Jetbrains decided to use their own offspring Kotlin as the base for the TeamCity Kotlin DSL and while I personally like it a more agnostic choice such as YAML or the provision of additional language bindings would make TeamCity configuration as code more widely attractive.
Thanks for reviewing this article: Benjamin Tisdall