A bug should only be found by a human once.
- Someone using continuous integration
A good development workflow has the ability to make good developers great while a bad development workflow will cripple developers and cost the project countless hours. Continuous integration makes it possible to start writing and running tests from the beginning. While this is only a benefit for those that choose to write good tests, it enables the developers to be aware of broken code just minutes after it's broken. Those that are veterans of continuous integration workflows know that any one bug should only be found by a human once. Once a bug is found and reported, the developers can write a test for that piece of functionality and, so long as tests are being run during continuous integration, that bug will never be able to surface again.
Here at Cardinal, we use continuous integration on as many projects as possible. When we started doing AngularJS development, we looked for best practices integrating with Jenkins - our continuous integration tool of choice. Note: This post assumes a basic knowledge of Jenkins.
Deciding what we want done during our Continuous Integration phase
The first step is to figure out what we want Jenkins to do for us to ready our project for production. At the very least, we want to run unit tests and notify us if any of them fail. However, there are some other tools we can leverage to improve the end product. Here is a complete list of what we like Jenkins to do:
- Run unit tests
- Do all of this every time code changes in our private Git repository
Jenkins is responsible for watching for code changes, while Grunt takes care of all the preparation such as minifying and running tests.
Configuring the Angular Project to use Grunt
Most of the time, we scaffold our project using one of the many angular generators for Yeoman. If you use Yeoman as well, you can skip to the next section. However, if you built it from scratch, you will need to use npm to install grunt with:
Installations for npm can be found at the NodeJS website.
Once grunt-cli is installed, you will need a package.json file to keep track of your dev dependencies. If you don't have one, create one in the root directory of your project and start with this example:
Now when you want to add a new grunt plugin, use the following format:
npm install <module> --save-dev
To read more about Grunt and how to tailor it for your application, the official grunt documentation is very useful.
Here is what our Gruntfile looks like: Sample Gruntfile
Now that the AngularJS project is all set up for continuous integration, it's time to set up Jenkins.
After you create a new job and name it according to your project, the next step is to set up jenkins to point to your project's source code management repository. Once that is set up, scroll down to Build Triggers and check the box next to Poll SCM. We set the schedule to H/5 * * * * to check SCM 5 times an hour.
Next you will need to scroll down to the Build Environment section and check the box next to Inject environment variables into the build process. If this doesn't exist, you will need to install the EnvInject plugin and then come back. Once the checkbox is checked, put the following in Properties Content (if you are using PhantomJS):
The second line is only necessary if you are using ruby/compass in your project.
What our injected environment variable section looks like.
You will then need to scroll to the Build section and add an Execute shell step. In the command box, enter:
bower install --dev
The npm install command will install the dependencies we require in order to build the project. We use bower and in order to install the required dependencies, we run bower install. Lastly, we created a custom Grunt process (which you can see in our sample gruntfile) called cibuild that prepares our app for production.
We now need to handle what happens after the build process finishes or aborts.
We added a post-build step to send an email if the build fails. We also specify the archived file that we created during the build process. It is important to specify that you want to Publish JUnit test result report with the xml file that was generated by the grunt command. This is what jenkins will use to determine if the tests passed or failed.
Lastly, we use SSH to transfer the archived file to our dev environment and unzip the file so it is available to the public. This way, our clients can see what we are building as we are building it.
The final Jenkins steps.
We made it
With all of these steps in place, you and your team will be set up for success. Whenever you push new code, Jenkins will run the tests and push the latest code out to your servers so you can show off all the hard work you put in to your AngularJS application. Additionally, should any tests fail, the application will not be pushed out and you will be promptly notified. From here on, you can continue to add to your Gruntfile as you find plugins that decrease redundancies and increase productivity.