Using Continuous Integration with Xamarin Apps

Using Continuous Integration with Xamarin Apps

Presented at Xamarin Evolve 2014

279b474d14f72e4daa1fc76e6f3c929f?s=128

Greg Shackles

October 08, 2014
Tweet

Transcript

  1. Using Continuous Integration with Xamarin Apps Senior Engineer, Olo @gshackles

    greg@gregshackles.com Greg Shackles
  2. Ship It A Cautionary Tale

  3. None
  4. None
  5. None
  6. None
  7. None
  8. None
  9. None
  10. The Worst by Vitruvius This app is just…the worst.

  11. None
  12. How can we deliver Continuous Awesomeness?

  13. Continuous Integration Let your tools work for you on every

    commit
  14. What is Continuous Integration?

  15. What is Continuous Integration? VCS

  16. What is Continuous Integration? VCS CI Server

  17. What is Continuous Integration? VCS CI Server CI Agent

  18. What is Continuous Integration? VCS CI Server CI Agent

  19. Options TFS Jenkins …and more

  20. Delivery TestFlight HockeyApp …and more

  21. CI with Xamarin Apps

  22. Build Agent Setup

  23. Build Agent Setup ▪ OS X machine for iOS builds

    - Mac Mini works well
  24. Build Agent Setup ▪ OS X machine for iOS builds

    - Mac Mini works well ▪ OS X or Windows machine for Android
  25. Build Agent Setup ▪ OS X machine for iOS builds

    - Mac Mini works well ▪ OS X or Windows machine for Android ▪ Install platform SDKs
  26. Build Agent Setup ▪ OS X machine for iOS builds

    - Mac Mini works well ▪ OS X or Windows machine for Android ▪ Install platform SDKs ▪ Install Xamarin
  27. Build Agent Setup ▪ OS X machine for iOS builds

    - Mac Mini works well ▪ OS X or Windows machine for Android ▪ Install platform SDKs ▪ Install Xamarin ▪ Install agent software for chosen CI server
  28. iOS: Build mdtool build -t:Build -c:AppStore MyAwesomeApp.sln /Applications/Xamarin Studio.app/Contents/MacOS/mdtool

  29. iOS: Package zip -r -y MyAwesomeIosApp.zip path/to/MyAwesomeIosApp.app

  30. Android: Build Windows: msbuild OS X: xbuild xbuild build /t:Build

    /p:Configuration=Release MyAwesomeApp.sln
  31. Android: Package (Part 1) Windows: msbuild OS X: xbuild xbuild

    build /t:PackageForAndroid /p:Configuration=Release MyAwesomeAndroidApp.csproj
  32. Android: Package (Part 2) jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore

    <keystore file> -storepass <keystore password> -signedjar path/to/MyAwesomeAndroidApp-Signed.apk path/to/MyAwesomeAndroidApp.apk <keystore alias>
  33. Android: Package (Part 2) jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore

    <keystore file> -storepass <keystore password> -signedjar path/to/MyAwesomeAndroidApp-Signed.apk path/to/MyAwesomeAndroidApp.apk <keystore alias> zipalign -f -v 4 path/to/MyAwesomeAndroidApp-Signed.apk path/to/MyAwesomeAndroidApp-Aligned.apk
  34. Package Restore NuGet mono NuGet.exe restore path/to/App.sln

  35. Package Restore NuGet mono NuGet.exe restore path/to/App.sln Xamarin Components mono

    xamarin-component.exe restore path/to/App.sln
  36. Xamarin.UITest Run using any NUnit runner nunit-console4 path/to/TestAssembly.dll -xml testresults.xml

  37. Pro Tip: Allow Build to Access UI jetbrains.teamcity.BuildAgent.plist

  38. Pro Tip: Allow Build to Access UI jetbrains.teamcity.BuildAgent.plist ▪ Set

    username <key>UserName</key> <string>your_user</string>
  39. Pro Tip: Allow Build to Access UI jetbrains.teamcity.BuildAgent.plist ▪ Set

    username <key>UserName</key> <string>your_user</string> ▪ Copy to /Library/LaunchAgents
  40. TestCloud Run your UI tests on hundreds of devices test-cloud.exe

    submit path/to/myapp.[ipa|apk] <TestCloud token> --devices <Device Set ID> --series "master" --locale "en_US" --assembly-dir “path/to/bin“ --nunit-xml testresults.xml
  41. FAKE #r @"FAKE/tools/FakeLib.dll"
 #load "build-helpers/iOS.fsx"
 
 open Fake
 
 Target

    "ios-build" (fun () ->
 iOS.build "Debug|iPhoneSimulator"
 )
 
 Target "ios-adhoc" (fun () ->
 iOS.build "AdHoc|iPhone"
 )
 
 Target "ios-testflight" (fun () ->
 iOS.publishToTestFlight()
 )
 
 "ios-adhoc"
 ==> "ios-testflight"
 
 RunTarget()

  42. FAKE #r @"FAKE/tools/FakeLib.dll"
 #load "build-helpers/iOS.fsx"
 
 open Fake
 
 Target

    "ios-build" (fun () ->
 iOS.build "Debug|iPhoneSimulator"
 )
 
 Target "ios-adhoc" (fun () ->
 iOS.build "AdHoc|iPhone"
 )
 
 Target "ios-testflight" (fun () ->
 iOS.publishToTestFlight()
 )
 
 "ios-adhoc"
 ==> "ios-testflight"
 
 RunTarget()
 TeamCityHelper.PublishArtifact "path/to/App.zip"
 Helpers
  43. FAKE #r @"FAKE/tools/FakeLib.dll"
 #load "build-helpers/iOS.fsx"
 
 open Fake
 
 Target

    "ios-build" (fun () ->
 iOS.build "Debug|iPhoneSimulator"
 )
 
 Target "ios-adhoc" (fun () ->
 iOS.build "AdHoc|iPhone"
 )
 
 Target "ios-testflight" (fun () ->
 iOS.publishToTestFlight()
 )
 
 "ios-adhoc"
 ==> "ios-testflight"
 
 RunTarget()
 TeamCityHelper.PublishArtifact "path/to/App.zip"
 TeamCityHelper.sendTeamCityNUnitImport "results.xml"
 Helpers
  44. FAKE #r @"FAKE/tools/FakeLib.dll"
 #load "build-helpers/iOS.fsx"
 
 open Fake
 
 Target

    "ios-build" (fun () ->
 iOS.build "Debug|iPhoneSimulator"
 )
 
 Target "ios-adhoc" (fun () ->
 iOS.build "AdHoc|iPhone"
 )
 
 Target "ios-testflight" (fun () ->
 iOS.publishToTestFlight()
 )
 
 "ios-adhoc"
 ==> "ios-testflight"
 
 RunTarget()
 TeamCityHelper.PublishArtifact "path/to/App.zip"
 TeamCityHelper.sendTeamCityNUnitImport "results.xml"
 TestFlight(fun p ->
 {p with
 File: "path/to/App.app"
 Notes: "Now with 42% more awesome!"
 })
 Helpers
  45. FAKE #r @"FAKE/tools/FakeLib.dll"
 #load "build-helpers/iOS.fsx"
 
 open Fake
 
 Target

    "ios-build" (fun () ->
 iOS.build "Debug|iPhoneSimulator"
 )
 
 Target "ios-adhoc" (fun () ->
 iOS.build "AdHoc|iPhone"
 )
 
 Target "ios-testflight" (fun () ->
 iOS.publishToTestFlight()
 )
 
 "ios-adhoc"
 ==> "ios-testflight"
 
 RunTarget()
 TeamCityHelper.PublishArtifact "path/to/App.zip"
 TeamCityHelper.sendTeamCityNUnitImport "results.xml"
 HipChatNotification(fun p ->
 {p with 
 From = "App BuildBot"
 Message = "Published to TestFlight"
 Color = "green"
 })
 TestFlight(fun p ->
 {p with
 File: "path/to/App.app"
 Notes: "Now with 42% more awesome!"
 })
 Helpers
  46. FAKE Helpers for Xamarin “path/to/MyApp.sln“ |> RestoreComponents (fun defaults ->

    {defaults with ToolPath = “...” })

  47. FAKE Helpers for Xamarin “path/to/MyApp.sln“ |> RestoreComponents (fun defaults ->

    {defaults with ToolPath = “...” })
 iOSBuild (fun defaults ->
 {defaults with
 ProjectPath = “path/to/MyApp.sln”
 Configuration = “AppStore”
 Target = “Build”
 })

  48. FAKE Helpers for Xamarin iOSBuild (fun defaults ->
 {defaults with


    ProjectPath = “path/to/MyApp.sln”
 Configuration = “AppStore”
 Target = “Build”
 })
 “path/to/MyApp.sln“ |> RestoreComponents (fun defaults -> {defaults with ToolPath = “...” })
 AndroidPackage (fun defaults ->
 {defaults with
 ProjectPath = “path/to/MyApp.csproj”
 })
 |> AndroidSignAndAlign (fun defaults ->
 {defaults with
 KeystorePath = “...”
 KeystorePassword = “...”
 KeystoreAlias = “...”
 })
 |> fun file -> TeamCityHelper.PublishArtifact file.FullName

  49. How We Work At

  50. None
  51. None
  52. None
  53. Git / GitHub ▪ Feature branches ▪ Every app builds

    on every branch ▪ Any branch can be pushed to TestFlight ▪ Pull Requests for code review
  54. Test-Driven Core

  55. UI Tests and Test Cloud

  56. Deployment and Distribution

  57. Deployment and Distribution

  58. Deployment and Distribution

  59. Continuous Monitoring

  60. Demo github.com/gshackles/xamarin-ci-sample

  61. Questions? @gshackles greg@gregshackles.com ! github.com/gshackles speakerdeck.com/u/gshackles