Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
fastlaneとBitriseで構築するiOSのCI/CDレシピ / iOS CI/CD R...
Search
rockname
February 21, 2019
Programming
3.6k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
fastlaneとBitriseで構築するiOSのCI/CDレシピ / iOS CI/CD Recipe Constructed with fastlane and Bitrise
rockname
February 21, 2019
More Decks by rockname
See All by rockname
AIを活用したレシート読み取り機能の開発から得られた実践知 / AI Receipt Scan Practice
rockname
2
4.7k
Unlock the Potential of Swift Code Generation
rockname
0
570
生成AIを活用したレシート読み取り機能のアプリ開発の裏側 / AI Receipt Scan App Development
rockname
0
140
ゼロから理解するDependency Injection / Understanding Dependency Injection from the Ground Up
rockname
2
4.4k
サブスクリプション機能制御の設計における勘所
rockname
0
1.3k
Anatomy of Dynamic color
rockname
1
1.3k
キャッシュによる状態管理のアーキテクチャ / Cache-based state management architecture
rockname
10
21k
Optimistic Updatesで UXを向上させる / Improve UX with Optimistic Updates
rockname
2
1.6k
モバイルアプリのリストUIにおける 理想的なState表示について / The ideal state display in a mobile app list UI
rockname
6
2.2k
Other Decks in Programming
See All in Programming
New "Type" system on PicoRuby
pocke
1
980
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
14
5.6k
スマートグラスで並列バイブコーディング
hyshu
0
200
Inside Stream API
skrb
1
740
フロントエンドとバックエンドで「1文字」を揃えよう
youkidearitai
PRO
0
710
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
170
Lemonade + Foundry Toolkit でお手軽アプリ開発
seosoft
1
360
Javaの型とAI時代に型が大事な理由 / java types and type in AI era
kishida
2
140
ふつうのFeature Flag実践入門
irof
8
4.1k
ECSアプリログをFireLensでコスト削減しようとしたけど諦めた話 in Fargate×Node.js
akihisaikeda
2
4.2k
セキュリティの専門家じゃなくてもできる。「セキュリティ意識」をアップデートして サプライチェーン攻撃への耐性を高めよう。
tk3fftk
5
890
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4.3k
Featured
See All Featured
Reality Check: Gamification 10 Years Later
codingconduct
0
2.2k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.7k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
We Have a Design System, Now What?
morganepeng
55
8.2k
Deep Space Network (abreviated)
tonyrice
0
210
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
170
Into the Great Unknown - MozCon
thekraken
41
2.6k
Designing for Timeless Needs
cassininazir
1
260
Writing Fast Ruby
sferik
630
63k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.5k
Test your architecture with Archunit
thirion
1
2.3k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
200
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!!!