fastlaneとBitriseで構築するiOSのCI/CDレシピ / iOS CI/CD Recipe Constructed with fastlane and Bitrise

070d33664df274636dad3da21c9b21fc?s=47 rockname
February 21, 2019

fastlaneとBitriseで構築するiOSのCI/CDレシピ / iOS CI/CD Recipe Constructed with fastlane and Bitrise

070d33664df274636dad3da21c9b21fc?s=128

rockname

February 21, 2019
Tweet

Transcript

  1. fastlaneͱBitriseͰߏங͢Δ iOSͷCI/CDϨγϐ CI/CD Test Night #3 2019/02/21 גࣜձࣾϛΫγΟ ΈͯͶࣄۀ෦ ϩΫωϜ@rockname

  2. ϩΫωϜ@rockname • גࣜձࣾϛΫγΟ 2018೥৽ଔೖࣾ • Ո଒ΞϧόϜΈͯͶ • ΞϓϦ։ൃάϧʔϓ 
 iOS(Swift,

    objc) > Android(Kotlin, Java) == Rails(ruby) ࣗݾ঺հ
  3. ΞδΣϯμ ʲ࿩͢͜ͱʳ • Bitriseಋೖ·ͰͷྲྀΕ • BitriseͷWorkflowͷ૊Έํɾछྨ • fastlaneʹ͓͚Δlaneͷ૊Έํɾछྨ ʲ࿩͞ͳ͍͜ͱʳ •

    Bitriseͷૢ࡞ํ๏ • fastlaneͷ࢖༻ํ๏
  4. BitriseಋೖҎલͷCI/CDࣄ৘ • ࣾ಺macminiͰJenkinsͱhubot͕ಈ͍͍ͯͨ

  5. Jenkins • GitHubͷWebhookͰPRʹίϛοτ͕ੵ·ΕΔͨͼʹςετ ͕૸Δ PR commits Test result Testing

  6. hubot • Slack͔ΒίϚϯυΛड͚͚ͭͯDeployGate,
 AppStore΁഑৴ Building Request Deploy

  7. ਏ͔ͬͨ͜ͱ • macminiΛ୭͔͕ϝϯςͯ͋͛͠Δඞཁ͕͋Δ • Xcode10ରԠͷࡍ͸͜͜ͷลΓͰ͍ͩͿർฐͨ͠ • ࣾ಺macmini͸ࣾ֎͔ΒͷΞΫηεΛ஄͍͍ͯΔͷͰɺϦϞʔτ ͰCIͷ݁ՌΛݟΕͳ͍ (VPNΛܨ͛͹ݟΕΔ͕…)

  8. Jenkins͔ΒBitriseʹ…ʂ!!

  9. Bitriseͱ͸ʁ • ϞόΠϧʹಛԽͨ͠CI/CDͷαʔϏε • ରԠ: iOS/Android/Xamarin/
 ReactNative/Flutter… • GUIʹΑΔࢹ֮తͳૢ࡞Ͱ
 WorkflowΛ؆୯ʹ૊ΊΔ

  10. ͳͥBitriseʹͨ͠ͷʁ • ଞͷΫϥ΢υCIαʔϏεͱൺ΂ͯ֨ஈʹ͍҆ • ৽όʔδϣϯͷXcodeରԠ͕ૣ͍ • νʔϜʹBitriseϢʔβʔ͕͢ͰʹԿ໊͔͍ͨ • ྲྀߦ͍ͬͯͨ

  11. Ͳ͏΍ͬͯҠߦ͍ͯ͘͠

  12. ݱঢ়ͷmacminiͰͷϏϧυ • Test • JenkinsͰPR΁ͷίϛοτΛϑοΫ • deploygate΁ͷ഑৴ • inhouse •

    ࣾ಺഑෍༻ (Enterprise) • staging • QA༻ • AppStore΁ͷΞοϓϩʔυ(fastlane/deliver)
  13. ݒ೦ • ҰؾʹBitriseʹҾͬӽ͢ͷ͸ා͍ • ϏϧυεΫϦϓτ͕187ߦ͋Δ • BitriseͰ࡞ΔWorkflowͷόʔδϣϯ؅ཧ͕Ͱ͖ͳ͍ • (Ͱ͖Δ͕GUIͰࢹೝͰ͖ͳ͘ͳΔ)

  14. ݒ೦ • ҰؾʹBitriseʹҾͬӽ͢ͷ͸ා͍ • ϏϧυεΫϦϓτ͕187ߦ͋Δ • BitriseͰ࡞ΔWorkflowͷόʔδϣϯ؅ཧ͕Ͱ͖ͳ͍ • (Ͱ͖Δ͕GUIͰࢹೝͰ͖ͳ͘ͳΔ)

  15. fastlaneͱ͸ʁ • ओʹiOS/AndroidΞϓϦͷϏϧυ, ςετ, σϓϩΠΛߦ͏ͨΊͷRuby੡λε Ϋϥϯφʔ • iOSͰ͸ • gym:

    ΞϓϦͷϏϧυ • deliver: AppStoreConnect΁ͷ
 Ξοϓϩʔυ • scan: ςετͷ࣮ߦ • …
  16. ํ਑ • Step1: fastlaneʹεΫϦϓτΛدͤΔ • Step2: Bitriseʹ׬શҠߦ => Ұؾʹmacmini͔ΒҠߦ͠ͳͯ͘ࡁΉ͔Β҆৺ =>

    git؅ཧ͍ͨ͠Ϩγϐ͸fastlaneͰ
  17. 2018೥12݄த०ʹ ແࣄʹҠߦ׬ྃ

  18. None
  19. None
  20. inhouse/staging/release • inhouse: • deploygate΁഑৴ • ࣾ಺഑෍༻ (Enterprise) • staging:

    • Deploygate΁഑৴ • QA༻ • release • AppStoreConnect΁Ξοϓϩʔυ
  21. Fastfile: inhouse/staging lane :inhouse do |options| gym upload_to_deploygate(config: "Inhouse") end

    lane :staging do |options| gym upload_to_deploygate(config: "Staging") end
  22. Fastfile: inhouse/staging lane :inhouse do |options| gym upload_to_deploygate(config: "Inhouse") end

    lane :staging do |options| gym upload_to_deploygate(config: "Staging") end gymͰipaੜ੒
  23. Fastfile: inhouse/staging lane :inhouse do |options| gym upload_to_deploygate(config: "Inhouse") end

    lane :staging do |options| gym upload_to_deploygate(config: "Staging") end private_laneͰDeploygate΁഑৴
  24. Fastfile: release lane :release do |options| gym deliver( ipa: “Mitene.ipa",

    submit_for_review: false, force: true ) upload_to_github_release( github_api_token: ENV["GITHUB_API_TOKEN"], tag_name: options[:tag_name] ) end
  25. Fastfile: release lane :release do |options| gym deliver( ipa: “Mitene.ipa",

    submit_for_review: false, force: true ) upload_to_github_release( github_api_token: ENV["GITHUB_API_TOKEN"], tag_name: options[:tag_name] ) end gymͰipaੜ੒
  26. Fastfile: release lane :release do |options| gym deliver( ipa: “Mitene.ipa",

    submit_for_review: false, force: true ) upload_to_github_release( github_api_token: ENV["GITHUB_API_TOKEN"], tag_name: options[:tag_name] ) end deliverͰAppStoreConnect΁Ξοϓϩʔυ
  27. Fastfile: release lane :release do |options| gym deliver( ipa: “Mitene.ipa",

    submit_for_review: false, force: true ) upload_to_github_release( github_api_token: ENV["GITHUB_API_TOKEN"], tag_name: options[:tag_name] ) end private_laneͰGithub ReleaseΛ࡞੒
  28. ʲTipsʳGymfileʹfor_laneͰ
 lane͝ͱͷϏϧυύϥϝʔλΛ౉͢ for_lane :inhouse do ɾɾɾ end for_lane :staging do

    ɾɾɾ end for_lane :release do ɾɾɾ end ࢀߟIUUQTTQFBLFSEFDLDPNHJHJOFUYJBOHKJFGBTUpMF
  29. Workflow • fastlaneͷstepͰlaneΛ ࢦఆ͢Δ͚ͩ

  30. Trigger • masterʹpush͞Εͨͱ͖ʹinhouseͰ഑৴

  31. slack • hubotܦ༝ͰϏϧυΛ࣮ߦ

  32. Build Trigger API • slack͔Βhubotܦ༝ͰϏϧυΛ࣮ߦ hook_info="\"hook_info\":{ \"type\":\"bitrise\", \"build_trigger_token\":\"${BUILD_TRIGGER_TOKEN}\" }" build_params="\"build_params\":{

    \"branch\":\"${branch}\", \"workflow_id\":\"${workflow}\" }" json="{${hook_info},${build_params},\"triggered_by\":\"curl\"}" curl https://app.bitrise.io/app/${APP_SLUG}/build/start.json —data "${json}"
  33. None
  34. update-libraries • CarthageͷoutdatedͳϥΠϒϥϦΛΞοϓσʔτͯ͠PRΛ࡞੒
 
 
 ˞@hiragram͞ΜͷԼهࣄΛ͔ͳΓࢀߟʹ͍͖ͤͯͨͩ͞·ͨ͠ “TravisCIͰiOSͷґଘϥΠϒϥϦͷߋ৽ΛࣗಈԽ͢Δ” https://tech.speee.jp/entry/2016/12/01/100253

  35. Scheduled Build • ຖि༵ۚ೔ͷਂ໷3࣌ʹఆظ࣮ߦ

  36. PR͕ࣗಈੜ੒

  37. PR͕ࣗಈੜ੒

  38. PR͕ࣗಈੜ੒

  39. PR͕ࣗಈੜ੒

  40. Fastfile • laneΛ࡞Δ lane :update_carthage_libraries

  41. Fastfile • outdatedͳϥΠϒϥϦΛग़͢ outdateds = "" Dir.chdir ".." do outdateds

    += sh("carthage", "outdated") end
  42. Fastfile • ݱࡏͷϒϥϯνΛୀආ͠ɺϥΠϒϥϦΛਖ਼نදݱͰ̍ߦͣͭநग़ current_branch = git_branch outdateds.each_line do |line| if

    outdated = line.match( /^(.+?) "(.+?)\.(.+?)\.(.+?)" -> “(.+?)\.(.+?)\.(.+?)"/ ) then lib = outdated[1] old_major = outdated[2] old_minor = outdated[3] old_patch = outdated[4] new_major = outdated[5] new_minor = outdated[6] new_patch = outdated[7]
  43. Fastfile • όʔδϣϯ͕ҧ͏ͳΒ৽͍͠branchΛ੾ͬͯ carthage update if old_major == new_major &&

    (old_minor != new_minor || old_patch ! = new_patch) then ɹɹBRANCH_NAME = ɹɹɹ“update-library-#{lib}-"+Time.now.strftime("%Y%m%d%H%M") ɹɹsh("git", "checkout", "-b", BRANCH_NAME) ɹɹcarthage( ɹɹɹcommand: "update", ɹɹɹdependencies: [lib], ɹɹɹplatform: “ios" ɹɹ) ɹɹgit_add(path: ".") ɹɹgit_commit(path: ".", message: "Update Library #{lib}.”)
  44. Fastfile • PRͷbodyΛੜ੒ version = line.match(/"(.*?)" -> "(.*?)"/) old_version =

    version[1] new_version = version[2] diff = sh("git", "diff", "--", "../Cartfile.resolved") repo = diff.match(/-github "(.+?)"/)[1] body = "## #{repo}\n” body += "#{old_version} -> [#{new_version}] (https://github.com/#{repo}/releases/tag/#{new_version})\n" body += “[compare](https://github.com/#{repo}/compare/ #{old_version}...#{new_version})\n"
  45. Fastfile • PRΛ࡞੒͠ɺϒϥϯνΛmasterʹ໭ͯ࣍͠ͷfor_loop΁ ɹɹsh("git", "push", "origin", BRANCH_NAME) ɹɹcreate_pull_request( ɹɹɹɹapi_token: ENV["GITHUB_API_TOKEN"],

    ɹɹɹɹrepo: "maychannel-dev/iOS", ɹɹɹɹtitle: "[Auto generated] Update library #{lib}", ɹɹɹɹbody: body ɹɹ) ɹɹsh("git", "checkout", current_branch) end
  46. update_carthage_libraries • gistʹ্͛ͨͷͰΑ͚Ε͹ࢀߟʹ͍ͯͩ͘͠͞ https://gist.github.com/rockname/ d30829947f9d00bee2b8be61ab70f6db

  47. None
  48. check-release-build • production؀ڥͰຖ೔ਂ໷ʹϏϧυ͍ͯ͠Δ • `#if RELEASE` ͰམͪΔ͜ͱʹϦϦʔε௚લͰؾ͍ͮͨମݧ͔Β…

  49. None
  50. update-metadata • ৹ࠪͰϦδΣΫτ͞Εͨࡍʹɺϝλσʔλ͚ͩFastlaneͷ
 deliverͰAppStoreConnectʹΞοϓϩʔυ͍ͨ͠ͱ͖ʹ࢖༻

  51. None
  52. measure-build-time/measure-coverage • Ϗϧυ࣌ؒͱςετΧόϨοδΛຖ೔ਂ໷ʹܭଌ

  53. measure-build-time • xcodebuild -showBuildTimingSummary Ͱܭଌ͢Δ͜ͱʹ • Xcode 10 ͔Β௥Ճ͞ΕͨɺΑΓৄࡉͳϏϧυ࣌ؒܭଌํ๏

  54. Workflow

  55. Workflow touch buildtime.txt xcodebuild \ -showBuildTimingSummary \ … \ clean

    build test | grep -E \ ’.* \| .*(seconds)$' >> buildtime.txt mv buildtime.txt $BITRISE_DEPLOY_DIR
  56. Workflow

  57. measure-coverage • ΧόϨοδऩूํ๏͸৭ʑ͋Δ͕ɺstar਺ͳͲΛݟͯ
 slather ͰΧόϨοδΛऩू͢Δ͜ͱʹ • Fastlaneͷlane΋͋Δ

  58. Gemfile + gem 'slather'

  59. Scanfile scheme("Mitene") workspace(“Mitene.xcworkspace") device("iPhone 6s Plus") clean(true) code_coverage(true) skip_slack(true) configuration("Testing")

  60. Scanfile scheme("Mitene") workspace(“Mitene.xcworkspace") device("iPhone 6s Plus") clean(true) code_coverage(true) skip_slack(true) configuration("Testing")

    CodeCoverageΛON
  61. Fastfile lane :coverage do |options| scan slather( proj: "Mitene.xcodeproj", workspace:

    "Mitene.xcworkspace", scheme: “Mitene", configuration: "Testing", html: true, output_directory: ENV["BITRISE_DEPLOY_DIR"], ignore: ["Carthage/Checkouts/*", "Pods/*"] ) end
  62. Fastfile lane :coverage do |options| scan slather( proj: "Mitene.xcodeproj", workspace:

    "Mitene.xcworkspace", scheme: “Mitene", configuration: "Testing", html: true, output_directory: ENV["BITRISE_DEPLOY_DIR"], ignore: ["Carthage/Checkouts/*", "Pods/*"] ) end ґଘϥΠϒϥϦΛignoreʹࢦఆ
  63. Artifact

  64. Artifact CSS͕ޮ͍ͨView͸ݟΕͳ͍

  65. Feature Request IUUQTEJTDVTTCJUSJTFJPUBSUJGBDUTNBLFJUQPTTJCMFUPWJFXIUNMpMFT XJUISFMBUJWFMJOLTDTTFUDSFGT

  66. None
  67. test • PRʹίϛοτ͞ΕΔ౓ʹtest͕૸Δ

  68. Pull Request • Dangerܦ༝Ͱswiftlintͷϝοηʔδ΍Coverage͕දࣔ͞ΕΔ

  69. Fastfile lane :test do |options| scan danger end

  70. Gemfile + gem 'danger-slather'

  71. Dangerfile slather.configure( "Mitene.xcodeproj", "Mitene", options: { workspace: “Mitene.xcworkspace", configuration: "Testing",

    ignore_list: ["Carthage/Checkouts/*", "Pods/*"] } ) slather.show_coverage
  72. ʲTipsʳAuto-cancel builds • طʹbuild͕૸͍ͬͯΔ৔߹͸cancelͯ͘͠ΕΔ IUUQTCMPHCJUSJTFJPBVUPDBODFMCVJMETBOELFFQSPMMJOH

  73. ·ͱΊ • BitriseΛಋೖͨ͜͠ͱͰ • macminiͷ໘౗ΛݟΔඞཁ͕ͳ͘ͳͬͨ • ϦϞʔτ͔ΒؾܰʹϏϧυ݁ՌΛݟΕΔΑ͏ʹͳͬͨ • GUIͰΆͪΆͪ࡞ΕΔͷͰWorkflow͕ͲΜͲΜ૿͑ɺ
 ॳݟʹ΋༏͍͠

    • όʔδϣϯ؅ཧ͍ͨ͠ϑϩʔ͸fastlaneʹدͤΔͱྑͦ͞͏ • ͱ͸͍͑ɺFastflieͷଐਓԽʹ͸ཁ஫ҙ
  74. Thank you!!!