This article originally appeared on July 2, 2014.
“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, whereas 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. Although this is only a benefit for those who choose to write good tests, it enables developers to be aware of broken code just minutes after it's broken.
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, developers can write a test for that piece of functionality and, as long as tests are being run during continuous integration, that bug will never be able to surface again.
Here at Insight, 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 article assumes a basic knowledge of Jenkins.
Goals 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 it to run unit tests and notify us if any of them fail. However, we can leverage some other tools to improve the end product. Here’s a complete list of what we’d 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 of 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’ll need to use npm to install Grunt with:
Installations for npm can be found at the NodeJS
Once grunt-cli is installed, you’ll 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 plug-in, 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’s 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’s 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 five 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’re using Ruby/Compass in your project. Here’s what our injected environment variable section looks like:
You’ll 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 we created during the build process. It’s 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 Secure Shell to transfer the archived file to our dev environment and unzip the file so it’s available to the public. This way, our clients can see what we’re building as we’re 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 to your servers so you can show off all of the hard work you put into your AngularJS application. Additionally, should any tests fail, the application will not be pushed out, and you’ll be promptly notified. From here on, you can continue to add to your Gruntfile as you find plug-ins that decrease redundancies and increase productivity.