One of the primary challenges with the software development is keeping the application up to date with the framework updates. The framework developers usually follow a faster release cycle than what the consumers of it do 🙂 And when it comes to enterprise applications development, catching up with framework updates are nearly a daunting task. With larger enterprises, some projects might run for a longer time with older versions of framework, whereas the newer ones generally start with the latest available version. This gets trickier when everyone uses a common build system (Jenkins, MSTFS or any other tool of their choice). To support all kinds of projects, the build system has to be up to date with the latest versions without breaking the builds of apps that use older versions. There are various means to address this concern. If you are using MSTFS, you can provision different build agent with different configurations to cater to different projects. If you are using Jenkins, you can provision different slaves to cater to different projects/configurations. But they come with a lot of maintenance overhead. Recently, in one of the projects I was working on, we encountered this very similar issue – the hybrid app what we have been working with was built with the below configurations
- Node version 4.4.5
- Cordova version 4.3.0
For some reasons, we couldn’t update the Cordova versions frequently and because of that even the node upgrades weren’t merely possible. We use Jenkins for building the app, and quite recently the Jenkins slave was updated with a latest version of Node & Cordova and that started breaking the build. Since it was an enterprise build system, we didn’t have complete control over the environment setup. Though the best way is to keep up to date with the latest node & Cordova versions to leverage their latest and greatest features, in our case, we had to resort for other means to manage the build. That’s where Docker came for the rescue!
With Docker images, you can create an image with exactly what configurations/framework versions you need and use that image for building the application, instead of using the Jenkins slave’s configuration.This way, all you need to make sure is that Docker Client is installed in the Jenkins slave that can talk to any Docker engine (remote/local) and run the docker commands. In this post, I will show you how to create a Dockerfile to build an image with the required node/Cordova framework versions.
Step 1 : Create a Dockerfile with appropriate steps
At the root folder of your project, Create a directory named Dockerfiles to store all the required docker files. Within that folder create a file named
Add the below lines of code into that file , as you can see I am using
Ubuntu v14.04 as the base image and adding the required components on top of it. I am installing all the required version of modules like
software-properties-common, python, curl, nodejs, cordova
#Build this image on top of Ubuntu v14.04 FROM ubuntu:14.04 MAINTAINER Swami #Install curl, git, software-properties-common, python, nodejs, cordova RUN \ apt-get update && \ apt-get install -y software-properties-common curl python git && \ curl https://nodejs.org/dist/v4.4.5/node-v4.4.5-linux-x64.tar.gz | tar xz -C /usr/local/ --strip=1 && \ npm install -g email@example.com
Step 2 : Build the cordova docker image
Once the Dockerfile is ready, you can use
docker build command to build the image.From the root directory of the application run the below command.
docker build -t myproject-cordova:v1 -f ./Dockerfiles/Dockerfile.Cordova ./Dockerfiles
While building the image, the context from where the command is triggered is important, make sure to have fewer and only required files are present in the directory as the entire directory contents mentioned as context will be sent to the docker host before executing the command. Also, I have used the name of the image as “myproject-cordova” and tagged it as “v1“, feel free to name and tag as appropriate.
Step 3 : Use the built docker image to build/run Cordova apps
Now that, I have the required node/Cordova environment ready, its time to use this for Cordova development and build processes. To do this, we have to use the
volume mapping feature offered by Docker. Navigate to the root directory of your Cordova application and run the below command
docker run -it -v "$(pwd)":/myproject --workdir /myproject myproject-cordova:v1
What the above command does is,
- Create a new container for the
- Volume map the host machine’s present working directory (root directory of the Cordova app) to folder
myprojectwithin the container
- Set the working directory within the container as
- Run the container in an interactive mode for us to execute other Cordova commands
Now you can run all the Cordova commands within the container and the output of them will be available in your host machine as well. Since I just built the docker image in my local machine, its readily available for me to run it in a container. To redistribute this image, you have to push the image to Docker registry. Docker registry can be public/private depending on your setup. The only change that’s required in your Jenkins job would be to include the above
docker run command before triggering the Cordova commands. (and of course, your Jenkins should be configured with the appropriate connection details to your docker registry)
With this approach, you are abstracting away the development/build machine’s environmental configurations and ensuring the Cordova app is built always with the same configuration using a docker image. If you would like to extend this approach to build android apps as well, you can very well do this, by creating a new image with the required versions of java and android SDK installed. The sample Dockerfile that just does that is given below. You can notice that, the android image is built on top of the Cordova image we just built to make sure the entire environmental dependencies your app needs are baked completely into a single image.
#Build this image on top of myproject-cordova:v1 FROM myproject-cordova:v1 MAINTAINER Swami #Install Java RUN \ apt-get update && \ echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && \ add-apt-repository -y ppa:webupd8team/java && \ apt-get update && \ apt-get install -y oracle-java8-installer && \ rm -rf /var/lib/apt/lists/* && \ rm -rf /var/cache/oracle-jdk8-installer #Set JAVA_HOME environment variable ENV JAVA_HOME /usr/lib/jvm/java-8-oracle #Install Android SDK RUN curl http://dl.google.com/android/android-sdk_r24.2-linux.tgz | tar xz -C /usr/local/ #Set ANDROID_HOME environment variable ENV ANDROID_HOME /usr/local/android-sdk-linux #Add ANDROID_HOME to PATH ENV PATH $PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools #Accept license and update android-sdk to API 23 RUN ( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | /usr/local/android-sdk-linux/tools/android update sdk --no-ui -a --filter platform-tool,build-tools-23.0.3,android-23
Follow the same instructions as defined in Step 2 to build the image from this dockerfile. Make sure to provide appropriate image tags and the Dockerfile names while running Docker run commands. The final image that’s built using the above Dockerfile might be bigger one, as it has a lot of components installed in it. In your build system, when this image is pulled for the first time from the docker registry, it might take a while from the second build onwards, the image would be loaded from local cache so, the build should run faster.
That’s it, its as simple as creating two Dockerfiles and images to solve the build issues. Docker has definitely simplified the concerns of environmental dependencies 🙂
Feel free to leave a comment on how you had resolved these kind of environment related issues. I would be more interested to learn other different ways as well!