Traditionally all needed versions of all needed tools were installed on the build server… and they have to be maintained. But that is quite an impossible task. Who decides which can be upgraded and which can be removed?! So as a result you will end up with a cluttered build server.
Our initial build server was becoming the same ‘monster’ and for a fresh build server we approached it a bit different. What if you only install Docker on it and have a script very close to the actual code to define how the code can/should be built.
What we did was adding a build.sh script to each code repository. That script would then live right next to your code. If your requirements change, you also change the way it is built. Since it is in the code repository, it is versioned as well. Whichever revision you check out, you can always build it (unless it does not compile in the first place).
A very simple example on how such a script would look like when you would have some Java code that required Java 8 together with Maven 3.3 is:
#! /usr/bin/env bash MVN_CMD="mvn -B clean package" MVN_CONTAINER_VERSION="maven:3.3-jdk-8" docker run --rm \ -v "$PWD":/usr/src/project \ -w /usr/src/project \ -v "$HOME"/.m2:/root/.m2 \ $MVN_CONTAINER_VERSION \ $MVN_CMD
Explaining the script
So what happens here…
First of all we define which command we are going to run. In our case this is a Maven command to package the artefact. Next the image is defined that is being used to execute the actual Maven command. So in this case it is a Docker image that has Maven 3.3 and Java 8 installed.
Then comes the most interesting part… the actual running of the Docker image.
Here we say: run the image, and remove it again when it exits. But also map our current directory to the project directory which is inside the running container. This directory then contains the source code on your machine. So when you would go into the container and look at the project directory, you would see all your source code and the build-script. Since we want to run any command from that directory, we also set the project directory as the working directory.
The next line again maps an external directory in the container. In this case the directory containing the Maven settings. This is a bit weird, since apparently you do need to configure something for Maven in order to get the container to work correctly. But then again, you do not need to have Maven installed somewhere (and keep it up to date), but you only need to configure it. How to do this, could be part of a README.md which also lives near the code and is versioned.
Last part of the script is telling Docker which image to use and which command to execute.
This approach has a couple advantages. One of them is, that on the CI server you only have to update Docker every now and then!
Another advantage is, that every developer can compile and test the code without having to install all tools. All a developer needs to have is a running Docker environment. If the CI server can run it… so can any developer.