(뻘소리 주의!! 시간을 아끼고 싶은 분들은 아래 '개요'부터 읽으시면 됩니다.)
서비스를 스토어에 출시한 지 벌써 1년 2개월이 지났다(25년 1월에 작성한 초안 글). 중간에 회사에서 또 다른 서비스를 기획하고 출시까지 해야되는 프로젝트가 있었어서 약 6개월 정도는 기존 서비스에 새로운 기능을 추가하거나 유지보수를 하진 못했었고, 단순 에러만 대응하고 버전 업데이트만 할 뿐이었다. 프로젝트가 끝난 뒤 2024년 9월부터 회사에서 기존 제품의 경쟁력을 갖추기 위해 신규 인력도 채용하고 추가적인 기획 및 개발을 해서 제품의 완성도를 올리려는 목표를 설정했다. 매주마다 지속적인 QA를 하며 사용자로부터 애로 및 건의사항을 통해 지속적으로 앱을 수정하며 기능을 추가하는 작업을 했었다. 이 과정에서 앱 버전을 업데이트하는 경우가 많았는데, 지금까지 모두 수동으로 작업을 했었다.
▲ 그림 1. 앱 버전 업데이트 기록
데스크톱 메모장에 일명 "매크로"라고 칭하여 매번 업데이트마다 아래 메모장을 참고하여 수동으로 배포했었다. Play Console 과 App Store Connect 에 해당 배포 작업이 약 20분정도 소요되었고, 해당 작업을 하는 동안은 그래도 계속 대기하며 붙들어매진 않았지만 중간중간에 버튼 몇번 딸깍 하는 작업이 필요했어서 번거로웠다.
▲ 그림 2. 수동으로 앱 버전 업데이트할 때
그럼 '여태까지 배포 자동화를 도입하지 않았던 이유가 무엇이냐'라고 한다면,
작업의 우선순위가 그리 높진 않았다. 우리 회사는 스타트업이기도 했었고, 개발인력이 많이 없음에도 불구하고 빠르게 기능을 개발해서 시장 평가를 받고 유의미하지 않으면 바로 교체해서 다른 기능을 선보여야 했기 때문에 기능 개발이 최우선이었다. 배포 자동화를 구축하면 업데이트 시 편리하긴 하겠지만 수동으로 해도 문제가 없었으니, 배포 자동화 시스템 구축을 Optional 하게 생각했었던 것이다.
'그럼 이제 와서 구축하는 이유는?' 라고 한다면,
일정에 여유가 생겨서이다. 조금의 프로젝트 복기할 시간이 생겨서 프로젝트 리팩토링도 할 겸, 겸사겸사 배포 자동화 시스템도 구축하기로 했다. 앱을 처음 출시할 당시 사내 백엔드 인력이 없어서 백엔드 업무를 봤었는데 그 때 Github Actions
, S3
, CodeDeploy
를 이용해 배포 자동화를 구축한 적이 있었다. 이 때는 Github repository 에 푸시만 하면 자동으로 테스트, 빌드, 배포까지 모두 진행이 되었던 터라, 모바일쪽에도 백엔드처럼 repository 에 푸시하면 자동으로 스토어에 앱 빌드 파일이 올라가면 편리하겠다는 니즈가 생겨 백엔드와 비슷한 flow로 구축하고자 한다.
지금까지 서론이 너무 길었는데, 이미 운영되고 있는 서비스에 Fastlane
과 Github Actions
를 이용하여 모바일 운영체제(iOS, Android) 에 따른 배포 자동화 시스템 구축에 대해 알아보도록 하자.
배포 자동화 시스템 구축에서 사용하는 툴이나 서비스들은 다음과 같다.
- CI/CD 시스템
- Github Actions
- Self-hosted Runner
- Secrets 관리(Github Actions Secrets)
- 자동 빌드 오픈소스(iOS & Android)
- Fastlane
- iOS
- Match (프로비저닝 프로필 및 인증서 관리)
- App Store Connect API (빌드 업로드 및 배포)
- Gym (IPA 빌드)
- Pilot (TestFlight 배포)
- Deliver (App Store 메타데이터 및 스크린샷 업로드)
- Android
- Gradle (APK/AAB 빌드)
- Play Console API (빌드 업로드 및 배포)
- Fastlane Supply (Google Play 메타데이터 관리)
sudo gem install fastlane -NV
cd ios
fastlane init
▲ 그림 3. iOS Fastlane init
여기서 목적에 맞게 선택하면 된다. TestFlight 에 업로드할 용도로'만' 쓴다면 2번, TestFlight 업로드를 제외하고 App Store Distribution 용으로'만' 쓴다면 3번을 선택하면 된다. 필자처럼 둘 다 고려해야 하는 상황이라면(TestFlight && App Store Distribution) 2번 혹은 3번 아무거나 선택해도 무방하다. 추후에 'Fastfile
'에 추가로 설정해주면 되기 때문이다.
▲ 그림 4. flavor Option
이 경우, 우리 프로젝트는 Flavor를 이용하여 개발용 환경인 'dev' 와 서비스 운영용 'prod' 이 둘을 관리하기 때문에 다음 사진처럼 두 가지 옵션이 나왔는데, 이 때 아무거나 선택해도 된다. 어차피 이후에 마찬가지로 Fastfile을 직접 수정해서 dev와 prod 모두 배포할 수 있도록 설정할 것이다.
▲ 그림 5. 자동 생성된 Fastfile 관련 파일
iOS 앱을 배포하기 위해서는 인증서(certificate)와 프로비저닝 프로필(provisioning profile)이 반드시 필요하다. 이 파일들은 팀원 간 공유가 어렵고, 수동으로 관리하면 빌드 오류나 인증서 충돌이 자주 발생한다.
match는 이러한 문제를 해결하기 위해 Fastlane에서 제공하는 프로비저닝 프로필 및 인증서 버전 관리 도구다. 팀 단위 개발에서 인증서를 안전하게 공유하고, CI/CD 환경에서도 동일한 인증서를 재사용할 수 있도록 해준다.
Git 기반 중앙 관리
- 인증서와 프로비저닝 프로필을 Git repository에 저장하여 팀원과 안전하게 공유 가능
자동 생성 및 설치
- 필요한 인증서를 자동으로 생성하고 설치하므로 수동 작업 최소화
충돌 없는 인증서 관리
- 기존 인증서와의 충돌 방지를 위한 match nuke 기능 제공
CI/CD 연동 최적화
- GitHub Actions, Bitrise, Jenkins 등 자동화 도구와 쉽게 연동 가능
fastlane match init
▲ 그림 6. 인증서 저장 위치 선택
git의 repository 에 profile 과 certificate를 저장할 것이기 때문에 1번을 선택.
git_url("[github_project_url]")
storage_mode("git")
type("appstore") # The default type, can be: appstore, adhoc, enterprise or development
# app_identifier(["tools.fastlane.app", "tools.fastlane.app2"])
# username("user@fastlane.tools") # Your Apple Developer Portal username
# For all available options run `fastlane match --help`
# Remove the # in the beginning of the line to enable the other options
# The docs are available on https://docs.fastlane.tools/actions/match
이후 기존에 로컬에서 사용하고 있는 인증서가 있다면 추후에 match 를 통해 생성할 인증서와 충돌이 생기기 때문에 모두 삭제를 해야 한다.
fastlane match nuke development # 개발인증서 삭제
fastlane match nuke distribution # 배포인증서 삭제
▲ 그림 7. 인증서 삭제
위의 커맨드를 통해 기존의 인증서들을 모두 삭제했다면 match 인증서를 어느 브랜치에서 관리할 것인지 다음과 같이 명시해주면 된다.
fastlane match development --git_branch "[브랜치 이름]" # 개발용 인증서
fastlane match appstore --git_branch "[브랜치 이름]" # 앱스토어 배포용 인증서
▲ 그림 8. Github Repository 에 자동 생성된 인증서
이제 로컬에서 사용했던 인증서(Xcode 에서 앱 빌드 시 사용되었던 인증서)를 모두 삭제했으니, match 를 통해 생성된 인증서를 연결해주면 된다.
▲ 그림 9. Provisioning Profile 변경 -> Match
iOS 앱을 TestFlight 또는 App Store에 배포하려면 App Store Connect에 접근해야 한다. Fastlane을 사용하는 경우, 이 인증을 처리하는 방식은 아래 세 가지 중 하나를 선택할 수 있다.
- App Store Connect Api Key
- 이중 인증(2FA)
- Application-specific passwords
이 중에서 Fastlane 공식 문서와 실무 환경에서는 1번 방식인 App Store Connect API Key 사용을 가장 권장한다. 이러한 이유로는 다음과 같다.
이러한 문제점을 해결하기 위해 Apple은 JWT 기반 인증 방식인 App Store Connect API Key를 도입했다. 이 방식은 로그인 없이도 인증이 가능하며, 인증 정보를 GitHub Secrets 등과 함께 안전하게 관리할 수 있어 완전한 무인 자동화가 가능하다.
▲ 그림 10. App Store Connect API Key 발급
Fastfile 구성은 다음과 같다. do_signing
lane은 App Store, TestFlight에 앱을 배포할 때 공통적으로 호출되며, App Store Connect Api 를 통해 발급받은 Api Key 정보를 기반으로 Apple Developer 계정에 인증한 후, match를 통해 git에 저장된 서명 정보(인증서, provisioning profile 등)를 안전하게 가져와 Xcode 빌드를 위한 사전 준비를 수행한다.
## iOS Signing 설정 (API Key & Match)
desc "Do Signing for Deploy iOS APP"
lane :do_signing do |options|
api_key = app_store_connect_api_key(
key_id: options[:key_id],
issuer_id: options[:issuer_id],
key_content: options[:key_content],
is_key_content_base64: true
)
match(
type: 'appstore',
app_identifier: options[:app_identifier],
api_key: api_key,
git_branch: 'release',
readonly: true
)
end
deploy_to_testflight
lane은 do_signing
을 거친 후, 동일하게 scheme에 맞춰 앱을 빌드하고, upload_to_testflight
액션을 통해 TestFlight로 업로드한다. 이 때 scheme은 flavor 의 빌드 환경을 말한다. skip_waiting_for_build_processing
옵션이 설정되어 있어 업로드 후 빌드가 준비되기를 기다리지 않고 즉시 작업이 종료되며, 이후의 Tester 초대 및 배포는 TestFlight 웹 콘솔에서 수동으로 설정할 수 있게 하였다.
## TestFlight 배포 Lane
desc "Deploy to TestFlight"
lane :deploy_to_testflight do |options|
flavor = options[:flavor] || "prod"
do_signing(
key_id: options[:key_id],
issuer_id: options[:issuer_id],
key_content: options[:key_content],
app_identifier: options[:app_identifier]
)
build_app(
clean: true,
workspace: "Runner.xcworkspace",
scheme: flavor,
configuration: "Release"
)
upload_to_testflight(skip_waiting_for_build_processing: true)
end
deploy_to_app_store
lane 또한 do_signing
을 통해 서명 과정을 마친 뒤, 지정된 scheme(기본값은 "prod")으로 앱을 빌드하고 upload_to_app_store
액션을 통해 App Store Connect에 앱 번들을 업로드한다. 이때 자동 출시(automatic_release)나 자동 심사 제출(submit_for_review)은 비활성화했는데, 이는 빌드 파일만 자동으로 올린 뒤 App Store Connect에서 수동으로 출시 및 심사를 진행하기 위함이다. 또한, 앱 버전, 메타데이터, 스크린샷은 모두 건너뛰기 설정했으며, 기존에 설정된 정보를 그대로 유지하게 설정하였다.
## App Store 배포 Lane
desc "Deploy to App Store Connect"
lane :deploy_to_app_store do |options|
flavor = options[:flavor] || "prod"
do_signing(
key_id: options[:key_id],
issuer_id: options[:issuer_id],
key_content: options[:key_content],
app_identifier: options[:app_identifier]
)
build_app(
clean: true,
workspace: "Runner.xcworkspace",
scheme: flavor
)
upload_to_app_store(
automatic_release: false, # 자동 출시 비활성화
submit_for_review: false, # 자동 심사 제출 비활성화
skip_screenshots: true, # 스크린샷 업로드 건너뛰기
skip_metadata: true, # 메타데이터 업데이트 건너뛰기
skip_app_version_update: true, # App Store의 버전 자동 업데이트 비활성화
force: true, # Preview 확인 건너뛰기
precheck_include_in_app_purchases: false # In-App Purchase 검사 건너뛰기
)
end
이처럼 App Store 및 TestFlight 배포 작업은 각각의 lane을 통해 자동화되어 있으며, 사전 준비된 인증 정보를 바탕으로 반복 가능한 안정적인 배포 파이프라인을 구성할 수 있다.
default_platform(:ios)
platform :ios do
## iOS Signing 설정 (API Key & Match)
desc "Do Signing for Deploy iOS APP"
lane :do_signing do |options|
api_key = app_store_connect_api_key(
key_id: options[:key_id],
issuer_id: options[:issuer_id],
key_content: options[:key_content],
is_key_content_base64: true
)
match(
type: 'appstore',
app_identifier: options[:app_identifier],
api_key: api_key,
git_branch: 'release',
readonly: true
)
end
## TestFlight 배포 Lane
desc "Deploy to TestFlight"
lane :deploy_to_testflight do |options|
flavor = options[:flavor] || "prod"
do_signing(
key_id: options[:key_id],
issuer_id: options[:issuer_id],
key_content: options[:key_content],
app_identifier: options[:app_identifier]
)
build_app(
clean: true,
workspace: "Runner.xcworkspace",
scheme: flavor,
configuration: "Release"
)
upload_to_testflight(skip_waiting_for_build_processing: true)
end
## App Store 배포 Lane
desc "Deploy to App Store Connect"
lane :deploy_to_app_store do |options|
flavor = options[:flavor] || "prod"
do_signing(
key_id: options[:key_id],
issuer_id: options[:issuer_id],
key_content: options[:key_content],
app_identifier: options[:app_identifier]
)
build_app(
clean: true,
workspace: "Runner.xcworkspace",
scheme: flavor
)
upload_to_app_store(
automatic_release: false, # 자동 출시 비활성화
submit_for_review: false, # 자동 심사 제출 비활성화
skip_screenshots: true, # 스크린샷 업로드 건너뛰기
skip_metadata: true, # 메타데이터 업데이트 건너뛰기
skip_app_version_update: true, # App Store의 버전 자동 업데이트 비활성화
force: true, # Preview 확인 건너뛰기
precheck_include_in_app_purchases: false # In-App Purchase 검사 건너뛰기
)
end
end
GitHub Actions는 GitHub 저장소에서 특정 이벤트(push, pull_request, release 등)가 발생했을 때 자동으로 정해진 작업을 수행할 수 있도록 해주는 워크플로 자동화 도구이다. 이는 코드 빌드, 테스트, 배포까지 이어지는 전체 CI/CD 흐름을 손쉽게 구성할 수 있게 해준다.
GitHub Actions에서 워크플로는 Runner라는 실행 환경에서 동작하며, 대표적으로 GitHub-hosted Runner와 Self-hosted Runner 두 가지 유형이 있다.
GitHub-hosted Runner는 GitHub에서 제공하는 가상 머신으로, 워크플로가 실행될 때마다 새로운 환경이 생성된다. 사용이 간편하고 관리가 필요 없다는 장점이 있지만, 무료로 제공되는 시간에는 제한이 있고, 초과 시 비용이 발생한다. 예를 들어, 퍼블릭 리포지토리는 무료지만, 프라이빗 리포지토리는 사용량이 제한되고, 초과 시 추가 요금이 청구된다.
Self-hosted Runner는 사용자가 직접 관리하는 서버에서 워크플로를 실행하는 방식이다. 실행 환경을 커스터마이징할 수 있고, 자체 인프라를 이용하므로 과금 부담을 줄일 수 있다는 점에서 유리하다. 특히, 반복적인 테스트나 빌드 작업이 많은 경우 비용 최적화 측면에서 효과적이다.
iOS 앱의 경우, macOS 환경에서만 빌드가 가능하기 때문에 GitHub-hosted Runner를 사용할 경우 반드시 macOS Runner를 사용해야 한다. 문제는 이 macOS Runner의 비용이 매우 높다는 점이다.
GitHub 공식 요금 정책에 따르면 minute multiplier 기준으로 macOS는 Linux 대비 10배의 소모량을 기록하므로, 같은 작업을 실행하더라도 과금 속도가 훨씬 빠르다. 예를 들어, macOS에서 10분짜리 작업을 실행하면 100분이 사용된 것처럼 계산된다. 이러한 비용 구조는 iOS 앱을 자주 빌드하거나 테스트하는 팀에게 상당한 과금 부담으로 이어질 수 있다.
그래서 Self-hosted Runner를 사용하여 macOS 환경을 자체 구축하고, 이를 통해 비용을 크게 절감하면서도 안정적으로 iOS 앱을 빌드할 수 있는 CI/CD 환경을 구성하게 되었다.
▲ 그림 11. Github-hosted Runner Pricing
https://devs0n.tistory.com/137 참고
GitHub Actions에서는 민감한 정보(API 키, 인증 토큰, 비밀번호 등)를 코드에 직접 작성하지 않고 안전하게 관리하기 위해 Secrets 기능을 제공한다.
이 값들은 저장소 설정에서 Settings > Secrets and variables > Actions > New repository secret
경로를 통해 등록할 수 있다.
Actions Secrets 에 추가한 목록들은 다음과 같다.
- Match 관련
- MATCH_PASSWORD : Fastlane match 명령어 실행 시 사용하는 저장소 비밀번호
- App Store Connect 관련
- ASC_KEY_ID: App Store Connect API Key의 Key ID
- ASC_ISSUER_ID: App Store Connect API Key의 Issuer ID
- ASC_KEY_P8: App Store Connect API Key 파일(.p8)의 내용을 base64 인코딩
- Fastlane 관련
- FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: Apple 계정용 App-Specific Password
- 앱 관련
- APP_IDENTIFIER: 앱 패키지
이는 iOS 배포 자동화 때 필요한 데이터들이고, .gitignore
에 추가했던 키 값, 써드 파티 연동 관련 파일(ex. Firebase 연동 시 필요한 파일-GoogleService-Info.plist) 들도 마찬가지로 추가해줘야 한다.
▲ 그림 12. Actions secrets 추가
이 밖에도 .gitignore
에는 추가되어 있지만 변경이 잦은 파일의 경우 Variables
에 추가해주면 좋다. Github Secrets 에 등록된 것과 차이점은 Secrets 의 경우 값을 수정할 경우 기존의 값이 보이지 않는데, Variables 에는 수정 시 기존 값을 열람할 수 있다는 장점이 있다.
따라서 Variables 에는 환경 관련된 변수들, 자체 서버 Api Endpoint 들이 명시되어 있는 파일을 관리하였다.
▲ 그림 13. Actions variables 추가
Github Actions 이 어떤 이벤트를 트리거함과 동시에 Runner 가 실행되기 위한 전제조건으로는 release
브랜치에 푸시 이벤트가 발생할 시로 설정했다. 해당 브랜치로 설정한 이유로는 우선 형상 관리 전략에 대해 먼저 이야기 해야하는데,
우리 모바일 개발팀의 경우 Git-flow 전략을 이용하고 있다. 웹 애플리케이션과 다르게 크로스플랫폼으로 개발한 앱의 경우 업데이트 시 App Store와 Play Console 의 심사 작업이 필요하고 여기서 리젝될 경우 추가 수정 작업이 요구된다. 실제로 앱 초기 출시 당시에 심사 리젝이 6번 연속으로 되어 한동안 수정 작업을 했던 경험이 있어서, 이를 고려하여 release
브랜치에서 추가 작업을 한 뒤 심사가 통과될 때 스토어에 올라온 버전으로는 main
브랜치에 병합 뒤 버전에 대한 Tag 를 남기는 방식으로 진행했었다.
따라서 해당 워크플로우는 배포를 하려고 할 때, release
브랜치에서 푸시 이벤트가 발생 시 워크플로우가 실행되는 식으로 구성했다.
name: Deploy pipeline
on:
push:
branches:
- release
iOS 와 Android 빌드 전에 우선 환경 설정부터 해야 하는데, Fastlane 이 수행되는데 사용되는 Ruby
와 Flutter
버전 명시 후 환경 설정, 빌드 초기화/패키지 다운로드 등 빌드 시 필요한 작업들을 수행해야 한다.
이러한 작업을 하도록 setup_environment
라 명명하였고, 해당 작업에서는 iOS 와 Android 공통으로 사용하는 모듈들을 설정하도록 하고, 'iOS 의 Pod 설치' 이러한 OS에 종속적인 작업들은 각 OS에서만 수행되면 되니까 분리시켜 놓았다.
또한 Actions Secrets & Variables 에 저장한 파일들(.gitignore
에 명시된)을 가져와 파일을 생성해주는 로직도 포함하고 있다. 이러한 이유는 Check Repository
에서 사용자가 해당 브랜치에 푸시한 코드들을 그대로 받아오는데, 이 때 .gitignore
를 제외한 파일들을 가져오므로 로컬과 동일한 환경을 구성하기 위해선 파일 복사 과정이 필요하다.
jobs:
setup_environment:
name: Setup Common Environment
runs-on: self-hosted
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Ruby 환경 설정
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.0.6
- name: Flutter 환경 설정
uses: subosito/flutter-action@v2
with:
flutter-version: '3.24.1'
- name: 캐시된 빌드 파일 초기화
run: flutter clean
- name: 패키지 다운로드
run: flutter pub get
- name: Gitignore 된 중요 파일 복사
run: |
# Flavor 환경 변수 파일
cat <<EOF > lib/environment.dart
${{ vars.ENVIRONMENT_CONFIG }}
EOF
# Server Endpoint 파일
cat <<EOF > lib/core/util/network/dio/paths.dart
${{ vars.SERVER_PATH }}
EOF
# [Common] Fastlane Service Account json 생성
cat <<EOF > ./gachiga-serviceAccount.json
${{ secrets.FASTLANE_SERVICE_ACCOUNT_JSON }}
EOF
# Firebase Config 복구
cat <<EOF > lib/core/util/firebase/firebase_options_dev.dart
${{ secrets.FIREBASE_OPTIONS_DEV }}
EOF
# Firebase 연동 시 필요한 json(운영 서버)
cat <<EOF > lib/core/util/firebase/firebase_options_prod.dart
${{ secrets.FIREBASE_OPTIONS_PROD }}
EOF
cat <<EOF > assets/json/firebase-service-key-prod.json
${{ secrets.FIREBASE_SERVICE_KEY_JSON }}
EOF
이번엔 iOS 빌드를 위한 환경설정 때 해야 하는 작업으로 Pod 설치, Gemfile 관련 설치 그리고 Firebase 프로젝트 연동때 사용되는 Info.plist 생성 등이 있다. 이를 수행한 작업은 다음과 같다.
deploy_ios:
name: iOS Deployment
runs-on: self-hosted
steps:
- name: Pod 설치
working-directory: ios
run: |
pod install --no-repo-update
- name: Gemfile 관련 설치
run: |
if ! gem list bundler -i > /dev/null; then
gem install bundler
fi
bundle install
- name: GoogleService-Info.plist 생성
run: |
## iOS
mkdir -p Runner/Firebase/dev
mkdir -p Runner/Firebase/prod
cat <<EOF > Runner/Firebase/dev/GoogleService-Info.plist
${{ secrets.IOS_GOOGLE_SERVICE_DEV_PLIST }}
EOF
cat <<EOF > Runner/Firebase/prod/GoogleService-Info.plist
${{ secrets.IOS_GOOGLE_SERVICE_PROD_PLIST }}
EOF
...
지금까지 빌드를 위한 설정이 모두 끝났고, Fastfile 에서 선언한 lane 을 호출해야 하는데, 우선 배포 옵션을 고려해야 한다.
Flavor 를 통해 dev(개발 환경), prod(운영 환경) 을 나누었기 때문에 개발 환경으로 내부 테스트용 TestFlight 에 배포할지, 운영 환경으로 TestFlight 에 배포할지, 운영 환경으로 스토어에 배포할지 등 옵션을 고려해야 해야 했으므로 이는 커밋 메시지에서 deploy:[배포 옵션]
를 기준으로 실행되도록 설정하였다.
커밋 메시지 예: "[커밋 메시지 내용] - deploy:1"
- name: 옵션에 따른 배포 실행
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD: ${{ secrets.FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD }}
KEY_ID: ${{ secrets.ASC_KEY_ID }}
ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
KEY_CONTENT: ${{ secrets.ASC_KEY_P8 }}
APP_IDENTIFIER: ${{ secrets.APP_IDENTIFIER }}
run: |
COMMIT_MSG=$(git log -1 --pretty=%B)
if [[ "$COMMIT_MSG" =~ deploy:[1-3] ]]; then
DEPLOY_OPTION=$(echo "$COMMIT_MSG" | grep -o 'deploy:[1-3]' | cut -d':' -f2)
else
echo "배포 옵션이 지정되지 않았습니다. 기본 옵션(1) 사용"
DEPLOY_OPTION="1"
fi
echo "선택된 배포 옵션: $DEPLOY_OPTION"
case "$DEPLOY_OPTION" in
"1")
echo "(prod) App Store Connect 배포 시작"
fastlane deploy_to_app_store flavor:prod key_id:$KEY_ID issuer_id:$ISSUER_ID key_content:$KEY_CONTENT app_identifier:$APP_IDENTIFIER
;;
"2")
echo "(prod) TestFlight 배포 시작"
fastlane deploy_to_testflight flavor:prod key_id:$KEY_ID issuer_id:$ISSUER_ID key_content:$KEY_CONTENT app_identifier:$APP_IDENTIFIER
;;
"3")
echo "(dev) TestFlight 배포 시작"
fastlane deploy_to_testflight flavor:dev key_id:$KEY_ID issuer_id:$ISSUER_ID key_content:$KEY_CONTENT app_identifier:$APP_IDENTIFIER
;;
*)
echo "Invalid deployment option selected: $DEPLOY_OPTION"
exit 1
;;
esac
TestFlight upload
▲ 그림 14. TestFlight 업로드 결과
AppStore Connect upload
▲ 그림 15. AppStore 업로드 결과
[Match]
[Github Actions Self-hosted Runner]
[Fastlane]