WHAT'S IN THIS CHAPTER?
The previous chapters covered how to manage dependencies, testing, and the Gradle build system, which are crucial pieces of the development life cycle. Those pieces are manually used and triggered. In this chapter, we cover continuous integration (CI) servers, which act as the cement between all other processes and convert them into an automated life cycle.
In this chapter you will learn more about CI and why you need it. You will also download and install your own CI server. Finally you learn how to set up a build job from a Git repository, how to trigger a build cycle on every commit, and how to publish your app automatically to Google Play.
An important part of any software development process is getting reliable builds of the software. Despite its importance, we are often surprised when this isn't done. We stress a fully automated and reproducible build, including testing, that runs many times a day. This allows each developer to integrate daily thus reducing integration problems.
—Martin Fowler and Matthew Foemmel, “Continuous Integration” (
http://martinfowler.com/articles/originalContinuousIntegration.html
)
Every software project consists of libraries, modules, and classes that need to integrate with each other. Keeping the integration stable while each piece of integration is subject to change can become a very expensive and time-consuming task. Each introduced change may break another piece of coding integrated with the changed component. Having proper test coverage technically helps to detect such problems. If tests are not consistently and automatically run, the stability of the code depends on how frequently tests are manually executed by developers.
The cost of fixing defects increases proportionally with the length of time it takes to discover them because other modules or systems may also start using the buggy code. Having few and late commit and manual build processes increases the impact of each defect. To have stable projects and builds, you need to minimize human interaction and error as much as possible and automate every eligible piece, including tests and builds.
In a CI system, builds can be broken for several reasons: Tests fail, a component works with some part of the project but fails with the rest, compilation fails, or code quality metrics do not match standards.
When a continuous build system is in action, email(s) with the error log details will be sent to anyone involved in a broken build. Because builds fail almost instantly after commits, CI systems immediately reveal problems and make them visible to everyone.
CI servers are very flexible and easy to integrate and can handle Android projects that use make files, Maven, or Gradle. You need to choose one of those to fully integrate your project with a CI server. This chapter focuses on Gradle, but you may prefer to choose make files or Maven.
Version control systems are another crucial part of the CI process. Each code commit triggers a build process that results in compilation, running tests, and packaging the app on the CI server. We covered the Git version control system in Chapter 9; in this chapter, we focus on integrating a Git project with your CI server.
You need a CI server to do the heavy lifting. Available CI alternatives include Hudson, Jenkins, and Bamboo. Bamboo is a commercial CI server from Atlassian. Hudson and Jenkins are open source, free CI servers used widely in open source and corporate projects and are derived from the same code base.
Throughout this chapter, we focus on Jenkins; however, as we mentioned before, you may prefer to use Hudson, which is similar. The Jenkins distribution can be downloaded from https://jenkins.io/index.html
. At the time of this writing, the latest stable version of Jenkins is 1.654 and Jenkins 2.0 is not yet available.
Jenkins can be downloaded either as a plain WAR file or as an application installer that bundles a web server to run Jenkins. If you already have a Java web server available and running on the target computer, you may prefer to download the WAR file. This section focuses on installing the bundle, which is very straightforward, and the bundle installs with no configuration.
https://jenkins.io/index.html
, as shown in Figure 10.1.
wget -q -O - http://pkg.jenkins-ci.org/debian-stable/jenkins-ci.org.key |
sudo apt-key add -
deb http://pkg.jenkins-ci.org/debian-stable binary/
sudo apt-get update
sudo apt-get install Jenkins
If you are using Mac OS X or Windows 10, just click on the installer.
The following steps are identical for each operating system.
Jenkins comes with the MIT license.
An information window like the one shown in Figure 10.5 displays when the installation is complete.
After you click Close, the installer will open a browser window pointing to localhost:8080
, as shown in Figure 10.6. If you have other applications or servers already using 8080, Jenkins might use another port.
You have installed Jenkins and it is up and running.
Jenkins relies on plugins to integrate with different setups, project types, and properties. Because you will be using Jenkins for Android projects, you need to install several plugins that differentiate an Android project from a standard Maven-based Java project.
Now that you have finished installing Gradle and Git plugins, you can set up a build job to start continuous integration.
Because you have a fresh Jenkins installation with no build jobs yet, Jenkins displays a “create new jobs” option just below the welcome message, as shown in Figure 10.13.
To demonstrate a full build by running tests, generating reports, and building APKs, we need a full project to integrate with our CI server. For this purpose, we will fork the Google I/O 2014 schedule app.
https://github.com/kevinmcdonagh/iosched
and click Fork, as shown in Figure 10.14.
GitHub will clone the repository into your GitHub account. Now you are ready to create your first build job.
Click the Add button next to Credentials and add your username and password, as shown in Figure 10.18.
Below the repository properties (refer to Figure 10.17), you can choose which branches to build. Jenkins allows you to build any branch and helps to oversee any integration and stability problems from the outset. For our purposes, we will continue with the master branch.
Build Triggers is another useful setting to control builds. The first option, Build after other projects are built, is used to create a build dependency to another project's build cycle. This is a very useful setting when your build system relies on another library or API that is subject to change. The second option, Build periodically, determines the frequency of periodic builds, as shown in Figure 10.19.
Any desired frequency can be declared in years, months, days, hours, and minutes. Because nightly builds are strongly encouraged in a CI cycle, add a daily build, which would happen between 12 p.m. and 7 a.m. For more information on custom schedules, click the blue question mark next to schedule box.
As a general rule, you want a build to be triggered immediately after every commit to see if the change has broken the build, so select the Build when a change is pushed to the GitHub option.
If your repository does not support Jenkins but publishes changes, you have to select the Poll SCM option to make Jenkins continuously check your source control system.
Jenkins will schedule a build and will execute the build process when it is idle, as shown in Figure 10.23.
Click the Console Output option to view the build messages and log shown in Figure 10.25.
Congratulations, you have just completed your first successful build! Go back to the Jenkins dashboard to view your project status, which should look like Figure 10.26.
The sun icon shown in the W column in Figure 10.27 represents the status of the project. Because the build is successful and you don't have a failed build, everything is sunny. You may see cloudy or even stormy icons depending on the stability of your build.
You have integrated your project into Jenkins, created a build schedule, and even had a successful build. However, you are still not utilizing Jenkins's Android-specific capabilities.
Once the download is finished and Jenkins restarts itself, you can start configuring the new plugins.
The emulator has the following properties as shown in Figure 10.32:
Now you can go back to build job settings to complete the Move Android APKs to a different release track option.
You have configured your build, so now you can go back and trigger a build and start watching the console output. Because you haven't previously used the Android-specific capabilities of Jenkins, the first build will automatically download and install Android SDK, Android tools, the emulator, and the emulator images, as shown in Figures 10.38 and 10.39.
Finally, when the SDK, tools, emulator, and emulator images are ready, the Android emulator plugin will create an emulator, as shown in Figure 10.40, and will continue with launching, as shown in Figure 10.41.
If the Play account credentials are correct, the Google Play plugin will publish your APK after a successful build and test process.
This chapter discussed the importance of stable and reliable software development. Continuous integration can greatly help minimize bugs and make them visible even in very early stages of application development. CI will also automate the building process, letting developers focus on development tasks.
We covered Jenkins, a widely used and accepted continuous integration server. We focused on how to make Jenkins work with Gradle and Android by making use of the plugins. We covered how to build, test, and publish Android projects by pulling the source from version control and publishing the APK to the Play Store.
Continuous integration servers are the integration point where Gradle and proper test coverage start to shine and where software projects can succeed.