Skip to content

Instantly share code, notes, and snippets.

@andrewabest
Last active February 16, 2018 05:46
Show Gist options
  • Save andrewabest/28040aa2006816b3ee0f32a5dd52ae91 to your computer and use it in GitHub Desktop.
Save andrewabest/28040aa2006816b3ee0f32a5dd52ae91 to your computer and use it in GitHub Desktop.
CI for Cordova (iOS) via VSTS, MacInCloud and HockeyApp

Install the Cordova Build and HockeyApp VSTS extensions into your VSTS tenant.

Sign up for a MacInCloud account, and create a VSTS build agent.

Create a pool for your agent in VSTS: https://support.macincloud.com/support/solutions/articles/8000016614-getting-started-with-the-macincloud-vsts-previously-vso-build-agent-plan and configure the agent following the instructions in the article.

Sign up for a HockeyApp account. Create an API token that VSTS will use to talk to HockeyApp via Account Settings > API Tokens.

For HockeyApp, create a service endpoint in VSTS: https://support.hockeyapp.net/kb/third-party-bug-trackers-services-and-webhooks/how-to-use-hockeyapp-with-visual-studio-team-services-vsts-or-team-foundation-server-tfs#installation-for-vsts

Create a new HockeyApp App. Use the value of the ID attribute within the widget definition in Cordova's config.xml (io.cordova.ci in my case) as the bundle identifier.

Join the apple developer program: https://developer.apple.com/enroll/ - or get someone in your enterprise to enrol you in their already paid-for subscription ;) This step is unavoidable, either you have to cough up $149 USD to personally enrol, or slip in on a previous EA. You cannot proceed without having membership though.

Register your device in the developer portal: https://developer.apple.com/account/ios/device/. To get your device UDID, send a HockeyApp user invite to yourself via your app in HockeyApp and accept it with the device in question. You can then find your device and it's UDID against the user within the HockeyApp web UI, which looks something like ea7e174965b979d7378be1880261f83f9ebxxxxx.

Create a development certificate, App ID and provisioning profile: https://www.joshmorony.com/how-to-create-an-ios-provisioning-profile-and-p12-with-windows. Make sure your App ID matches your widget ID in in config.xml (io.cordova.ci).

The development certificate downloaded from the apple developer portal does not contain your private key, which xcode will need to build your app. You need to generate the p12 file which contains both your public and private keys. Don't just upload the cer to your build agent, as you will end up with errors like No signing certificate "iOS Development" found: No "iOS Development" signing certificate matching team ID "YN278H5GS2" with a private key was found. Don't miss this step!

You'll want an "iOS App Development" provisioning profile, in which you'll seelct your certificate, the app in question, and the devices you'd like to deploy onto.

Upload the development certificate and provisioning profile to your MacInCloud VSTS Agent via the "Actions" menu on the agents page for that agent: https://portal.macincloud.com/#/agent/vsts

Add a build.json alongside the Cordova config.xml so that xcode on our MacInCloud agent uses the correct parameters when we build. It looks somewhat like so:

{
    "ios": {
        "debug": {
            "codeSignIdentity": "iPhone Development",
            "provisioningProfile": "926c2bd6-8de9-4c2f-8407-1016d2dxxxxx",
            "developmentTeam": "FG35JLLxxxxx",
            "packageType": "development"
        },
        "release": {
            "codeSignIdentity": "iPhone Distribution",
            "provisioningProfile": "70f699ad-faf1-4adE-8fea-9d8473xxxxx",
            "developmentTeam": "FG35JLLxxxxx",
            "packageType": "app-store"
        }
    }
}
  • codeSignIdentity will be "iPhone Development", which aligns with the certificate we created in the apple developer portal
  • provisioningProfile get the GUID from your profile like so: openssl smime -inform der -verify -noverify -in file.mobileprovision
  • developmentTeam is the TeamID from your account, which can be retrieved from: https://developer.apple.com/account/#/membership
  • packageType in our case is development *TODO: clarify

Further build.json info from Cordova: https://cordova.apache.org/docs/en/latest/guide/platforms/ios/#signing-an-app

Finally configure your build tasks in VSTS!

Your Cordova Build task will have platform ios, the working directory will be wherever the root of your Cordova app is (folder that contains config.xml), and have the output set to bin.

Your HockeyApp task will use your HockeyApp service as it's connection, use the appID from HockeyApp for the app, and have the Binary File Path as bin/ios/debug/CordovaCI.ipa - the ipa file name being derived from the widget > name element in config.xml.

Once your build is green, you'll get the app appear in HockeyApp on your device, which you can install and run! Woohoo!

Not covered here

  • Environmental configuration substitution
  • Deployment to app stores
  • Android build
  • Plugins etc
@randbb
Copy link

randbb commented Jun 16, 2017

I'm having the same problem as @CinnamonRage. I've confirmed that the codeSignIdentity is correct, and it's authenticating the p12 just fine. It's able to find the mobileprovisioning file according to the VSTS build log. And I've looked inside the mobileprovision file itself and confirmed that the plist has a UUID key defined with the appropriate guid as the value. I'm out of ideas at this point. When googling the error, this is one of three results.

@randbb
Copy link

randbb commented Jun 16, 2017

Alright, I finally figured out the problem @CinnamonRage and I might have been having. When I was following other tutorials, they indicated that we should specify a mobile provisioning and p12 file that's included in source control in the Cordova iOS build step in VSTS. However, if you leave these fields blank, it will use whatever mobile provisioning file was originally uploaded through the macincloud portal. I'm not sure why populating the fields in the build step would cause this kind of error, since it's supposedly doing the equivalent of the default behavior, but in any case, if you follow the @andrewabest instructions and leave those fields blank, I think the build error should go away.

@andrewabest
Copy link
Author

@randbb awesome! Thanks for following that up and posting a solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment