[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
---
|
||||
id: mobile-ci-cd-fastlane
|
||||
title: Mobile CI/CD — Fastlane / EAS / 자동 배포
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [mobile, ci, fastlane, eas, vibe-coding]
|
||||
tech_stack: { language: "Ruby / TS", applicable_to: ["iOS", "Android"] }
|
||||
applied_in: []
|
||||
aliases: [Fastlane, EAS, Bitrise, Codemagic, App Store, TestFlight, Play Console]
|
||||
---
|
||||
|
||||
# Mobile CI/CD
|
||||
|
||||
> 빌드 / 서명 / 업로드 자동화. **Fastlane = native, EAS = Expo, Codemagic / Bitrise = 매니지드**. Match 로 인증서 공유. 매 PR = 빌드, main = TestFlight + internal track.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- 인증서 / provisioning: 자동 동기 (Fastlane Match).
|
||||
- Versioning: Marketing version + build number.
|
||||
- Track / Channel: internal / beta / production.
|
||||
- Symbol upload: dSYM / mapping → Crashlytics / Sentry.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### Fastlane (iOS)
|
||||
```ruby
|
||||
# fastlane/Fastfile
|
||||
default_platform(:ios)
|
||||
|
||||
platform :ios do
|
||||
desc "Build and upload to TestFlight"
|
||||
lane :beta do
|
||||
setup_ci if ENV['CI']
|
||||
match(type: 'appstore', readonly: ENV['CI'] == 'true')
|
||||
increment_build_number(xcodeproj: 'App.xcodeproj')
|
||||
build_app(scheme: 'App', export_method: 'app-store')
|
||||
upload_to_testflight(skip_waiting_for_build_processing: true)
|
||||
upload_symbols_to_crashlytics(dsym_path: 'App.app.dSYM.zip')
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
```bash
|
||||
fastlane beta
|
||||
```
|
||||
|
||||
### Fastlane Match (인증서 git repo)
|
||||
```ruby
|
||||
# fastlane/Matchfile
|
||||
git_url 'git@github.com:acme/certs.git'
|
||||
storage_mode 'git'
|
||||
type 'appstore'
|
||||
app_identifier ['com.acme.app']
|
||||
```
|
||||
|
||||
```bash
|
||||
fastlane match appstore
|
||||
# 인증서 + provisioning 자동 sync
|
||||
```
|
||||
|
||||
### Fastlane (Android)
|
||||
```ruby
|
||||
platform :android do
|
||||
desc "Internal track"
|
||||
lane :internal do
|
||||
gradle(task: 'bundleRelease')
|
||||
upload_to_play_store(
|
||||
track: 'internal',
|
||||
aab: 'app/build/outputs/bundle/release/app-release.aab',
|
||||
)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### GitHub Actions
|
||||
```yaml
|
||||
name: ios-beta
|
||||
on:
|
||||
push: { branches: [main] }
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with: { ruby-version: '3.3', bundler-cache: true }
|
||||
- uses: actions/setup-node@v4
|
||||
- run: yarn install --immutable
|
||||
- name: Setup keychain
|
||||
env:
|
||||
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
||||
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
|
||||
run: bundle exec fastlane beta
|
||||
```
|
||||
|
||||
### EAS (Expo)
|
||||
```json
|
||||
// eas.json
|
||||
{
|
||||
"build": {
|
||||
"development": { "developmentClient": true, "distribution": "internal" },
|
||||
"preview": { "distribution": "internal", "ios": { "simulator": true } },
|
||||
"production": { "autoIncrement": true }
|
||||
},
|
||||
"submit": {
|
||||
"production": {
|
||||
"ios": { "ascAppId": "1234567890" },
|
||||
"android": { "track": "internal" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
eas build --platform all --profile production
|
||||
eas submit --platform all --profile production
|
||||
```
|
||||
|
||||
### Versioning 자동
|
||||
```ruby
|
||||
# Marketing 은 git tag 또는 package.json 에서, build = CI run number
|
||||
build = ENV['GITHUB_RUN_NUMBER'] || `git rev-list --count HEAD`.to_i
|
||||
increment_build_number(build_number: build)
|
||||
```
|
||||
|
||||
### App Store Connect API Key (auth)
|
||||
```ruby
|
||||
app_store_connect_api_key(
|
||||
key_id: ENV['ASC_KEY_ID'],
|
||||
issuer_id: ENV['ASC_ISSUER_ID'],
|
||||
key_content: ENV['ASC_KEY_CONTENT'],
|
||||
)
|
||||
```
|
||||
|
||||
session 만료 / 2FA 우회 — 권장.
|
||||
|
||||
### Symbol 업로드
|
||||
```ruby
|
||||
# iOS Crashlytics
|
||||
upload_symbols_to_crashlytics(dsym_path: 'App.app.dSYM.zip')
|
||||
|
||||
# Sentry
|
||||
sentry_upload_dif(
|
||||
auth_token: ENV['SENTRY_AUTH_TOKEN'],
|
||||
org_slug: 'acme', project_slug: 'app',
|
||||
path: './App.app.dSYM.zip',
|
||||
)
|
||||
```
|
||||
|
||||
```ruby
|
||||
# Android — Sentry CLI
|
||||
sh "sentry-cli upload-proguard --org acme --project app app/build/outputs/mapping/release/mapping.txt"
|
||||
```
|
||||
|
||||
### Beta 사용자 알림 (TestFlight + Slack)
|
||||
```ruby
|
||||
slack(
|
||||
message: "iOS beta #{build} uploaded to TestFlight",
|
||||
channel: '#mobile',
|
||||
default_payloads: [],
|
||||
)
|
||||
```
|
||||
|
||||
### Production rollout
|
||||
```yaml
|
||||
# Play Console: staged rollout
|
||||
upload_to_play_store(
|
||||
track: 'production',
|
||||
rollout: '0.1', # 10% 부터 시작
|
||||
)
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 상황 | 추천 |
|
||||
|---|---|
|
||||
| Native iOS / Android | Fastlane + GH Actions |
|
||||
| Expo 앱 | EAS Build + Submit |
|
||||
| 매니지드 / 작은 팀 | Codemagic / Bitrise |
|
||||
| Self-host runner | macOS GH Actions runner (mac mini) |
|
||||
| 큰 조직 | Bitrise / Xcode Cloud |
|
||||
| Beta 분배 (외부) | TestFlight + Firebase App Distribution |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **인증서 수동 공유 (slack)**: 잃어버림. Match.
|
||||
- **Match git repo public**: 보안. Private + encryption.
|
||||
- **Build number 수동 증가**: forget. CI 자동.
|
||||
- **dSYM / mapping 누락**: prod crash 추적 불가.
|
||||
- **2FA 수동**: CI 못 인증. App Store Connect API key.
|
||||
- **Push to main = prod 자동**: 위험. internal → manual promote.
|
||||
- **모든 PR 가 빌드**: 비용. main 만 또는 label 트리거.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- Fastlane Match + ASC API key + dSYM 자동 업로드 3종.
|
||||
- Internal → beta → production 단계.
|
||||
- staged rollout 으로 안전 점진.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Mobile_E2E_Testing]]
|
||||
- [[Native_Crash_Reporting]]
|
||||
- [[DevOps_CI_CD_Pipeline_Patterns]]
|
||||
Reference in New Issue
Block a user