AngularJS in continuous integration with Jenkins

Do you want to put your JavaScript project in a continuous integration environment? You've tried it but didn't go through with it because of lack of time, tools or feedback? Here's how we did it at ContentSide.

What does continuous integration bring?

Let's remember that continuous integration brings a lot to the life of a project. So it's not a geeky whim to want to put a JavaScript framework in a Jenkins, but a real need.

Just like a java project, the main goal is to have as soon as possible (so after each commit I would say) a warning if the sources become unstable. For example, code modifications incompatible with those of your teammate, or unit tests that fail (those famous tests that pass at home, but fail elsewhere because the environment is different). Why do they fail? Because an anomaly detected during the development phase costs (much) less (literally!) than one detected later (during internal acceptance, customer acceptance, pre-production, production). Because you have to reproduce locally, because you have to go back to developments that may be old, because you have to prepare new deliverables, because you have to re-deliver, ... not to mention your reputation / the customer's trust.

So although we are convinced that continuous integration is a good practice, we have only rarely seen it described with a JavaScript project on the Internet. Maybe because we don't have enough time, or because not everyone is as convinced as we are. So here is our little experiment on this subject.

For information, this is an AngularJS project managed with Yeoman (Yo, Grunt and Bower). In the architecture of this type of project, you will find the NodeJS modules (directory 'nodes_modules/') and the Bower libraries (directory 'bower_components/'). Good practice before you start: don't put these directories in your source management tool! Configuration files list the versions of the modules and libraries needed for the project. So there is no need to version them.

Chosen solution: A Maven plugin

To integrate the project into a Jenkins, we chose to "mavenize" it and entrust the build's grunt tasks to a maven plugin: yeoman-maven-plugin. The ideal would have been to have a Jenkins plugin that would do this, but there was none, and we didn't have the heart to develop one. While a maven plugin was waiting on the web for us to test it a bit 🙂

The fact of mavenising the project also has the advantage of being able to release the project in our maven repository. So we can easily pull this dependency into another project such as a Back Office. With a good configuration of its pom, we can obtain a single War including the Front and Back. This facilitates deliveries, deployments and avoids cross-domain problems(CORS).

Project "mavenisation".

Nothing complicated to start with: We add a pom, an empty src/ directory and our Angular sources in a yo/ directory next to it

We add the plugin in the pom that will orchestrate the build's Grunt tasks. And we configure in the known Maven plugins the location of the files to be put in the final War and the directories to be emptied during the Maven 'clean' task.

So if the 'grunt' task works locally in the yo/ directory, the 'mvn clean package' task on the root directory will do the same.

If you have difficulties with the grunt, you will have the same difficulties with the maven plugin. So I advise you to update the node.js 'yo' package: "npm update yo". And then if you still have problems, you can comment out the grunt tasks that are causing problems in order to continue on our main topic. But here you have to know where you stand, because some tasks depend on other tasks. Example of grunt tasks put in comments : jshint, uglify, ...

A "Free-style" Jenkins job!

Now let's create the Jenkins job on your integration server by choosing "Build a free-style software project" and not "Build a maven2/3 project". I explain why below. Otherwise, classic configuration: Creation of the job, configuration of the versioning server at least, choice of the build (Maven3).

Let's run a build ... and if your integration server is not your dev machine, the result is not very conclusive. And yes, the maven plugin runs the 'npm install', 'bower install' and 'grunt' tasks. But these tools are not necessarily installed on your server.

By the way, this is why you don't need to put the 'nodes_modules/' and 'bower_components/' directories in the configuration management tool. The dependencies are managed by the 'package.json' and 'bower.json' configuration files and are installed by the first 2 tasks.

Installation of build tools on the server

Let's prepare the server hosting your Jenkins. To do this, follow the same steps as for your development environment, namely the installations of : Node.js (npm), Karma, Yeoman (Yo, Grunt and Bower), Ruby (gem), Sass, Compass.

On the other hand, you may encounter problems with rights on a Linux machine. With some Unix knowledge, or even a system administrator, you will be able to get through these steps without any problem (an environment variable management plugin under Jenkins could also save your life).

On my side I had problems in grunt tasks. If the jenkins logs are not clear enough, run the jobs in isolation. To do this, go to an SSH console in the yo/ directory of your Jenkins job (something like /var/lib/jenkins/job/...workspace/yo/) and with the same user that Jenkins uses.

Run a 'grunt' like the plugin, or directly the problematic task in Jenkins.

It can happen that there is a difference in the execution of a task with different users (root and Jenkins user for example). If this is the case, it is a problem of rights or system property definitions that are different.

It also happens (too often) that older versions of nodes are less problematic. For example, a karma@0.8.3 vs karma@0.8.6 or grunt-contrib-imagemin@0.1.4 vs grunt-contrib-imagemin@0.2.0.

Execution of unit tests

Last point, the tests! In the karma.conf and karma-e2e.conf files the browser used for testing is specified. Check that it is PhantomJs, otherwise change it. Normally it has been installed with the Grunt suite.

A bit more touchy with this maven plugin, the failed tests don't make the build unstable (with the list of failed tests) but make it fail. You'll have to look at the Jenkins logs to see what happened.

To solve this, we need to get the output of Karma tests and make them interpretable by Jenkins. The configuration is described on the Karma version 0.10 website. Depending on your version of Karma, the configuration differs a bit. So use the right one!

An important detail: The Karma documentation asks to add the action "Publish JUnit test result report" in the "Post-build" section. In my case, I did not have this option in my Jenkins job. Indeed, I had created a job by selecting "Build a maven2/3 project". And in this job configuration, it is not possible to add the action requested by Karma. So you have to create a job by selecting "Build a free-style software project".

Now you have a Jenkins job that builds your AngularJS project while clearly escalating the failed tests.

Conclusion

It took a while to set up, but we're delighted to have put our AngularJS project into a continuous integration environment. It was a necessary step for our dev team, and even if you're on your own, it ensures a certain quality of your code and allows you todo your releases more easily. Now we can think about automatic deployment... 😉

Discuss it with our devs?


Contact

Are you interested in this topic?

CONTACT US