devops, cordova, ios,

Managing Cordova App Version and Build Numbers in CI Environments

Simon Prickett Simon Prickett Follow Nov 23, 2015 · 4 mins read
Managing Cordova App Version and Build Numbers in CI Environments
Share this

As the Cordova CLI tooling has matured, I’ve been using it more and more on projects in a Continuous Integration environment. I’ve mostly been using the Jenkins Continuous Integration server, but the same principles would apply to others such as Hudson, Circle CI or Bamboo.

(This article originally appeared on the Modus Create blog).

It’s very straightforward to script a Cordova app build, and have your chosen CI server run the build whenever some trigger happens (commit to Github, user chooses to initiate a build manually, etc).

You’d simply use something like this in a build script:

$ cordova build android
$ cordova build ios

Which will build Android and iOS for you. For Android, you’d end up with a .apk file you can put on a device and for iOS you’ll need to export and sign your code with something like this (Apple provisioning profile ID and developer ID masked):

xcodebuild -scheme CordovaLib clean build

xcodebuild -sdk iphoneos -scheme MyApp clean archive \
           -archivePath "$WORKSPACE"/MyApp-$buildNumber

xcodebuild -exportArchive \
           -archivePath "$WORKSPACE"/MyApp-$buildNumber.xcarchive \
           -exportPath "$WORKSPACE"/MyApp-$buildNumber \
           -exportFormat ipa \
           -exportSigningIdentity "iPhone Developer: Simon Prickett (XXXXXXXXXX)" \
           PROVISIONING_PROFILE=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

This assumes it’s running within Jenkins, which sets $WORKSPACE to the folder that Jenkins is working in, and $buildNumber to the build number it is building.

(There’s a few ways of scripting the Apple signing and ipa generation process — if you have a better way I’d love to hear from you).

This is OK, gets the job done, and you end up with a binary (at least on iOS) which has the build number in the file name. You could also rename the Android .apk to have the build number in it with something like:

mv $WORKSPACE/MyApp/platforms/android/ant-build/MyApp.apk \
   $WORKSPACE/MyApp/platforms/android/ant-build/MyApp-$buildNumber.apk

However, what this doesn’t do is set the version or build number of the app itself (the thing that shows up in the App Store / Google Play, or which you may also want to show in your application).

In Cordova, these values are set in the app’s config.xml file as attributes of the widget element:

<?xml version="1.0" encoding="utf-8"?>
<widget id="org.crudworks.myapp" android-versionCode="0000" 
        ios-CFBundleVersion="0000" version="1.0.0" 
        xmlns="http://www.w3.org/ns/widgets" 
        xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>MyApp</name>
    ...
</widget>
  • version: the app version (both platforms).
  • android-versionCode: build number (Android).
  • ios-CFBundleVersion: build number (iOS).

Note: if your config.xml was generated by the Cordova CLI when you originally created the app, you’re probably not going to see the ios-CFBundleVersion and android-versionCode attributes as they aren’t part of the boilerplate template that the CLI uses. You will have to add those attributes if you want to use them.

The CLI will take care of setting the native code version / build numbers accordingly when you run:

$ cordova build <platform>

So, to set version and build number, we edit config.xml. This works, but is not particularly suited to easy scripting where we want to use the Continuous Integration server’s ability to run parameterized builds and supply us with a version and build number dynamically. In an ideal world we’d have a Cordova CLI command that looks something like:

$ cordova version 1.0.0
$ cordova version android 0000
$ cordova version ios 0000
... other platforms as needed ...

or

$ cordova build ios 1.0.0 0000
$ cordova build android 1.0.0 0000
... other platforms as needed ...

Maybe this is something I should raise as an enhancement idea to enable the Cordova CLI to work more seamlessly with a Continuous Integration server?

Meantime, I had a pressing need for something like this, so went ahead and wrote a Python script to make the required changes to config.xml, and take version and build numbers as parameters. I did this in Python as I’m working on Mac OS X, where Python comes with the operating system so doesn’t create an additional dependency. It’s also portable to other operating systems.

I put this on my Github so you can grab it and use it as is, or change it to suit your own needs.

The script expects to be invoked with the following parameters:

  • Version number.
  • Build number.
  • Path to your project’s config.xml.
setCordovaVersion.py 0.0.0 $buildNumber <path_to>/config.xml

Which would set the app version number to 0.0.0, and the build number to whatever build number Jenkins was running the script as part of. You may also want to parameterize the app version number and feed that in through Jenkins or whichever Continuous Integration server you’re using as well.

I run this as a CI build step prior to running the Cordova build commands.

Feel free to check out my script on Github, and use it as you need.

There may be a better way of achieving this, but this is working for me right now. If you have a better idea, or are achieving this in some other way, please do let me know in the comments or on Twitter.

Simon Prickett
Written by Simon Prickett Follow
Experienced software professional based in San Diego, CA.