Automated PhoneGap builds with Grunt and Maven
The idea of Continuous Integration (CI) which was first published in an article of Martin Fowler in the year 2000 is an integral part of nearly every modern software development project. This is also true for mobile applications. PhoneGap allows you to write mobile applications with JavaScript, HTML and CSS only without the need to write a single line of native code in languages such as Java or Objective-C. These projects are either built manually with the appropriate platform specific SDKs or just by using the “PhoneGap Build Cloud Service” provided by Adobe. This build-service offers you a user interface plus certain APIs to automate tasks for platform specific compilation, as well. This is the part where “Grunt – The JavaScript Task Runner” comes into play. Grunt is a command line build tool, executing repetitive tasks such as minification, compilation, testing etc. of JavaScript Projects. In this case we focus on mobile apps. Grunt provides tasks for remote execution in the “PhoneGap Build Cloud Service” as well as plenty additional tasks. There are CI-Servers without native Grunt integration on the market or companies want to still use their Maven infrastructure. Hence, we like to demonstrate how a remote PhoneGap build with Grunt is integrated in an existing Maven infrastructure.
Introduction
Why do we want to avoid building our mobile HTML5/JavaScript applications with native SDKs? The answer is fairly simple. It’s cumbersome, platform dependent and not interoperable. First, is the need to have an Apple machine with a running OSX in order to be able to setup the development environment with Xcode/Apple developer tools and the iOS SDK. Second, is setting up an automated build for iOS which can be a time consuming task ending up to take weeks with sometimes questionable results. We did have native iOS build setups in our company, but it was a process involving several intermediate manual tasks. We kept this approach, until the only person being able to execute this so called “automated iOS build” was absent due to illness. In this context shipping a next scheduled release to the customer was not that easy. Just out of this circumstance we did some research and found the PhoneGap Build Service. After two more hours, we were able to ship that particular release. From this moment on, we didn’t trigger any native iOS build. But use PhoneGap Build Service.
Advantages PhoneGap build
One of the main advantages is the ability of compiling mobile applications remote. There’s no need to have any local native SDKs installed. The build service supports multiple platforms like iOS, Android Windows Phone, Blackberry and WebOS. You are asked to upload your sources and trigger the build. A provisioning profile and a certificate is still needed for iOS builds. In addition the service provides a very convenient collaboration feature. This includes user management and enables other team member’s participation either with “readonly” access retrieving the mobile app or with full “read/write” access allowing them uploads, triggering builds etc. There is no additional need for intermediate distribution mechanism. Furthermore the “hydration” feature allows users to speed up deployment cycles. Once this feature is enabled, there’s no need for further redeployment of the mobile app. the service will check for new available versions on every startup and asks the user for per-mission to install to latest version. In order to make a beta version of the app accessible you don’t have to send it via email around the world. You can directly download that from the build service via a QR-Code or with the url linking to the installation. At the end of the day you can use the PhoneGap Build Service even without the user interface but as a command line tool.
Grunt Decision
We use the PhoneGap Build Service as a tool to easily build our HTML5/JavaScript mobile apps. Let’s talk about automation. There are a couple of possibilities provided. First of all, we want to mention that PhoneGap provides a REST API which is used in the background for most approaches. With this API you can create projects, trigger builds update code, maintain signing keys, maintain collaborators and download an app for specific platforms. You can easily integrate it programmatically in your build scripts. One possibility is to tie in a “Webhook” triggering the PhoneGap Build Service on every commit if your source code is hosted on a GitHub repository. Another possibility to include this in our existing continuous integration (CI) environment. You can write your own build script or you directly use Maven in combination with the “phonegap-build-maven-plugin”. Another way is using Grunt. The decision for us to use Grunt is based on the fact that we already use Grunt to build all of our web and mobile applications. Hence, we stick to the same technology.
Prerequisites
- Java installed
- Maven 3.x installed
- Adobe account
- PhoneGap Project created on http://build.PhoneGap.com
- Current Grunt version installed
Grunt configuration
Grunt in combination with two simple tasks enables us to trigger the PhoneGap build. First of all we need to create a zip file with the “grunt-compress” task, uploading the sources. In order to use the task, we need to install it via the node package manager using the command
npm install grunt-compress –save-dev
in the command prompt. After the installation, we can create the Gruntfile.js with the simple configuration as you see in listing 1.
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
compress: {
main: {
options: {
archive: 'target/<%= pkg.name %>-<%= pkg.version %>.zip'
},
files: [
{src: ["www/**/*", "config.xml"], dest: '.'}
]
}
}
});
grunt.registerTask('default', 'compress')
}
Listing 2 – Package information from package.json
{
"name": "petshop",
"version": "0.0.1",
"description": "petshop mobile app",
"main": "index.html",
"author": "",
"license": "",
"devDependencies": {
"grunt": "0.4.5",
"grunt-contrib-compress": "0.9.1",
"grunt-PhoneGap-build": "0.0.8"
}
}
The “options” defines the target directory of the created zip file. The name of the zip file is defined in the package information which can be retrieved from the “package.json” file in listing 2. The files section describes which resources need to be added and compressed to the zip file. These are all static resources recursively under the “www” root folder and the “config.xml” which is on the same level as the “www” folder. Once we trigger the Grunt default task with we will see that a zip file with the name “petshop-0.0.1.zip” is created. We still need to upload that to the build service and trigger the build. We first need to install the “grunt-PhoneGap-build” task with the command
npm install grunt-PhoneGap-build –save-dev
in the command prompt. Once we have installed the task we need to extend the Gruntfile.js as you see in listing 3. Listing 3 – The “grunt-PhoneGap-build” configuration
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-PhoneGap-build');
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
compress: {
main: {
options: {
archive: 'target/<%= pkg.name %>-<%= pkg.version %>.zip'
},
files: [
{src: ["www/**/*", "config.xml"], dest: '.'}
]
}
},
"PhoneGap-build": {
build: {
options: {
archive: "target/<%= pkg.name %>-<%= pkg.version %>.zip",
"appId": "821521",
"user": {
"email": "john.doe@whatever.com",
"password": "secret"
},
"keys": {
ios: {
password: "secret"
}
},
"download": {
ios: "target/<%= pkg.name %>-<%= pkg.version %>.ipa",
android: "target/<%= pkg.name %>-<%= pkg.version %>.apk"
}
}
}
}
});
grunt.registerTask('default', 'compress')
};
The “archive” option describes where the task expects to find the zip file to upload it. The “appId” can be found in the created project on the PhoneGap Build Service. The credentials from your Adobe account must be provided under the “user” section. A password must be provided in order to unlock the signing key for e.g. iOS in the keys section for the appropriate platform. If you want to download the compiled binaries the download section must be properly configured.
CI integration
In order to trigger the Grunt tasks within a CI build, we use Maven as there are still a lot of CI servers on the market that do not provide a smooth integration like Apache Continuum. We do use the “exec-maven-plugin” to execute Grunt as you can see in Listing 4. The watchful observer has already seen that we do a little trick in order to avoid the creation of an unnecessary jar file. We therefore use the “maven-jar-plugin” to set the phase of the jar creation to “none”. In order to trigger the maven build we just need to execute mvn clean package. You will find the zip file created from Grunt and the downloaded binaries build with the PhoneGap Build Service when you inspect the target folder after the build is finished. Listing 4 – Maven configuration Once we have the iOS binaries installed we see our lovely example mobile app running smoothly as expected.
Conclusion
The article has shown us how the “Phonegap Build Service” can be leveraged in order to simplify the build of classic HTML5/JS mobile applications. It reduces the former complexity and improves the interoperability since it handles cross platform code compilation. It does also provide a convenient way to distribute our mobile applications quick and easy by an integrated user management and deployment feature called “Hydration”.
References
http://build.PhoneGap.com PhoneGap Build Service: http://build.PhoneGap.com PhoneGap Build Developer API: http://docs.build.PhoneGap.com/en_US/developer_api_api.md.html#PhoneGap%20Build%20Developer%20API Grunt: http://gruntjs.com/ grunt-PhoneGap-build: https://github.com/centralway/grunt-PhoneGap-build http://mojo.codehaus.org/exec-maven-plugin