Slide 1

Slide 1 text

laprasDrum (Β΀) note inc. ʮͦΕɺࣗಈԽͰ͖·͢Αʯ note Λࢧ͑ΔϫʔΫϑϩʔେશ iOSDC JAPAN 2020

Slide 2

Slide 2 text

2018/5 2019 2020/5 1ਓ 2ਓ 3ਓ ೖࣾ 2ਓೖࣾ

Slide 3

Slide 3 text

2018/5 2019 2020/5 1ਓ 2ਓ 3ਓ ೖࣾ 2ਓೖࣾ ࣗ෼Λॿ͚ΔͨΊ ະདྷͷಉ྅Λॿ͚ΔͨΊ

Slide 4

Slide 4 text

iOS ։ൃʹ͓͚ΔࣗಈԽʁ

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

2ͭͷϧʔϧʹ΋ͱ͍ͮͯ શ෦ࣗಈԽ͠·ͨ͠

Slide 14

Slide 14 text

1. ΞΫγϣϯτϦΨʔ͕ ػցతʹݕ஌ՄೳͰ͋Δ͔

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

1. ΞΫγϣϯτϦΨʔ͕ ػցతʹݕ஌Մೳ Ͱ͋Δ͔ ʹͰ͖Δ͔ {

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

2. ࣗಈԽͰ׬݁͢ΔͨΊͷ ౔୆͕Ͱ͖͍ͯΔ͔

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

ϝʔϧˠ୺຤ొ࿥ͱ഑৴ ʹ৚݅Ϛον͢Δϝʔϧ͕དྷͨΒ Ͱߋ৽ɾ഑৴

Slide 23

Slide 23 text

desc "[CI] add new device UDID. It requires options: name, udid" lane :add_new_device do |options| if (options[:name].nil? or options[:name].empty?) or (options[:udid].nil? or options[:udid].empty?) UI.user_error!("missing options! call with name:{device name} udid:{device udid}") end UI.important("registering #{options[:name]} (#{options[:udid]})") register_device( name: options[:name], udid: options[:udid] ) # ূ໌ॻߋ৽ match( app_identifier: ['com.myapp.viewer'], # ొ࿥͍ͨ͠ identifier ͢΂ͯೖΕΔ type: 'adhoc', # ඞཁͳΒ development ΋ force_for_new_devices: true ) # Ϗϧυ gym(...) # ഑৴ firebase_app_distribution(...) end fastlane add_new_device udid:xxx name:yyy

Slide 24

Slide 24 text

import json # Zapier Ͱઃఆͨ͠؀ڥม਺͸ input_data ͰऔಘՄೳ headers = { 'Accept': 'application/json', 'Content-Type': 'application/json' } auth = (input_data['circleci_token'], '') response = requests.post( "https://circleci.com/api/v2/project/gh/org/repo/pipeline", data=json.dumps({ "branch": "master", "parameters": { "run_device_registration_from_ci": True, "device_name": input_data['device_name'], "device_udid": input_data['device_udid'] }}), headers = headers, auth = auth) response.raise_for_status() output = response.json() parameters: run_device_registration_from_ci: type: boolean default: false workflows: version: 2.1 # --- API ܦ༝ͷΈ # fastlane register_device Λ࣮ߦ͢Δ add_new_device_from_ci: when: << pipeline.parameters.run_device_registration_from_ci >> steps: - checkout - run: *build - run: bundle exec fastlane add_new_device name:<< pipeline.parameters.device_name >> udid:<< pipeline.parameters.device_udid >> config.yml CI ্Ͱ fastlane ࣮ߦ

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

1. ΞΫγϣϯτϦΨʔΛػցతʹݕ஌ՄೳͰ͋Δ͔ 2. ࣗಈԽͰ׬݁͢ΔͨΊͷ౔୆͕Ͱ͖͍ͯΔ͔

Slide 27

Slide 27 text

ೖྗτϦΨʔ remote macOS server

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

ϦΞΫγϣϯˠissue΁ ͷൃݴʹ ͚ͭͨΒ issue ࡞Δ͔୳͢

Slide 30

Slide 30 text

sample-channel

Slide 31

Slide 31 text

ϏϧυΛ΋ͬͱ଎͍ͨ͘͠ ·ͣ͸ܭଌ͢Δ - צॴA - צॴB ϏϧυΛ΋ͬͱ଎͍ͨ͘͠ ·ͣ͸ܭଌ͢Δ - צॴA - צॴB ϏϧυΛ΋ͬͱ଎͍ͨ͘͠

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

Slack ͷ౤ߘϦϯΫͱ ౤ߘऀ ຊจશ෦ description ʹ͍Ε͓ͯ͘

Slide 35

Slide 35 text

3. Ͱݟ͔ͭΒͳ͔ͬͨ = ৽ن࡞੒ͨ͠৔߹

Slide 36

Slide 36 text

3. Ͱݟ͔ͭͬͨ৔߹

Slide 37

Slide 37 text

ϦΞΫγϣϯˠissue΁

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

/command→CircleCI ͷ /command Webhook ΛΩϟονͨ͠Β Λ࣮ߦ͢Δ Ҿ਺ͷspace࡟আ Ҿ਺ check CircleCI API ௨஌

Slide 41

Slide 41 text

/command→CircleCI Ҿ਺ͷspace࡟আ Ҿ਺ check CircleCI API ௨஌

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

/command→CircleCI ver. x.y.z Λ੾Γग़͢

Slide 44

Slide 44 text

/command→CircleCI 3.1.0

Slide 45

Slide 45 text

release branch ࡞੒ import json import base64 headers = { "Content-type": "application/json", "Authorization": f"token {input_data['github_token']}" } # master branch HEAD ͷ SHA Λऔಘ master_ref_url = "https://api.github.com/repos/org/repo/git/ref/heads/master" master_ref_response = requests.get(master_ref_url, headers=headers) master_ref_response.raise_for_status() master_ref_json = master_ref_response.json() master_sha = master_ref_json['object']['sha'] # release/x.y.z branch Λ࡞੒ release_branch_url = "https://api.github.com/repos/org/repo/git/refs" payload = { "ref": f"refs/heads/{input_data['branch']}", # branch = 'x.y.z' "sha": master_sha } release_branch_response = requests.post( release_branch_url, data=json.dumps(payload), headers=headers ) release_branch_response.raise_for_status() output = release_branch_response.json()

Slide 46

Slide 46 text

CircleCI ্Ͱ࣮ߦ workflows: version: 2.1 auto_update_commit: when: << pipeline.parameters.run_auto_update_commit >> jobs: - "Commit and Push version update": filters: branches: only: /^release\/.+$ jobs: "Commit and Push version update": steps: - run: *checkout_and_build - run: name: "commit & push version update to ${NEXT_VERSION}" command: | NEXT_VERSION=<< pipeline.parameters.next_version >> git reset --hard HEAD git config user.email "[email protected]" git config user.name "your auto committer" bundle exec fastlane update_version to:${NEXT_VERSION} git add . git commit -m "[ci skip] [auto commit] update to ${NEXT_VERSION}" git push origin release/${NEXT_VERSION} - when: condition: << pipeline.parameters.run_integration_tests >> steps: *upload_latest_note_to_magic_pod release branch ͷΈ࣮ߦ fastlane update_version Λ࣮ߦͯ͠ commit & push

Slide 47

Slide 47 text

fastlane update_version

Slide 48

Slide 48 text

fastlane update_version desc "[CI] update version & deploy adhoc apps" lane :update_version do |options| increment_version_number(version_number: options[:to].to_s) generate_changelog match(...) gym(...) upload_to_firebase(...) end github-changelog-generator Ͱ 3.0.0 tag ~ HEAD ؒͷ diff Λऔͬͯ ReleaseNote Λ࡞੒

Slide 49

Slide 49 text

/command→CircleCI

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

ϝʔϧˠSlack΁ ʹϚον͢Δϝʔϧ͕དྷͨΒ ੔ܗޙ emoji ͱจݴ੔ܗ ΁ྲྀ͢

Slide 52

Slide 52 text

ϦϦʔεϝʔϧˠmerge

Slide 53

Slide 53 text

release tag ࡞੒ # generator Ͱ࡞੒ͨ͠ ReleaseNote ͷຊจΛऔಘ branch = f"release/{input_data['version']}" release_note_url = f"https://api.github.com/repos/org/repo/contents/LatestReleaseCheckIssue.md?ref={branch}" body_response = requests.get(release_note_url, headers=headers) body_response.raise_for_status() body_json = body_response.json() decoded_body = base64.b64decode(body_json['content']).decode('utf-8') # version tag ࡞੒ release_tag_url = "https://api.github.com/repos/org/repo/releases" payload = { 'tag_name': input_data['version'], 'target_commitish': branch, 'name': input_data['version'], 'body': decoded_body, 'draft': False, 'prereleases': False } released_response = requests.post(release_tag_url, data=json.dumps(payload), headers=headers) released_response.raise_for_status() released_json = released_response.json()

Slide 54

Slide 54 text

merge # master ʹ޲͚ͯ PR ࡞੒ pr_url = "https://api.github.com/repos/org/repo/pulls" payload = { 'title': f"[Auto Create & Merge] {input_data['version']}", 'head': branch, 'base': 'master', 'body': 'created by zapier' } pr_response = requests.post(pr_url, data=json.dumps(payload), headers=headers) pr_response.raise_for_status() pr_json = pr_response.json() pr_number = str(pr_json['number']) pr_link = f"https://github.com/org/repo/pull/{pr_number}" # merge merge_url = f"https://api.github.com/repos/org/repo/pulls/{pr_number}/merge" merge_response = requests.put(merge_url, headers=headers) result = merge_response.json() # Slack ʹ౤ߘ͢ΔϝοηʔδΛ࡞੒ʢauto merge ੒ޭ or ࣦഊʣ if 'merged' in result and result['merged'] == True: return { 'message': f"{input_data['version']} ϦϦʔεʂmaster merge ͨ͠Α {pr_link}" } else: return { 'message': f"{input_data['version']} ϦϦʔεʂ͚ͩͲ master merge Ͱ͖ͳ͔ͬͨΑ {pr_link}" }

Slide 55

Slide 55 text

ϦϦʔεϝʔϧˠmerge

Slide 56

Slide 56 text

ϦϦʔεϝʔϧˠdSYM

Slide 57

Slide 57 text

ϦϦʔεϝʔϧˠdSYM lane :upload_dsym do |options| version = options[:version] || get_info_plist_value(path: "path/to/info.plist", key: "CFBundleShortVersionString") app_identifier = options[:app_identifier] || 'com.you.app' # ֘౰ ver. ͷ dSYM Λμ΢ϯϩʔυ sh 'rm -rf ../output/dsyms' sh 'mkdir -p ../output/dsyms' download_dsyms( version: version, app_identifier: app_identifier, output_directory: 'output/dsyms' ) Dir.chdir("../") do # FirebaseCrashlytics/upload-symbols ͰΞοϓϩʔυ͢ΔεΫϦϓτΛ࣮ߦ sh 'realpath output/dsyms/*.zip ɹɹɹɹɹ | xargs -I{} ./scripts/upload_dsyms.sh {} ./note/GoogleService-Info-Prod.plist' sh 'rm -rf output/dsyms' end end

Slide 58

Slide 58 text

ϦϦʔεϝʔϧˠdSYM

Slide 59

Slide 59 text

ʹ͗΍͔

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

To be continued...

Slide 62

Slide 62 text

ٕज़ॻయͰ ຊग़͠·ͨ͠

Slide 63

Slide 63 text

Thank you