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 Dockerfile.Cordova.

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


#Install curl, git, software-properties-common, python, nodejs, cordova
apt-get update && \
apt-get install -y software-properties-common curl python git && \
curl | tar xz -C /usr/local/ --strip=1 && \
npm install -g cordova@4.3.0

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 myproject-cordova:v1¬†image
  • Volume map the host machine’s present working directory (root directory of the Cordova app) to folder myproject¬†within the container
  • Set the working directory within the container as myproject¬†directory
  • 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


#Install Java
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 | tar xz -C /usr/local/

#Set ANDROID_HOME environment variable
ENV ANDROID_HOME /usr/local/android-sdk-linux


#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!