Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

ϩΫωϜ@rockname • גࣜձࣾϛΫγΟ 2018೥৽ଔೖࣾ • Ո଒ΞϧόϜΈͯͶ • ΞϓϦ։ൃάϧʔϓ 
 iOS(Swift, objc) > Android(Kotlin, Java) == Rails(ruby) ࣗݾ঺հ

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Jenkins͔ΒBitriseʹ…ʂ!!

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Ͳ͏΍ͬͯҠߦ͍ͯ͘͠

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

update-libraries • CarthageͷoutdatedͳϥΠϒϥϦΛΞοϓσʔτͯ͠PRΛ࡞੒
 
 
 ˞@hiragram͞ΜͷԼهࣄΛ͔ͳΓࢀߟʹ͍͖ͤͯͨͩ͞·ͨ͠ “TravisCIͰiOSͷґଘϥΠϒϥϦͷߋ৽ΛࣗಈԽ͢Δ” https://tech.speee.jp/entry/2016/12/01/100253

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

PR͕ࣗಈੜ੒

Slide 37

Slide 37 text

PR͕ࣗಈੜ੒

Slide 38

Slide 38 text

PR͕ࣗಈੜ੒

Slide 39

Slide 39 text

PR͕ࣗಈੜ੒

Slide 40

Slide 40 text

Fastfile • laneΛ࡞Δ lane :update_carthage_libraries

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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]

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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"

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Workflow

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

Workflow

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

Gemfile + gem 'slather'

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

Artifact

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Feature Request IUUQTEJTDVTTCJUSJTFJPUBSUJGBDUTNBLFJUQPTTJCMFUPWJFXIUNMpMFT XJUISFMBUJWFMJOLTDTTFUDSFGT

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

Fastfile lane :test do |options| scan danger end

Slide 70

Slide 70 text

Gemfile + gem 'danger-slather'

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

·ͱΊ • BitriseΛಋೖͨ͜͠ͱͰ • macminiͷ໘౗ΛݟΔඞཁ͕ͳ͘ͳͬͨ • ϦϞʔτ͔ΒؾܰʹϏϧυ݁ՌΛݟΕΔΑ͏ʹͳͬͨ • GUIͰΆͪΆͪ࡞ΕΔͷͰWorkflow͕ͲΜͲΜ૿͑ɺ
 ॳݟʹ΋༏͍͠ • όʔδϣϯ؅ཧ͍ͨ͠ϑϩʔ͸fastlaneʹدͤΔͱྑͦ͞͏ • ͱ͸͍͑ɺFastflieͷଐਓԽʹ͸ཁ஫ҙ

Slide 74

Slide 74 text

Thank you!!!