-
Fastlane 으로 Firebase Distribution 연동하기iOS 2020. 8. 6. 10:02반응형
Fastlane 은 커맨드 라인으로 iOS 어플리케이션을 배포하게 하는 프로그램입니다.
Firebase Distribution은 애드훅을 이용해 테스트플라이트의 빌드를 거치지 않고 배포하게 하는 툴입니다.
두개를 조합하면, 커맨드라인에서 파이어베이스로 원큐에 올리는 프로그램을 작성할 수 있습니다.
이번 포스트에서는 fastlane 사이닝 이슈, firebase distribution 연동 이슈 등은 생략합니다.
https://sesang06.tistory.com/124
https://sesang06.tistory.com/182
https://sesang06.tistory.com/187
Firebase Distribution CLI 설치하기
Firebase CLI(GitHub)는 Firebase 프로젝트를 관리, 조회, 배포할 수 있는 다양한 도구를 제공합니다.
설치하기
쉘에서 아래 명령어를 입력합니다.
curl -sL https://firebase.tools | bash
이 스크립트는 운영체제를 자동으로 감지하고 최신 Firebase CLI 릴리스를 다운로드한 다음 디렉터리에 관계없이 사용 가능한
firebase
명령어를 사용 설정합니다.로그인하여 Firebase CLI 테스트
CLI를 설치한 후에는 인증해야 합니다. 그러면 Firebase 프로젝트를 나열하여 인증을 확인할 수 있습니다.
다음 명령어를 실행하여 Google 계정으로 Firebase에 로그인합니다.
firebase login
이 명령어는 로컬 머신을 Firebase에 연결하고 Firebase 프로젝트에 대한 액세스 권한을 부여합니다.
참고:
firebase login
명령어는 머신에서localhost
에 연결되는 웹페이지를 엽니다. 원격 머신을 사용 중이며localhost
에 액세스할 수 없는 경우--no-localhost
플래그를 사용하여 명령어를 실행합니다.Fastlane 설치하기
Xcode 커맨드 라인 툴을 설치합니다.
xcode-select --install
fastlane을 설치합니다.
# RubyGems 사용 sudo gem install fastlane -NV # Homebrew 사용 brew cask install fastlane
Fastlane - Firebase Distribution 플러그인 최초 연동하기
-
앱 배포를 fastlane 구성에 추가하려면 iOS 프로젝트의 루트에서 다음 명령어를 실행합니다.
fastlane add_plugin firebase_app_distribution
명령어에서 원하는 옵션을 선택하라는 메시지가 표시되면
Option 3: RubyGems.org
를 선택합니다.
fastlane을 사용하여 테스터에 iOS 앱 배포 | Firebase
이미 연동된 프로젝트에 설정된 Firebase Distribution 설치하기
이미 연동된 프로젝트에 새 머신에서 플러그인을 설치하려고 할 경우에, Firebase Distribution 플러그인이 깔려 있으므로, 아래 커맨드로 새 머신에서는 받아옵니다.
fastlane install_plugins
Sudo 명령어를 치지 않아도 Firebase Distribution 명령어를 칠 수 있도록 설정하기
카탈리나부터 설치에 이슈가 생겨서
sudo
를 fastlane firebase plugin 에 붙여주지 않으면 읽지 못하는 오류가 생겨났습니다.아래 명령어로 읽기 권한을 일반 유저에게도 추가하여,
sudo
를 치지 않아도 되도록 수정합니다.sudo chmod -R a+r /Library/Ruby/Gems/2.6.0/gems/fastlane-plugin-firebase_app_distribution-0.1.4
How to fix fastlane-plugin-firebase_app_distribution undefined
Fastfile에 배포 스크립트 작성하기
ruby 기반으로 되어 있는 fastfile 에 스크립트를 작성하면, 쓸만한 자동화를 건져낼 수 있습니다.
제 일반적인 배포는 다음과 같이 일어납니다.
- develop 브랜치에서 qa 브랜치를 땁니다.
- adhoc 사이닝을 진행합니다.
- 앱을 빌드합니다.
- 버전을 하나 올리고 커밋합니다.
- 애드훅으로 아카이브하여 파이어베이스에 업로드합니다.
- 파이어베이스에 릴리즈 노트를 작성하고, 테스트 그룹을 추가합니다.
- 관계자에게 슬랙으로 파이어베이스에 업로드되었음을 알리고, 변경된 내역을 정리합니다.
- 리모트 깃에 내역을 공유합니다.
1부터 8까지의 내역을 모두 자동화할 수 있습니다.
fastlane 도큐먼트에는 쓸만한 slack, git 관련 명령어들이 많기 때문입니다.
이 과정을 전부 자동화한 것이
upload_to_firebase
함수입니다.# This file contains the fastlane.tools configuration # You can find the documentation at https://docs.fastlane.tools # # For a list of all available actions, check out # # https://docs.fastlane.tools/actions # # For a list of all available plugins, check out # # https://docs.fastlane.tools/plugins/available-plugins # # Uncomment the line if you want fastlane to automatically update itself # update_fastlane default_platform(:ios) module Constant # 프로젝트 이름 PROJECT = "a.xcodeproj" # 심사 요청 스키마 TARGET_RELEASE = "a" # 개발 버전 스키마 TARGET_DEBUG = "a UrlConvertable" # 앱 올리고 나서 배포할 때 slack hook SLACK_URL = "https://hooks.slack.com/services/adsfkjas;dlkfjasdkl;fj" # 인증서를 가져올 app id APP_IDENTIFIERS = ["com.company.a"] # 파이어베이스 배포에 올릴 앱 FIREBASE_APP_ID = "1:1234564654564:ios:1231321231" # 파이어베이스 배포 테스트 그룹 FIREBASE_TEST_GROUP = "test" # 파이어베이스 배포를 하고 버전 범프를 푸시할 리모트의 이름 REMOTE_NAME = "origin" end def get_version() return get_version_number(xcodeproj: Constant::PROJECT, target: Constant::TARGET_RELEASE) end def get_build() return get_build_number(xcodeproj: Constant::PROJECT) end def set_version(version) increment_version_number( version_number: version, xcodeproj: Constant::PROJECT ) update_info_plist( plist_path: "Settings.bundle/Root.plist", block: proc do |plist| versionSpecifiers = plist["PreferenceSpecifiers"].find{|specifiers| specifiers["Key"] == "Version"} versionSpecifiers["DefaultValue"] = version end ) end def set_build(build) increment_build_number( build_number: build, xcodeproj: Constant::PROJECT ) end def increase_build(increase_by = 1) build = get_build() splited = build.split('.') major = splited[0] minor = splited[1] patch = splited[2] build_number = splited[3] new_build = "#{major}.#{minor}.#{patch}.#{build_number.to_i + increase_by}" set_build(new_build) return new_build end def increase_version(increase_by = 1) version = get_version() splited = version.split('.') major = splited[0] minor = splited[1] patch = splited[2] new_version = "#{major}.#{minor}.#{patch.to_i + increase_by}" new_build = "#{new_version}.0" set_version(new_version) set_build(new_build) return new_version end def on_success(message_value) version = get_version() build = get_build() slack( message: message_value, success: true, slack_url: Constant::SLACK_URL, attachment_properties: { fields: [ { title: "버전", value: version }, { title: "빌드", value: build } ] } ) end def on_error(exception) version = get_version() build = get_build() slack( message: "<!here> fastlane 빌드 중 문제가 발생하였습니다.", success: false, slack_url: Constant::SLACK_URL, attachment_properties: { fields: [ { title: "에러 내역", value: exception }, { title: "버전", value: version }, { title: "빌드", value: build } ] } ) end def version_bump() current_build = increase_build() git_add commit_message = "버전 변경 #{current_build}" commit_version_bump( message: commit_message, xcodeproj: Constant::PROJECT, force: true ) end def get_commit_log() last_tag = last_git_tag() add_git_tag changelog = changelog_from_git_commits( between: [last_tag, "HEAD"] ) current_build = get_build() log = """ #{current_build} 버전은 아래와 같은 수정사항이 반영되었습니다. #{changelog} """ return log end def make_qa_branch_if_needed() current_branch = git_branch if current_branch.match(/^qa/) puts("현재 qa 브랜치 #{current_branch} 로 설정되어 있습니다.") return end qa_branch_name = "qa/#{major}_#{minor}_#{patch}" sh("git", "checkout", "-b", qa_branch_name) puts("현재 qa 브랜치 #{qa_branch_name} 를 생성했습니다.") end def upload_to_firebase() make_qa_branch_if_needed() match(app_identifier: Constant::APP_IDENTIFIERS, type: "adhoc") build_app(project: Constant::PROJECT, scheme: Constant::TARGET_DEBUG, configuration: "Debug") version_bump() release_notes = get_commit_log() firebase_app_distribution( app: Constant::FIREBASE_APP_ID, groups: Constant::FIREBASE_TEST_GROUP, release_notes: release_notes ) push_to_git_remote( remote: Constant::REMOTE_NAME, tags: false ) slack_message = """ <!here> 파이어베이스에 배포 버전이 올라갔습니다. #{release_notes} """ on_success(slack_message) end platform :ios do lane :release do begin match(app_identifier: Constant::APP_IDENTIFIERS, type: "adhoc") build_app(project: Constant::PROJECT, scheme: Constant::TARGET_RELEASE) on_success("심사버전 빌드를 완료했습니다. 업로드를 진행합니다.") upload_to_app_store(skip_metadata: true, skip_screenshots: true) on_success("심사버전 업로드를 완료했습니다.") rescue => exception on_error(exception) end end lane :develop do begin match(app_identifier: Constant::APP_IDENTIFIERS, type: "appstore") build_app(project: Constant::PROJECT, scheme: Constant::TARGET_DEBUG, configuration: "Debug") on_success("개발버전 빌드를 완료했습니다. 업로드를 진행합니다.") upload_to_testflight(skip_submission: true, skip_waiting_for_build_processing: true) on_success("개발버전 업로드를 완료했습니다.") rescue => exception on_error(exception) end end lane :firebase do begin upload_to_firebase() rescue => exception on_error(exception) end end lane :qa do make_qa_branch_if_needed() end # get_version 명령어 def command_gv() version = get_version() puts("현재 버전은 #{version} 입니다. 🎉") end lane :get_version do command_gv() end lane :gv do command_gv() end # get_build 명령어 def command_gb() build = get_build() puts("현재 빌드는 #{build} 입니다. 🎉") end lane :get_build do command_gb() end lane :gb do command_gb() end # set_version 명령어 def command_sv(options) version = get_version() new_version = options[:number].strip new_build = "#{new_version}.0" set_version(new_version) set_build(new_build) puts("버전이 #{version} 에서 #{new_version} 으로 변경되었습니다. 🎉") puts("빌드가 #{new_build} 로 자동 설정되었습니다. 🎉") end lane :set_version do |options| command_sv(options) end lane :sv do |options| command_sv(options) end # set_build 명령어 def command_sb(options) build = get_build() new_build = options[:number].strip set_build(new_build) puts("빌드가 #{build} 에서 #{new_build} 으로 변경되었습니다. 🎉") end lane :set_build do |options| command_sb(options) end lane :sb do |options| command_sb(options) end # increase_version 명령어 def command_iv(options) increase_by = 1 if options[:by] increase_by = options[:by].to_i end new_version = increase_version(increase_by) puts("버전이 #{new_version} 으로 올라갔습니다. 🎉") puts("빌드가 #{new_version}.0 으로 자동 설정되었습니다. 🎉") end lane :increase_version do |options| command_iv(options) end lane :iv do |options| command_iv(options) end # increase_build 명령어 def command_ib(options) increase_by = 1 if options[:by] increase_by = options[:by].to_i end new_build = increase_build(increase_by) puts("빌드가 #{new_build} 으로 올라갔습니다. 🎉") end lane :increase_build do |options| command_ib(options) end lane :ib do |options| command_ib(options) end end
반응형'iOS' 카테고리의 다른 글
FastLane Match 를 이용해 사이닝 관리하기 (0) 2020.08.06 XcodeGen을 활용해 xcodeproj의 마지 컨플릭트에서 벗어나라 (0) 2020.04.26 애드훅보다 간편하다! 테스트플라이트보다 빠르다! 파이어베이스 iOS 배포를 활용해 보자 (4) 2020.04.26 Xcode 11 에서 애드훅으로 앱을 배포하는 방법, 드롭박스나 구글 클라우드 이용보다 확실한 배포 방법 (1) 2020.04.26 [잡설] 빌드 속도 단축에 대한 고민 (0) 2020.04.25