Fastlane, Jenkins를 이용해서 평일 아침 7시에 iOS 앱의 베타 버전을 TestFlight, Firebase로 자동으로 배포한다.

Jongwon Woo
6 min readAug 8, 2020

--

“Fastlane, Jenkins를 이용해서 평일 아침 7시에 iOS 앱의 베타 버전을 TestFlight, Firebase로 자동으로 배포한다.”

이 목표를 작게 쪼개면…

  • Git에서 브랜치를 체크아웃하고,
  • Xcode로 빌드하고,
  • IPA를 추출하고,
  • IPA를 TestFlight, Firebase에 업로드하고,
  • 슬랙으로 결과를 알려주고,
  • 이 모든 과정을 정해진 시간에 반복해서 실행한다.

빌드, 업로드, 노티는 Fastlane으로 해결할 수 있다. 체크아웃, 스케줄링은 Jenkins로 해결할 수 있다.

(*) Xcode Server는 선택지에서 제외했다. 왜냐하면 IPA로 추출하는 것도, TestFlight로 업로드하는 것도 복잡한 커맨드를 작성해야 하기 때문이다.

우선 Fastlane으로 빌드하고 IPA를 추출하는 작은 목표를 해결한다.

(*) Xcode 프로젝트는 이미 생성되었고, 프로젝트에는 Release, Adhoc Configuration이 있고, 하나의 scheme이 있는 상태이다.

(*) 커맨드 입력은 프로젝트 루트 폴더에서 진행한다.

Fastlane을 설치하고…

$ xcode-select — install

$ /bin/bash -c “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

$ brew install fastlane

$ sudo gem install fastlane

Fastlane 설정을 진행하고…

$ fastlane init

이때 선택하라고 나오는데 TestFlight에 업로드할거니까 TestFlight가 있는 옵션을 선택한다.

여기까지 진행하면 프로젝트 루트에 fastlane 폴더가 생기고 Fastfile, Appfile이 생긴다. Appfile을 열면 번들 아이디, 애플 계정 정보 등이 쓰여있다. Fastfile을 열어서 Fastfile을 프로젝트에 맞게 수정한다.

lane을 하나 추가하고 이름을 tf라고 짓는다. TestFlight 업로드에 사용할 lane이다.

lane :tf do
..build_app(
….workspace: “MyApp.xcworkspace”,
….configuration: “Release”,
….scheme: “myapp”,
….export_method: “app-store”
..)
end

빌드가 잘 되고 IPA가 만들어지는지 테스트한다. IPA는 프로젝트 루트에 생긴다.

$ faslane tf

IPA를 TestFlight에 업로드하는 작은 목표를 해결한다.

tf lane에 upload_to_testflight 액션을 추가한다.

upload_to_testflight(
..username: “yourappleid@test.com”,
..skip_waiting_for_build_processing: true
)

skp_waiting_for_build_processing 옵션은 바이너리 프로세싱을 기다리지 않고 커맨드 실행에서 빠져나오는 역할을 한다.

TestFlight에 바이너리를 올리려면 TestFlight에 이미 존재하는 빌드번호가 아니어야 한다. 그래서 빌드할 때 빌드번호를 자동으로 올려주는게 편하다.

private_lane :update_buildnumber do
..current_build_number = latest_testflight_build_number(
….username: “yourappleid@test.com”,
….app_identifier: “com.my.app.bundle.id”
..)
..increment_build_number(
….build_number: current_build_number + 1,
….xcodeproj: “MyApp.xcodeproj”
..)
end

빌드번호를 업데이트해주는 lane을 tf lane에 추가한다.

lane :tf do
..update_buildnumber
..build_app(
….workspace: “MyApp.xcworkspace”,
….configuration: “Release”,
….scheme: “myapp”,
….export_method: “app-store”
..)
end

IPA가 TestFlight에 업로드되는지 확인한다.

$ faslane tf

lane 실행 결과를 Slack으로 알려주는 작은 목표를 해결한다.

Slack에 Incoming Webhook을 설정한다. webhook URL을 slack 액션에 추가한다.

slack(
..message: “[#{build_number}] App successfully uploaded to TestFlight”,
..slack_url: slack_webhook_url
)

이 액션을 tf lane에 추가하고 Slack으로 메시지가 잘 오는지 확인한다.

위 과정을 주기적으로 하는 작은 목표를 해결한다.

Jenkins를 설치하고 실행한다.

$ brew install jenkins

$ jenkins

웹브라우저에서 http://localhost:8080 접속한다.

Jenkins에 프로젝트를 설정하고…

  • Create freestyle project
  • Setting up Git
  • Build Periodically. 평일 아침 7시 -> “0 7 * * 1–5”
  • Execute shell. “fastlane tf”

Build now 버튼을 눌러서 잘 실행되는지 확인한다. App Store Connect에 접속해서 TestFlight에 새로운 바이너리가 추가되었는지 확인한다.

(Optional) Firebase App Distribution에도 업로드하려면 firebase_app_distribution 액션을 추가해주면 된다.

더 하면 좋을 것들…

Apple 계정 정보, Firebase 계정 정보, Git 계정 정보 등을 안전하게 보관하고 사용하는 방법을 고민한다. 여러가지 방법이 이미 존재한다.

Fastlane 실행 시 발생할 수 있는 에러를 처리하는 방법을 고민한다. TestFlight, Firebase, Git 등 의존하고 있는 모든 것들이 항상 온전할 수는 없다.

Tips

Jenkins에서 fastlane: command not found 에러가 발생하면… Jenkins 환경변수에 Path = /bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin 등을 추가한다.

Fastlane 실행 시 invalid byte sequence in US-ASCII 에러가 발생하면… 한글 때문에 발생하는 이슈이다. Jenkins 환경변수에 LC_ALL = en_US.UTF-8을 추가한다.

Jenkins가 정해진 시간에 빌드를 하지 않는다면… macOS 에너지 절약 설정에서 컴퓨터 잠자기를 막는다.

시간이 생기면 (다른 일을 할 수 있다.) 놀 수 있다.

--

--