One big thorn in my side lately has been getting our Phonegap/Cordova builds out to our team in a timely manner or just getting our mobile app environment set up. Currently our process involves changing a few files up, running some command line prompts, and then hitting a few different IDE’s to crank out builds to manually upload to Testflight (www.testflightapp.com).
What usually happens
“Hey can we please get a new build out to test with?” – DataChomp
“Yea, give me a few minutes to get the build out.” – DevDweeb
“Ok, lemme know when” – DataChomp
—30 minutes later—
“Hows that build coming?” – DataChomp
“Oh man.. not sure.. something messed up along the way. It’ll be a bit more” – DevDweeb
—waits a bit more—-
“Nevermind. I figured it out.” – DataChomp
“Ok whew, cuz it will be a bit more” – DevDweeb
What I wish would happen
“Hey can we please get a new build out to test with?” – DataChomp
“That was done an hour ago, sir” – Jenkins
What is the prob, bob
It’s the process. Here’s what a human would typically go through for the project:
1) Point the build at the correct API end point (localhost/stage/production)
2) Ensure the HTML has the script for weinre remote debugging (may need to be commented out if not needed)
3) Open XCode – build the ios project
4) Take the build from the project – upload to Testflight
5) Open Eclipse – build the android project
6) Take the build from the android project – upload to Testflight
That is a predictable set of steps for a human, but as we all know, humans are prone to make errors. I know I do.
The answer, then, is I didn’t know how bad ass Grunt was and has so many plugins to assist with automation as I do now.
Luckily for me, I found a great post from Jim at imgur, from his post here: http://imgur.com/blog/2013/07/16/tech-tuesday-using-grunt-to-simplify-deployment-processes/
I’m going to dive in to some ways I’ve put together some grunt tasks to accomplish the above tasks.
Introducing Grunt Task Runner
Grunt is a javascript task runner. Learn more at http://gruntjs.com/
The reason I like using it – the config for grunt is in javascript, grunt is lightweight, grunt has very little requirements, and you can get started with a ton of plug-ins available.
I plan on looking at Jenkins to integrate some of these tasks on check-ins for auto or nightly builds. See this post for an idea: http://sideroad.secret.jp/articles/grunt-on-jenkins/
A few plug-ins I’m using so far:
Grunt Shell – https://github.com/sindresorhus/grunt-shell This plugin gives you some shell commands to easily fire off shell commands such as xcodebuild, or even fire up a weinre server.
Grunt preprocess – https://github.com/jsoverson/grunt-preprocess Great tool to combine template files with environment settings to preprocess HTML/Javascript files to drop in IPs or other settings you specify
Grunt env – https://github.com/jsoverson/grunt-env Grunt tasks to automate environment configuration for future tasks.
Removing the human element for app settings
The human must first place the proper host or ip address in place based on where the build may desire to be pointed at as well as whether or not they want Weinre remote debugging (Read about weinre here: http://people.apache.org/~pmuellr/weinre/docs/latest/).
The host/ip address is stored in the appsettings.js file and the weinre remote debugging IP is stored in the index.html page.
First I specified the files that would be preprocessed in the grunt config file. In this case, I specified both appsettings.js and index.html located in a template directory being processed to another location relative from the gruntfile.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Then I specified the ENV settings in the grunt config:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
You’ll notice above, I assigned an ENV variable IP_ADDRESS to a variable ipAddress, which I’ve specified for Grunt as an option that is passed in via command line. That looked something like this snip:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Using the command line to specify the host, you’d invoke the following grunt command to set up a local dev environment with the server at the specified IP Address:
1
|
|
Now I need to specify some templates to make use of the ENV variables set up. The grunt preprocess plugin documentation is great, so head there for more info. Here is how I applied it to the two files, appsettings.js and index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
And the template for index.html:
1 2 3 4 5 |
|
Removing the human element from app uploads
Another tool that changed the game up for me was the Nomad-cli – a set of tools to build and upload to testflight, amazon, or your FTP choice – found at http://nomad-cli.com/
This gives us a ruby gem we can use to fire off to handle all of our iOS tasks for building and pushing to test flight. The tool I mainly use is called Shenzhen.
A few things are needed. First, we had to create a Gemfile in a subdirectory that used the nomad cli gem:
1 2 |
|
Using the grunt shell task, I needed to ensure whoever ran this task got the nomad-cli gem first, fired off the command to build and distribute to testflight via shenzhen. It looked like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
As you can see, there are quite a bit of commands contained in that grunt shell task. Let’s step through the steps, just to be clear.
- CD to the IOS folder
- Call the nomad CLI tool to build iOS app
- Call the nomad CLI tool to upload to test flight
- CD to the Android folder
- Call the ant script to build the app
- Curl to upload the file to Test Flight
Perhaps you’re wondering how I got the testflight_settings, I specified those as follows:
1 2 3 4 5 6 7 8 |
|
Putting the pieces together
Lets see that Grunt config file now…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
|
So say then, our designer wants to try his design changes out with data from stage. He doesn’t know what files to go touch, and most likely it gets confusing for him. Now with automation, he just types ‘grunt stage’.
Or say, now someone needs to get a build out on test flight for some testers. Simply type ‘grunt testflight’. 1 step is easier and way more predictable than the handful of steps one must jump through.
Although I’m sure there are better ways to do this, I’d love to hear about them. After fighting through tasks such as the grunt testflight plugin, and some vague issues there, the rather clear shell commands provide enough value for myself and my team to automate builds and even have these tasks integrated with any CL like Jenkins.
I hope you can walk away with a few ideas and become more productive. Cheers!