For a recent project I have been using Bitbucket, TeamCity and HockeyApp to create a canary build. In addition it is using CocoaPods for dependency management. This approach works well but it takes some time to figure all things out. If something goes wrong it is difficult to find out what went wrong although in most cases somewhere in the large logfiles a clue can be found.
Building an IPA file successfully does not guarantee the signing was successful as well. So the logfile says yes but your testers or early adopters say No. For this reason it is always smart to verify if the app can be installed on a device using Hockey app.
I will tell you what I did to make the four of them cooperate and how you can avoid some of the mistakes that I have made. It goes beyond the purpose of this blog to tell you everything about TeamCity or HockeyApp, so for now I assume you already have installed TeamCity and that you do have a HockeyApp account.
Prerequisites
-
You are using Bitbucket (or another Git based repository such as GitHub)
TeamCity has been installed and configured on a build server.
You do have a Hockey app account up and running.
An Apple developer account.
Obtain a distribution certificate
Use the machine (your own MacBook or a dedicated build server) where TeamCity is running to create a distribution certificate in Apples developer portal.
Create app ID, add devices and create an ad hoc provision profile
Unless you have already done so you need to create a new app ID, add some devices and create an ad hoc provision profile, just like you are used to do when creating an ad hoc distribution on your development machine.
Let's create a little script that takes care of dependencies in the Pod file, building an archive and creating an IPA file, using a provision profile file. At the end of the script we will upload the app to Hockey app and clean the archive and IPA file. It is that easy. Well, once you know what is going on, things are easy...
Pod install
If you are using CocoaPods to manage the dependencies in your project (and well, of course you do !) then you need to update them on your build server as well. That makes sense. So the first of the script goes like this:
pod install
Note: If CocoaPods is not installed on your build server then you need to install the gem first.
sudo gem install cocoapods
Build the archive
The command below is what you need for building the archive for a workspace, as is the case if you are using CocoaPods.
Here we will build the sample workspace using the sample scheme. It will create a sample_ad_hoc XCode archive file in the work folder. It is the same thing as when you choose Archive from the menu in the Xcode IDE.
xcodebuild -workspace sample.xcworkspace -scheme sample clean archive -archivePath /path to your TeamCity work folder/sample_ad_hoc.xcarchive
If you have no clue about schemes (or targets) in your project or workspace. you could use the list command to find out. Execute this command in the directory where your workspace or project reside.
xcodebuild -list - or - xcodebuild -workspace sample.xcworkspace -list
Information about project "sample": Targets: sample sampleTests sampleUITests Build Configurations: Debug Release If no build configuration is specified and -scheme is not passed then "Release" is used. Schemes: sample sample-cal
Export the archive
If everything went well an archive file has been created. The next step is to create a distributable IPA file from it. For this we need to have a valid provision profile file that you have created in the developer portal previously.
xcrun xcodebuild -exportArchive -exportPath build/ -archivePath "path to work folder/sample_ad_hoc.xcarchive" exportOptionsPlist exportOptions.plist -exportProvisioningProfile "name of the provisioning profile"
Here you need the provision profile file that you have download from the Apple developer portal. It probably will be convenient to commit the provision profile file to the repository as well.You can also download the provision profile in a different directory on the build server and include the path to it.
Note: The name of the provision profile is the name as you have typed it at the Apple developer portal when creating it (or as it appears in Xcode -if you would have downloaded and installed the provision profile by double clicking on it-). Other than you might have expected it is not the name of the file.
Distribute the IPA file
Verify if a build.ipa file exists in the work folder. If it does you can distribute it to HockeyApp. It requires an app token from HockeyApp and a Hockey app Id. Make sure the app Id is configured for uploading purposes.
Using the curl command you can easily upload the IPA file you have just created. If this is for a daily (or continuous) build you probably do not want to notify all users each time a new version is available. You can use notify=0 for that.
HockeyApp obtains the version Id from the IPA file so you might want to create an auto incremental script later.
curl -F "status=2" -F "notify=0" -F "ipa=@/build.ipa" -H "X-HockeyAppToken: " https://rink.hockeyapp.net/api/2/apps/ /app_versions/upload
Clean up
Very important but easy to forget is to clean up things, just to make sure that Hockey App fails if the build fails as well. Include this in your script to remove both the IPA and the archive:
rm build.ipa rm -rf //sample_ad_hoc.xcarchive
Conclusion
Finally your script looks more or less like this:
pod install xcodebuild -workspace sample.xcworkspace -scheme sample clean archive -archivePath /path to your TeamCity work folder/sample_ad_hoc.xcarchive xcrun xcodebuild -exportArchive -exportPath build/ -archivePath "path to work folder/sample_ad_hoc.xcarchive" exportOptionsPlist exportOptions.plist -exportProvisioningProfile "name of the provisioning profile" curl -F "status=2" -F "notify=0" -F "ipa=@/build.ipa" -H "X-HockeyAppToken:your hockey app app token" https://rink.hockeyapp.net/api/2/apps/ your hockey app app id/app_versions/upload rm build.ipa rm -rf / /sample_ad_hoc.xcarchive
You can store the script in a file and call it in a single build step or you can create multiple build steps. With some modifications you can use also the script for Jenkins instead of Teamcity.
These are just the basics for a CI/CD flow and there many things that you could include with a script or additional build steps. What about automated unit testing? Or running Cucumber tests on your build server? That would be fun too!
Further reading
-
1. TeamCity
2. CocoaPods
3. Building from the Command Line
4. Xcode command line tools
5. Cucumber
6. Hockey app