Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
fastlaneとBitriseで構築するiOSのCI/CDレシピ / iOS CI/CD Recipe Constructed with fastlane and Bitrise
rockname
February 21, 2019
Programming
3
1.7k
fastlaneとBitriseで構築するiOSのCI/CDレシピ / iOS CI/CD Recipe Constructed with fastlane and Bitrise
rockname
February 21, 2019
Tweet
Share
More Decks by rockname
See All by rockname
rockname
0
500
rockname
11
2.9k
rockname
11
1.6k
rockname
3
1.5k
rockname
2
580
rockname
0
1.1k
rockname
3
1k
rockname
11
5.1k
rockname
2
1k
Other Decks in Programming
See All in Programming
numeroanddev
1
240
kilometer
2
310
gernotstarke
0
390
afilina
PRO
0
150
boriswilhelms
0
160
tetsukick
0
180
rarous
0
170
martysuzuki
1
560
line_developers_tw2
0
4.8k
yoshinoriiiii
0
110
kawaji_scratch
0
110
grapecity_dev
0
180
Featured
See All Featured
notwaldorf
16
1.8k
jonyablonski
19
1.2k
addyosmani
1346
190k
denniskardys
219
120k
rasmusluckow
318
18k
destraynor
222
47k
philhawksworth
190
17k
smashingmag
283
47k
michaelherold
225
8.5k
holman
288
130k
carmenhchung
31
1.5k
reverentgeek
167
7.3k
Transcript
fastlaneͱBitriseͰߏங͢Δ iOSͷCI/CDϨγϐ CI/CD Test Night #3 2019/02/21 גࣜձࣾϛΫγΟ ΈͯͶࣄۀ෦ ϩΫωϜ@rockname
ϩΫωϜ@rockname • גࣜձࣾϛΫγΟ 2018৽ଔೖࣾ • ՈΞϧόϜΈͯͶ • ΞϓϦ։ൃάϧʔϓ iOS(Swift,
objc) > Android(Kotlin, Java) == Rails(ruby) ࣗݾհ
ΞδΣϯμ ʲ͢͜ͱʳ • Bitriseಋೖ·ͰͷྲྀΕ • BitriseͷWorkflowͷΈํɾछྨ • fastlaneʹ͓͚ΔlaneͷΈํɾछྨ ʲ͞ͳ͍͜ͱʳ •
Bitriseͷૢ࡞ํ๏ • fastlaneͷ༻ํ๏
BitriseಋೖҎલͷCI/CDࣄ • ࣾmacminiͰJenkinsͱhubot͕ಈ͍͍ͯͨ
Jenkins • GitHubͷWebhookͰPRʹίϛοτ͕ੵ·ΕΔͨͼʹςετ ͕Δ PR commits Test result Testing
hubot • Slack͔ΒίϚϯυΛड͚͚ͭͯDeployGate, AppStore৴ Building Request Deploy
ਏ͔ͬͨ͜ͱ • macminiΛ୭͔͕ϝϯςͯ͋͛͠Δඞཁ͕͋Δ • Xcode10ରԠͷࡍ͜͜ͷลΓͰ͍ͩͿർฐͨ͠ • ࣾmacminiࣾ֎͔ΒͷΞΫηεΛ͍͍ͯΔͷͰɺϦϞʔτ ͰCIͷ݁ՌΛݟΕͳ͍ (VPNΛܨ͛ݟΕΔ͕…)
Jenkins͔ΒBitriseʹ…ʂ!!
Bitriseͱʁ • ϞόΠϧʹಛԽͨ͠CI/CDͷαʔϏε • ରԠ: iOS/Android/Xamarin/ ReactNative/Flutter… • GUIʹΑΔࢹ֮తͳૢ࡞Ͱ WorkflowΛ؆୯ʹΊΔ
ͳͥBitriseʹͨ͠ͷʁ • ଞͷΫϥυCIαʔϏεͱൺͯ֨ஈʹ͍҆ • ৽όʔδϣϯͷXcodeରԠ͕ૣ͍ • νʔϜʹBitriseϢʔβʔ͕͢ͰʹԿ໊͔͍ͨ • ྲྀߦ͍ͬͯͨ
Ͳ͏ͬͯҠߦ͍ͯ͘͠
ݱঢ়ͷmacminiͰͷϏϧυ • Test • JenkinsͰPRͷίϛοτΛϑοΫ • deploygateͷ৴ • inhouse •
ࣾ༻ (Enterprise) • staging • QA༻ • AppStoreͷΞοϓϩʔυ(fastlane/deliver)
ݒ೦ • ҰؾʹBitriseʹҾͬӽ͢ͷා͍ • ϏϧυεΫϦϓτ͕187ߦ͋Δ • BitriseͰ࡞ΔWorkflowͷόʔδϣϯཧ͕Ͱ͖ͳ͍ • (Ͱ͖Δ͕GUIͰࢹೝͰ͖ͳ͘ͳΔ)
ݒ೦ • ҰؾʹBitriseʹҾͬӽ͢ͷා͍ • ϏϧυεΫϦϓτ͕187ߦ͋Δ • BitriseͰ࡞ΔWorkflowͷόʔδϣϯཧ͕Ͱ͖ͳ͍ • (Ͱ͖Δ͕GUIͰࢹೝͰ͖ͳ͘ͳΔ)
fastlaneͱʁ • ओʹiOS/AndroidΞϓϦͷϏϧυ, ςετ, σϓϩΠΛߦ͏ͨΊͷRubyλε Ϋϥϯφʔ • iOSͰ • gym:
ΞϓϦͷϏϧυ • deliver: AppStoreConnectͷ Ξοϓϩʔυ • scan: ςετͷ࣮ߦ • …
ํ • Step1: fastlaneʹεΫϦϓτΛدͤΔ • Step2: BitriseʹશҠߦ => Ұؾʹmacmini͔ΒҠߦ͠ͳͯ͘ࡁΉ͔Β҆৺ =>
gitཧ͍ͨ͠ϨγϐfastlaneͰ
201812݄த०ʹ ແࣄʹҠߦྃ
None
None
inhouse/staging/release • inhouse: • deploygate৴ • ࣾ༻ (Enterprise) • staging:
• Deploygate৴ • QA༻ • release • AppStoreConnectΞοϓϩʔυ
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
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ੜ
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৴
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
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ੜ
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Ξοϓϩʔυ
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Λ࡞
ʲTipsʳGymfileʹfor_laneͰ lane͝ͱͷϏϧυύϥϝʔλΛ͢ for_lane :inhouse do ɾɾɾ end for_lane :staging do
ɾɾɾ end for_lane :release do ɾɾɾ end ࢀߟIUUQTTQFBLFSEFDLDPNHJHJOFUYJBOHKJFGBTUpMF
Workflow • fastlaneͷstepͰlaneΛ ࢦఆ͢Δ͚ͩ
Trigger • masterʹpush͞Εͨͱ͖ʹinhouseͰ৴
slack • hubotܦ༝ͰϏϧυΛ࣮ߦ
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}"
None
update-libraries • CarthageͷoutdatedͳϥΠϒϥϦΛΞοϓσʔτͯ͠PRΛ࡞ ˞@hiragram͞ΜͷԼهࣄΛ͔ͳΓࢀߟʹ͍͖ͤͯͨͩ͞·ͨ͠ “TravisCIͰiOSͷґଘϥΠϒϥϦͷߋ৽ΛࣗಈԽ͢Δ” https://tech.speee.jp/entry/2016/12/01/100253
Scheduled Build • ຖि༵ۚͷਂ3࣌ʹఆظ࣮ߦ
PR͕ࣗಈੜ
PR͕ࣗಈੜ
PR͕ࣗಈੜ
PR͕ࣗಈੜ
Fastfile • laneΛ࡞Δ lane :update_carthage_libraries
Fastfile • outdatedͳϥΠϒϥϦΛग़͢ outdateds = "" Dir.chdir ".." do outdateds
+= sh("carthage", "outdated") end
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]
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}.”)
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"
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
update_carthage_libraries • gistʹ্͛ͨͷͰΑ͚Εࢀߟʹ͍ͯͩ͘͠͞ https://gist.github.com/rockname/ d30829947f9d00bee2b8be61ab70f6db
None
check-release-build • productionڥͰຖਂʹϏϧυ͍ͯ͠Δ • `#if RELEASE` ͰམͪΔ͜ͱʹϦϦʔεલͰؾ͍ͮͨମݧ͔Β…
None
update-metadata • ৹ࠪͰϦδΣΫτ͞Εͨࡍʹɺϝλσʔλ͚ͩFastlaneͷ deliverͰAppStoreConnectʹΞοϓϩʔυ͍ͨ͠ͱ͖ʹ༻
None
measure-build-time/measure-coverage • Ϗϧυ࣌ؒͱςετΧόϨοδΛຖਂʹܭଌ
measure-build-time • xcodebuild -showBuildTimingSummary Ͱܭଌ͢Δ͜ͱʹ • Xcode 10 ͔ΒՃ͞ΕͨɺΑΓৄࡉͳϏϧυ࣌ؒܭଌํ๏
Workflow
Workflow touch buildtime.txt xcodebuild \ -showBuildTimingSummary \ … \ clean
build test | grep -E \ ’.* \| .*(seconds)$' >> buildtime.txt mv buildtime.txt $BITRISE_DEPLOY_DIR
Workflow
measure-coverage • ΧόϨοδऩूํ๏৭ʑ͋Δ͕ɺstarͳͲΛݟͯ slather ͰΧόϨοδΛऩू͢Δ͜ͱʹ • Fastlaneͷlane͋Δ
Gemfile + gem 'slather'
Scanfile scheme("Mitene") workspace(“Mitene.xcworkspace") device("iPhone 6s Plus") clean(true) code_coverage(true) skip_slack(true) configuration("Testing")
Scanfile scheme("Mitene") workspace(“Mitene.xcworkspace") device("iPhone 6s Plus") clean(true) code_coverage(true) skip_slack(true) configuration("Testing")
CodeCoverageΛON
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
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ʹࢦఆ
Artifact
Artifact CSS͕ޮ͍ͨViewݟΕͳ͍
Feature Request IUUQTEJTDVTTCJUSJTFJPUBSUJGBDUTNBLFJUQPTTJCMFUPWJFXIUNMpMFT XJUISFMBUJWFMJOLTDTTFUDSFGT
None
test • PRʹίϛοτ͞ΕΔʹtest͕Δ
Pull Request • Dangerܦ༝ͰswiftlintͷϝοηʔδCoverage͕දࣔ͞ΕΔ
Fastfile lane :test do |options| scan danger end
Gemfile + gem 'danger-slather'
Dangerfile slather.configure( "Mitene.xcodeproj", "Mitene", options: { workspace: “Mitene.xcworkspace", configuration: "Testing",
ignore_list: ["Carthage/Checkouts/*", "Pods/*"] } ) slather.show_coverage
ʲTipsʳAuto-cancel builds • طʹbuild͕͍ͬͯΔ߹cancelͯ͘͠ΕΔ IUUQTCMPHCJUSJTFJPBVUPDBODFMCVJMETBOELFFQSPMMJOH
·ͱΊ • BitriseΛಋೖͨ͜͠ͱͰ • macminiͷ໘ΛݟΔඞཁ͕ͳ͘ͳͬͨ • ϦϞʔτ͔ΒؾܰʹϏϧυ݁ՌΛݟΕΔΑ͏ʹͳͬͨ • GUIͰΆͪΆͪ࡞ΕΔͷͰWorkflow͕ͲΜͲΜ૿͑ɺ ॳݟʹ༏͍͠
• όʔδϣϯཧ͍ͨ͠ϑϩʔfastlaneʹدͤΔͱྑͦ͞͏ • ͱ͍͑ɺFastflieͷଐਓԽʹཁҙ
Thank you!!!