fastlane이란 android와 ios 앱 어플리케이션을 자동으로 배포할 수 있도록 도와주는 오픈소스 플랫폼이다. fastlane을 사용하면 매번 앱을 빌드하고 배포할 때 수행해야하는 반복 작업들을 자동화할 수 있다. 이는 귀찮은 작업을 줄여주는 효과뿐 아니라, 소요되는 시간을 줄여주고 휴먼에러를 방지해주는 효과도 있기 때문에 자주 서비스를 배포해야하는 환경이라면 배포자동화를 구축하는 것이 좋다. 다양한 plugin들을 지원해주기도하고, github actions나 jenkins, Circle CI 같은 CI tool과도 연계해서 사용이 가능하기 때문에 특정 브랜치에 push했을 때 firebase app distribution에 배포하거나, codepush를 날려주거나, 정식으로 스토어에 업로드하는 등 다양한 상황에 맞춰 사용할 수 있는 범용성도 갖추고 있다. 이번에는 우선 ios 기준으로, testflight에 자동으로 배포하는 환경을 구축해보고자 한다.
fastlane은 ruby를 사용하기 때문에 rubygems를 사용해서 설치할 수 있다. 만약 mac을 사용하고 있다면 homebrew를 통해서도 설치가 가능하니 마음에 드는 선택지를 고르면 된다.
# Using RubyGems
sudo gem install fastlane -NV
# Alternatively using Homebrew
brew cask install fastlane
(하지만 내가 참고했던 블로그에서는 homebrew로 설치했을 때 에러가 발생하기도 했다고 하니, 나는 안전하게 gem을 이용해 fastlane을 설치했다.)
react native 프로젝트의 루트 폴더에서 ios 폴더로 이동한 뒤 fastlane init 명령어를 이용해 fastlane 파일들을 생성해주었다.
cd ios && fastlane init
초기화하는 동안 어떤 목적으로 자동화를 구축하려고 하는지 물어보는데, 이때 나는 testflight 배포 자동화에 해당하는 2번을 골랐다.(사실상 코드를 거의 갈아엎어야하기 때문에 수동 설정에 해당하는 4번을 골라도 무방하다.)
그리고 이후에는 app store connect와의 연동을 위해 로그인 정도를 물어보는데, 이때 apple 계정의 아이디와 비밀번호를 입력해주면 초기화 과정이 끝이난다.
명령어가 성공적으로 수행되면 ios 폴더에 fastlane 폴더가 생성되며, 안쪽에 Appfile과 Fastfile이라는 두개의 파일이 생성된 것을 확인할 수 있다.
우리가 주로 다뤄야하는 부분은 바로 Fastfile이다. Fastfile을 열어보면 기본적인 lane이 설정되어 있는데(2번을 선택했다면). lane이란 특정 명령어를 입력했을 때 실행할 자동화코드들을 모아놓은 것이라고 이해하면 된다. testflight 배포 자동화의 경우 아래와 같이 초기코드가 설정되어 있다.
default_platform(:ios)
platform :ios do
desc "Push a new beta build to TestFlight"
lane :beta do
increment_build_number(xcodeproj: "myApp.xcodeproj")
build_app(workspace: "myApp.xcworkspace", scheme: "myAppScheme")
upload_to_testflight
end
end
여기서 xcodeproj,workspace,scheme에 해당하는 부분은 각자 진행하는 프로젝트 이름으로 자동입력될 것이다. 간단하게 코드를 설명하자면, ios폴더에서 fastlane beta라는 명령어를 실행하면 increment_build_number, build_app, upload_to_testflight 순서로 미리 설정해둔 코드가 실행되며 업로드가 완료되는 형태이다.
다만, 이 상태로 fastlane beta 명령어를 실행하면 한가지 에러가 발생하게 된다. fastlane이 appstore connect에 업로드할 때 인증에 실패한다는 내용이었다.
Error: Unable to upload archive. Failed to get authorization for username and password
스택오버플로우에 올라온 답변에 따르면 이 에러는 https://appleid.apple.com/account/manage 에 접속해서 App-Specific Passwords(앱 암호)를 발급받아서 해결할 수 있다고 한다.
링크로 접속해 나온 페이지에서 '앱 암호' 박스를 클릭해서 password를 발급받고, 이 패스워드를 fastlane 폴더 안쪽에 .env.default 파일을 만들어 입력하는 형태이다.
//.env.default
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=여기에 앱 암호 입력해주세요.
여담으로 App-Specific Passwords란 일종의 토큰같은 개념이다. fastlane과 같이 일부 서비스들은 apple 서비스에 접근할 수 있는 권한을 요청하게 되는데, 이때 apple 계정의 id와 비밀번호를 공개하는건 너무 위험하므로, 앱 암호라는 것을 만들어 사용할 수 있게 하는 것이다. 발급한 앱 암호는 언제든지 해지할 수 있기 때문에 보안측면에서 훨씬 유리하다.
기본적으로 제공하는 코드만으로도 배포가 가능하긴 하지만 보다 쾌적한 자동화를 위해 코드를 수정할 필요가 있다. 결과부터 보자면 아래와 같이 수정하면 된다.
default_platform(:ios)
platform :ios do
def updateVersion(options)
if options[:version]
version = options[:version]
else
version = prompt(text: "버전을 입력해주세요 (ex 1.0.0) : ")
end
re = /\d+.\d+.\d+/
versionNum = version[re, 0]
if (versionNum)
increment_version_number(
version_number: versionNum
)
elsif (version == 'major' || version == 'minor' || version == 'patch')
increment_version_number(
bump_type: version
)
elsif (version == 'maintain')
else
UI.user_error!("[ERROR] Wrong version!!!!!!")
end
end
desc "Push a new beta build to TestFlight"
lane :beta do |options|
cert
sigh(force: true)
updateVersion(options)
increment_build_number(xcodeproj: "myApp.xcodeproj")
build_app(workspace: "myApp.xcworkspace", scheme: "myAppScheme")
upload_to_testflight
end
end
가장 크게 바뀐 것은 updateVersion 함수가 새롭게 정의된 부분이다. 배포할 경우 이전 배포와 비교해서 반드시 version number가 증가하거나, build number가 증가해야한다. 이때 version number를 컨트롤해줄 함수를 정의한 것이다.
updateVersion 함수는 version을 파라미터로 받아서, 입력받은 version에 따라 version number를 변경해주는 역할을 수행한다. version을 입력받는 방법은 총 2가지로, 처음 명령어를 실행할 때 주는 방법과, 나중에 입력하는 방법이 있다.
fastlane beta version:1.2.3
or
fastlane beta
...
버전을 입력해주세요 (ex 1.0.0) : 1.2.3
'1.2.3'과 같이 특정한 버전을 입력할 경우, 입력한 그대로 version number를 수정하고, major, minor, patch 등 특정 문자를 입력하면 각각에 해당하는 number를 1씩 증가시키는 역할이다.
여기서 major, minor, patch는 각각 version number에서 몇번째에 있는 숫자인지를 나타낸다. 예를 들어 '1.2.3'라는 버전이 있을 때 major는 1, minor는 2, patch는 3에 해당한다. 참고
//현재 버전 1.2.3
fastlane beta version:major // 2.0.0
fastlane beta version:minor // 1.3.0
fastlane beta version:patch // 1.2.4
여기서 추가적으로 나는 maintain이라는 케이스를 추가했는데, increment_version_number를 실행하지 않는 케이스이다. maintain을 version으로 주면, 이전 버전과 동일한 number를 가지고, build number만 증가하게 된다.
//현재 버전 1.2.3
fastlane beta version:maintain //1.2.3
이전에는 안보였던 코드 중에 cert와 sigh가 보일 것이다. 이부분은 인증서를 다루는 파트이다. 이 코드를 삽입하고 배포를 실행하면 keychain password를 입력하는 문구가 등장하는데, 이때 password를 입력하고, 그 password를 아까 앱 암호와 마찬가지로 .env.default 파일에 저장해두면 나중에 다시 입력하는 귀찮음을 덜 수 있다.
//.env.default
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=여기에 앱 암호 입력해주세요.
MATCH_KEYCHAIN_PASSWORD=여기에 키체인 암호를 임력해주세요.
보통 fastlane에서 인증서를 다룰 때에는 match를 많이 사용하게 되는데, cert와 sigh와 어떤 차이점이 있는지, 어떤 상황에서 무엇을 사용해야하는지 등에 대해서는 추가적으로 공부할 예정이다.
이제 ios에서 fastlane으로 배포 자동화를 할 모든 준비가 끝났다.
fastlane beta
.
.
.
+------+------------------------+-------------+
| fastlane summary |
+------+------------------------+-------------+
| Step | Action | Time (in s) |
+------+------------------------+-------------+
| 1 | default_platform | 0 |
| 2 | cert | 1 |
| 3 | sigh | 5 |
| 4 | increment_build_number | 0 |
| 5 | build_app | 142 |
| 6 | upload_to_testflight | 293 |
+------+------------------------+-------------+
명령어를 입력하고 기다리자 배포가 완료되고 서머리가 출력된다. 이제 appstore connect에 접속해보면 성공적으로 앱이 배포된 것을 확인할 수 있다. 이번에는 testflight에만 업로드를 진행했지만, 스토어에 정식 배포하는 과정도 이와 크게 다르지 않으므로, 쉽게 따라해볼 수 있다.
참조 링크: https://dev-yakuza.posstree.com/ko/react-native/fastlane/
위 코드대로 실행하면 정상적으로 배포가 완료되지만, build_app 페이즈에서 scheme를 수정하면 수정된 scheme를 찾지 못한다는 에러가 발생한다.
예를 들어서 나의 경우 release 타입으로 빌드하는 A라는 스키마 외에 staging 타입으로 빌드하는 B라는 스키마를 별도로 만들어 주었다. 그런데 staging 배포를 위해 build_app(workspace: "myApp.xcworkspace", scheme: "B") 라고 하면 B를 찾지 못한다는 메시지와 함께 A 스키마로 빌드가 진행되는 현상이 발생했다.
이 에러는 B라는 스키마를 새로 만들면서 share를 비활성화 해놨기 때문에 발생했다. 문제를 해결하기 위해 xcode에 scheme 설정을 변경해 주었다.
Xcode의 Manage Schemes를 누르면 나오는 창에서 우측 Shared를 체크해주면 fastlane이 공유된 scheme를 인식하고 에러가 사라진다.