Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

rockname
February 21, 2019

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

rockname

February 21, 2019
Tweet

More Decks by rockname

Other Decks in Programming

Transcript

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

    View Slide

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

    iOS(Swift, objc) > Android(Kotlin, Java) == Rails(ruby)
    ࣗݾ঺հ

    View Slide

  3. ΞδΣϯμ
    ʲ࿩͢͜ͱʳ
    • Bitriseಋೖ·ͰͷྲྀΕ
    • BitriseͷWorkflowͷ૊Έํɾछྨ
    • fastlaneʹ͓͚Δlaneͷ૊Έํɾछྨ
    ʲ࿩͞ͳ͍͜ͱʳ
    • Bitriseͷૢ࡞ํ๏
    • fastlaneͷ࢖༻ํ๏

    View Slide

  4. BitriseಋೖҎલͷCI/CDࣄ৘
    • ࣾ಺macminiͰJenkinsͱhubot͕ಈ͍͍ͯͨ

    View Slide

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

    View Slide

  6. hubot
    • Slack͔ΒίϚϯυΛड͚͚ͭͯDeployGate,

    AppStore΁഑৴
    Building
    Request
    Deploy

    View Slide

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

    View Slide

  8. Jenkins͔ΒBitriseʹ…ʂ!!

    View Slide

  9. Bitriseͱ͸ʁ
    • ϞόΠϧʹಛԽͨ͠CI/CDͷαʔϏε
    • ରԠ: iOS/Android/Xamarin/

    ReactNative/Flutter…
    • GUIʹΑΔࢹ֮తͳૢ࡞Ͱ

    WorkflowΛ؆୯ʹ૊ΊΔ

    View Slide

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

    View Slide

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

    View Slide

  12. ݱঢ়ͷmacminiͰͷϏϧυ
    • Test
    • JenkinsͰPR΁ͷίϛοτΛϑοΫ
    • deploygate΁ͷ഑৴
    • inhouse
    • ࣾ಺഑෍༻ (Enterprise)
    • staging
    • QA༻
    • AppStore΁ͷΞοϓϩʔυ(fastlane/deliver)

    View Slide

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

    View Slide

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

    View Slide

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

    Ξοϓϩʔυ
    • scan: ςετͷ࣮ߦ
    • …

    View Slide

  16. ํ਑
    • Step1: fastlaneʹεΫϦϓτΛدͤΔ
    • Step2: Bitriseʹ׬શҠߦ
    => Ұؾʹmacmini͔ΒҠߦ͠ͳͯ͘ࡁΉ͔Β҆৺
    => git؅ཧ͍ͨ͠Ϩγϐ͸fastlaneͰ

    View Slide

  17. 2018೥12݄த०ʹ
    ແࣄʹҠߦ׬ྃ

    View Slide

  18. View Slide

  19. View Slide

  20. inhouse/staging/release
    • inhouse:
    • deploygate΁഑৴
    • ࣾ಺഑෍༻ (Enterprise)
    • staging:
    • Deploygate΁഑৴
    • QA༻
    • release
    • AppStoreConnect΁Ξοϓϩʔυ

    View Slide

  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

    View Slide

  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ੜ੒

    View Slide

  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΁഑৴

    View Slide

  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

    View Slide

  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ੜ੒

    View Slide

  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΁Ξοϓϩʔυ

    View Slide

  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Λ࡞੒

    View Slide

  28. ʲTipsʳGymfileʹfor_laneͰ

    lane͝ͱͷϏϧυύϥϝʔλΛ౉͢
    for_lane :inhouse do
    ɾɾɾ
    end
    for_lane :staging do
    ɾɾɾ
    end
    for_lane :release do
    ɾɾɾ
    end
    ࢀߟIUUQTTQFBLFSEFDLDPNHJHJOFUYJBOHKJFGBTUpMF

    View Slide

  29. Workflow
    • fastlaneͷstepͰlaneΛ
    ࢦఆ͢Δ͚ͩ

    View Slide

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

    View Slide

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

    View Slide

  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}"

    View Slide

  33. View Slide

  34. update-libraries
    • CarthageͷoutdatedͳϥΠϒϥϦΛΞοϓσʔτͯ͠PRΛ࡞੒



    ˞@hiragram͞ΜͷԼهࣄΛ͔ͳΓࢀߟʹ͍͖ͤͯͨͩ͞·ͨ͠
    “TravisCIͰiOSͷґଘϥΠϒϥϦͷߋ৽ΛࣗಈԽ͢Δ”
    https://tech.speee.jp/entry/2016/12/01/100253

    View Slide

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

    View Slide

  36. PR͕ࣗಈੜ੒

    View Slide

  37. PR͕ࣗಈੜ੒

    View Slide

  38. PR͕ࣗಈੜ੒

    View Slide

  39. PR͕ࣗಈੜ੒

    View Slide

  40. Fastfile
    • laneΛ࡞Δ
    lane :update_carthage_libraries

    View Slide

  41. Fastfile
    • outdatedͳϥΠϒϥϦΛग़͢
    outdateds = ""
    Dir.chdir ".." do
    outdateds += sh("carthage", "outdated")
    end

    View Slide

  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]

    View Slide

  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}.”)

    View Slide

  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"

    View Slide

  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

    View Slide

  46. update_carthage_libraries
    • gistʹ্͛ͨͷͰΑ͚Ε͹ࢀߟʹ͍ͯͩ͘͠͞
    https://gist.github.com/rockname/
    d30829947f9d00bee2b8be61ab70f6db

    View Slide

  47. View Slide

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

    View Slide

  49. View Slide

  50. update-metadata
    • ৹ࠪͰϦδΣΫτ͞Εͨࡍʹɺϝλσʔλ͚ͩFastlaneͷ

    deliverͰAppStoreConnectʹΞοϓϩʔυ͍ͨ͠ͱ͖ʹ࢖༻

    View Slide

  51. View Slide

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

    View Slide

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

    View Slide

  54. Workflow

    View Slide

  55. Workflow
    touch buildtime.txt
    xcodebuild \
    -showBuildTimingSummary \
    … \
    clean build test | grep -E \
    ’.* \| .*(seconds)$' >> buildtime.txt
    mv buildtime.txt $BITRISE_DEPLOY_DIR

    View Slide

  56. Workflow

    View Slide

  57. measure-coverage
    • ΧόϨοδऩूํ๏͸৭ʑ͋Δ͕ɺstar਺ͳͲΛݟͯ

    slather ͰΧόϨοδΛऩू͢Δ͜ͱʹ
    • Fastlaneͷlane΋͋Δ

    View Slide

  58. Gemfile
    + gem 'slather'

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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ʹࢦఆ

    View Slide

  63. Artifact

    View Slide

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

    View Slide

  65. Feature Request
    IUUQTEJTDVTTCJUSJTFJPUBSUJGBDUTNBLFJUQPTTJCMFUPWJFXIUNMpMFT
    XJUISFMBUJWFMJOLTDTTFUDSFGT

    View Slide

  66. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  70. Gemfile
    + gem 'danger-slather'

    View Slide

  71. Dangerfile
    slather.configure(
    "Mitene.xcodeproj",
    "Mitene",
    options: {
    workspace: “Mitene.xcworkspace",
    configuration: "Testing",
    ignore_list: ["Carthage/Checkouts/*", "Pods/*"]
    }
    )
    slather.show_coverage

    View Slide

  72. ʲTipsʳAuto-cancel builds
    • طʹbuild͕૸͍ͬͯΔ৔߹͸cancelͯ͘͠ΕΔ
    IUUQTCMPHCJUSJTFJPBVUPDBODFMCVJMETBOELFFQSPMMJOH

    View Slide

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

    ॳݟʹ΋༏͍͠
    • όʔδϣϯ؅ཧ͍ͨ͠ϑϩʔ͸fastlaneʹدͤΔͱྑͦ͞͏
    • ͱ͸͍͑ɺFastflieͷଐਓԽʹ͸ཁ஫ҙ

    View Slide

  74. Thank you!!!

    View Slide