콘텐츠로 이동

fastlane으로 iOS 빌드와 TestFlight 업로드 자동화

Unity가 뽑아낸 Xcode 프로젝트를 대상으로, App Store Connect API 키로 인증해 TestFlight까지 올리는 lane을 만들었다. 설정 파일은 세 가지로 나눈다.

파일 역할 공유 범위
.env App Store Connect API 인증 키 개발자 계정(조직)별
Appfile 번들 ID, Apple ID, 팀 ID 앱별
Fastfile 빌드·업로드 lane 모든 프로젝트 공통

Xcode 프로젝트 루트에서 최초 1회 init한다.

터미널 창
fastlane init

App Store Connect API로 인증하려면 세 가지 값이 필요하다. 앱이 서로 다른 개발자 계정에 속해 있으면 계정마다 값이 다르다.

터미널 창
ASC_KEY_ID=<API Key ID>
ASC_ISSUER_ID=<Issuer ID>
ASC_KEY_CONTENT=<.p8 키 내용>

프로젝트마다 다르게 설정한다.

app_identifier("com.example.app") # The bundle identifier of your app
apple_id("<apple-id>") # Your Apple Developer Portal username
itc_team_id("<itc-team-id>") # App Store Connect Team ID
team_id("<team-id>") # Developer Portal Team ID

모든 프로젝트가 같은 Fastfile을 쓴다. 흐름은 필수 옵션 검증, API 키 인증, 자동 서명 설정, TestFlight 최신 빌드 번호 조회 후 +1, 앱 빌드, Changelog 읽기, TestFlight 업로드 순이다.

default_platform(:ios)
platform :ios do
desc "Build and upload to TestFlight"
lane :beta do |options|
# 필수 옵션 검증
UI.user_error!("Missing required option: :name") unless options[:name]
UI.user_error!("Missing required option: :version") unless options[:version]
# App Store Connect API 인증
api_key = app_store_connect_api_key(
key_id: ENV["ASC_KEY_ID"],
issuer_id: ENV["ASC_ISSUER_ID"],
key_content: ENV["ASC_KEY_CONTENT"],
duration: 1200,
in_house: false
)
# 자동 코드 서명 설정
update_code_signing_settings(
use_automatic_signing: true,
path: "Unity-iPhone.xcodeproj"
)
# 최신 빌드 넘버를 가져와서 증가
current_build_number = latest_testflight_build_number(
version: options[:version],
api_key: api_key
)
increment_build_number(
build_number: current_build_number + 1,
xcodeproj: "Unity-iPhone.xcodeproj"
)
# IPA 파일명 설정
ipa_name = "#{options[:name]}.ipa"
# 앱 빌드
build_app(
workspace: "Unity-iPhone.xcworkspace",
scheme: "Unity-iPhone",
clean: true,
export_options: {
allowProvisioningUpdates: true,
signingStyle: "automatic"
},
output_directory: "./build",
output_name: ipa_name,
xcargs: "-allowProvisioningUpdates"
)
# Changelog 파일 읽기 (없으면 기본값 사용)
changelog_path = "./Changelog.txt"
changelog = File.exist?(changelog_path) ? File.read(changelog_path) : "Empty Changelog."
# TestFlight 업로드
upload_to_testflight(
api_key: api_key,
changelog: changelog,
ipa: "./build/#{ipa_name}"
)
end
end

Jenkins에서는 빌드 파라미터로 이름을 넘긴다.

터미널 창
fastlane beta name:"${IPA_NAME}"

로컬에서는 이름을 직접 넣는다.

터미널 창
fastlane beta name:GameA

앱마다 .env·Appfile 정보가 달라서, Jenkins 작업 공간 아래에 앱별 디렉토리를 따로 두고 관리한다.