project-auto-wizard 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/bin/project-auto-wizard.js +19 -0
- package/package.json +36 -0
- package/payload/coderabbit.yaml +19 -0
- package/payload/config/breaking-changes.json +1 -0
- package/payload/config/wizard-prompts.yml +62 -0
- package/payload/scripts/changelog_manager.py +726 -0
- package/payload/scripts/version_manager.py +617 -0
- package/payload/version.yml.template +48 -0
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +303 -0
- package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +293 -0
- package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +289 -0
- package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +192 -0
- package/payload/workflows/common/secret-backup/PROJECT-COMMON-SECRET-FILE-UPLOAD.yaml +209 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +591 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +700 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +308 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +992 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +689 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +987 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +471 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-SUH-LAB-APP-BUILD-TRIGGER.yaml +500 -0
- package/payload/workflows/next/PROJECT-NEXT-CI.yaml +185 -0
- package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +271 -0
- package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +81 -0
- package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +2191 -0
- package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +386 -0
- package/payload/workflows/react/PROJECT-REACT-CI.yaml +194 -0
- package/payload/workflows/react/PROJECT-REACT-CICD.yaml +255 -0
- package/payload/workflows/spring/PROJECT-SPRING-GITHUB-PACKAGES-PUBLISH.yml +84 -0
- package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-CI.yml +316 -0
- package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-PUBLISH.yml +58 -0
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +535 -0
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +437 -0
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +2277 -0
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +425 -0
- package/src/cli/args.js +95 -0
- package/src/cli/help.js +27 -0
- package/src/commands/full.js +53 -0
- package/src/commands/interactive.js +276 -0
- package/src/commands/revert.js +60 -0
- package/src/commands/version.js +31 -0
- package/src/commands/workflows.js +49 -0
- package/src/context.js +26 -0
- package/src/core/assets.js +47 -0
- package/src/core/branches.js +60 -0
- package/src/core/branding.js +15 -0
- package/src/core/breaking-check.js +65 -0
- package/src/core/breaking.js +26 -0
- package/src/core/copy/coderabbit.js +26 -0
- package/src/core/copy/gitignore.js +55 -0
- package/src/core/copy/readme.js +30 -0
- package/src/core/copy/simple.js +23 -0
- package/src/core/copy/workflows.js +233 -0
- package/src/core/detect-fs.js +106 -0
- package/src/core/detect.js +62 -0
- package/src/core/fsutil.js +46 -0
- package/src/core/options-ask.js +99 -0
- package/src/core/paths-resolve.js +261 -0
- package/src/core/paths.js +16 -0
- package/src/core/version-yml.js +190 -0
- package/src/core/wizard-env.js +101 -0
- package/src/core/wizard-labels.js +107 -0
- package/src/index.js +162 -0
- package/src/ui/ansi.js +26 -0
- package/src/ui/banner.js +29 -0
- package/src/ui/env-plan.js +207 -0
- package/src/ui/prompts.js +99 -0
- package/src/ui/readline-engine.js +257 -0
- package/src/ui/status-cards.js +56 -0
- package/src/ui/summary.js +127 -0
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
# ===================================================================
|
|
2
|
+
# Flutter iOS TestFlight 자동 배포 워크플로우
|
|
3
|
+
# ===================================================================
|
|
4
|
+
#
|
|
5
|
+
# 이 워크플로우는 Flutter iOS 앱을 빌드하여 TestFlight에 자동 배포합니다.
|
|
6
|
+
#
|
|
7
|
+
# ★ 마법사 우선 아키텍처 ★
|
|
8
|
+
# - 빌드에 필요한 설정 파일들은 웹 마법사가 생성합니다
|
|
9
|
+
# - 워크플로우는 마법사가 생성한 파일들을 그대로 사용합니다
|
|
10
|
+
# - 마법사 경로: .github/util/flutter/ios-testflight-setup-wizard/index.html
|
|
11
|
+
# (브라우저에서 열어서 사용)
|
|
12
|
+
#
|
|
13
|
+
# 빌드 파이프라인:
|
|
14
|
+
# 1. flutter build ios --no-codesign (Flutter 빌드)
|
|
15
|
+
# 2. xcodebuild archive (Xcode 아카이브 생성)
|
|
16
|
+
# 3. xcodebuild -exportArchive (IPA 생성, ExportOptions.plist 사용)
|
|
17
|
+
# 4. fastlane deploy (마법사 생성 Fastfile 사용, DEPLOY_MODE에 따라 TestFlight/심사 제출)
|
|
18
|
+
#
|
|
19
|
+
# ===================================================================
|
|
20
|
+
# 🔑 필수 GitHub Secrets
|
|
21
|
+
# ===================================================================
|
|
22
|
+
#
|
|
23
|
+
# 🔐 Apple 인증서 & 프로비저닝 프로파일:
|
|
24
|
+
# APPLE_CERTIFICATE_BASE64: .p12 인증서 (base64 인코딩)
|
|
25
|
+
# APPLE_CERTIFICATE_PASSWORD: .p12 인증서 비밀번호
|
|
26
|
+
# APPLE_PROVISIONING_PROFILE_BASE64: .mobileprovision 파일 (base64 인코딩)
|
|
27
|
+
# IOS_PROVISIONING_PROFILE_NAME: 프로비저닝 프로파일 이름
|
|
28
|
+
#
|
|
29
|
+
# 🔑 App Store Connect API:
|
|
30
|
+
# APP_STORE_CONNECT_API_KEY_ID: API Key ID (10자리)
|
|
31
|
+
# APP_STORE_CONNECT_ISSUER_ID: Issuer ID (UUID 형식)
|
|
32
|
+
# APP_STORE_CONNECT_API_KEY_BASE64: AuthKey_XXXXXX.p8 파일 (base64 인코딩)
|
|
33
|
+
#
|
|
34
|
+
# 📝 환경 설정 (선택):
|
|
35
|
+
# ENV_FILE (선택): .env 파일 내용
|
|
36
|
+
# SECRETS_XCCONFIG (선택): ios/Flutter/Secrets.xcconfig 내용
|
|
37
|
+
#
|
|
38
|
+
# ===================================================================
|
|
39
|
+
# 🛠️ 초기 설정 방법
|
|
40
|
+
# ===================================================================
|
|
41
|
+
#
|
|
42
|
+
# 1. 웹 마법사 실행:
|
|
43
|
+
# 브라우저에서 .github/util/flutter/ios-testflight-setup-wizard/index.html 열기
|
|
44
|
+
# → 필요한 정보 입력 후 설정 파일 다운로드
|
|
45
|
+
#
|
|
46
|
+
# 2. GitHub Secrets 설정 (위 목록 참고)
|
|
47
|
+
#
|
|
48
|
+
# 3. 생성된 파일 커밋:
|
|
49
|
+
# git add ios/
|
|
50
|
+
# git commit -m "chore: iOS TestFlight 배포 설정"
|
|
51
|
+
#
|
|
52
|
+
# 4. {{MAIN_BRANCH}} 브랜치로 푸시하여 배포 시작
|
|
53
|
+
#
|
|
54
|
+
# ===================================================================
|
|
55
|
+
|
|
56
|
+
name: PROJECT-iOS-TestFlight-Deploy
|
|
57
|
+
|
|
58
|
+
on:
|
|
59
|
+
push:
|
|
60
|
+
branches: ["{{MAIN_BRANCH}}"]
|
|
61
|
+
workflow_dispatch:
|
|
62
|
+
inputs:
|
|
63
|
+
deploy_mode:
|
|
64
|
+
description: "배포 모드 (store_only=TestFlight까지 / store_prepare=ASC 제출직전 / store_submit=심사 자동제출)"
|
|
65
|
+
required: false
|
|
66
|
+
default: "store_only"
|
|
67
|
+
type: choice
|
|
68
|
+
options:
|
|
69
|
+
- store_only
|
|
70
|
+
- store_prepare
|
|
71
|
+
- store_submit
|
|
72
|
+
|
|
73
|
+
# ============================================
|
|
74
|
+
# 🔧 프로젝트별 설정 (아래 값들을 수정하세요)
|
|
75
|
+
# ============================================
|
|
76
|
+
env:
|
|
77
|
+
FLUTTER_ROOT: "." # @wizard auto:flutter-root
|
|
78
|
+
FLUTTER_VERSION: "3.35.5"
|
|
79
|
+
XCODE_VERSION: "26.0"
|
|
80
|
+
PROJECT_TYPE: "flutter"
|
|
81
|
+
|
|
82
|
+
# ============================================
|
|
83
|
+
# 📝 환경 파일 설정
|
|
84
|
+
# ============================================
|
|
85
|
+
# .env 파일 생성 경로 (프로젝트 루트 기준)
|
|
86
|
+
ENV_FILE_PATH: ".env"
|
|
87
|
+
|
|
88
|
+
# ============================================
|
|
89
|
+
# 📝 TestFlight Changelog 설정
|
|
90
|
+
# ============================================
|
|
91
|
+
SKIP_WAITING_FOR_BUILD_PROCESSING: "false"
|
|
92
|
+
|
|
93
|
+
# ============================================
|
|
94
|
+
# 📝 배포 모드 (store_only | store_prepare | store_submit)
|
|
95
|
+
# ============================================
|
|
96
|
+
DEPLOY_MODE: ${{ github.event.inputs.deploy_mode || vars.IOS_DEPLOY_MODE || 'store_only' }}
|
|
97
|
+
|
|
98
|
+
jobs:
|
|
99
|
+
prepare-build:
|
|
100
|
+
name: 환경 설정 및 준비
|
|
101
|
+
runs-on: macos-26
|
|
102
|
+
defaults:
|
|
103
|
+
run:
|
|
104
|
+
working-directory: ${{ env.FLUTTER_ROOT }}
|
|
105
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
|
|
106
|
+
|
|
107
|
+
outputs:
|
|
108
|
+
version: ${{ steps.current_version.outputs.version }}
|
|
109
|
+
build_number: ${{ steps.current_version.outputs.build_number }}
|
|
110
|
+
project_type: ${{ steps.current_version.outputs.project_type }}
|
|
111
|
+
|
|
112
|
+
steps:
|
|
113
|
+
- name: Checkout repository
|
|
114
|
+
uses: actions/checkout@v5
|
|
115
|
+
with:
|
|
116
|
+
ref: main
|
|
117
|
+
fetch-depth: 0
|
|
118
|
+
|
|
119
|
+
- name: Pull latest changes
|
|
120
|
+
run: git pull origin main
|
|
121
|
+
|
|
122
|
+
- name: Select Xcode version
|
|
123
|
+
run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
124
|
+
|
|
125
|
+
- name: Create .env file
|
|
126
|
+
run: |
|
|
127
|
+
cat << 'EOF' > ${{ env.ENV_FILE_PATH }}
|
|
128
|
+
${{ secrets.ENV_FILE || secrets.ENV }}
|
|
129
|
+
EOF
|
|
130
|
+
echo "✅ ${{ env.ENV_FILE_PATH }} file created"
|
|
131
|
+
|
|
132
|
+
- name: Create Secrets.xcconfig file
|
|
133
|
+
run: |
|
|
134
|
+
mkdir -p ios/Flutter
|
|
135
|
+
if [ -n "${{ secrets.SECRETS_XCCONFIG }}" ]; then
|
|
136
|
+
echo "${{ secrets.SECRETS_XCCONFIG }}" > ios/Flutter/Secrets.xcconfig
|
|
137
|
+
echo "Secrets.xcconfig created"
|
|
138
|
+
else
|
|
139
|
+
echo "// No secrets provided" > ios/Flutter/Secrets.xcconfig
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
- name: Set up Flutter
|
|
143
|
+
uses: subosito/flutter-action@v2
|
|
144
|
+
with:
|
|
145
|
+
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
146
|
+
cache: true
|
|
147
|
+
|
|
148
|
+
- name: Cache Flutter dependencies
|
|
149
|
+
uses: actions/cache@v4
|
|
150
|
+
with:
|
|
151
|
+
path: ~/.pub-cache
|
|
152
|
+
key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
|
|
153
|
+
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
154
|
+
|
|
155
|
+
- name: Install dependencies
|
|
156
|
+
run: flutter pub get
|
|
157
|
+
|
|
158
|
+
- name: Set up Ruby
|
|
159
|
+
uses: ruby/setup-ruby@v1
|
|
160
|
+
with:
|
|
161
|
+
ruby-version: "3.1"
|
|
162
|
+
bundler-cache: true
|
|
163
|
+
|
|
164
|
+
- name: Install CocoaPods
|
|
165
|
+
run: |
|
|
166
|
+
gem install cocoapods
|
|
167
|
+
cd ios && pod install
|
|
168
|
+
|
|
169
|
+
- name: 현재 버전 정보 가져오기
|
|
170
|
+
id: current_version
|
|
171
|
+
working-directory: ${{ github.workspace }} # version_manager.py가 레포 루트 version.yml을 읽어야 함
|
|
172
|
+
run: |
|
|
173
|
+
VERSION=$(python3 .github/scripts/version_manager.py get | tail -n 1)
|
|
174
|
+
BUILD_NUMBER=$(python3 .github/scripts/version_manager.py get-code | tail -n 1)
|
|
175
|
+
|
|
176
|
+
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
177
|
+
echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
|
|
178
|
+
echo "project_type=${{ env.PROJECT_TYPE }}" >> $GITHUB_OUTPUT
|
|
179
|
+
|
|
180
|
+
echo "📋 버전: $VERSION (빌드번호: $BUILD_NUMBER)"
|
|
181
|
+
|
|
182
|
+
- name: 릴리즈 노트 생성
|
|
183
|
+
id: release_notes
|
|
184
|
+
working-directory: ${{ github.workspace }} # CHANGELOG.json·changelog_manager.py·final_release_notes.txt(upload 짝)는 레포 루트 기준
|
|
185
|
+
run: |
|
|
186
|
+
VERSION="${{ steps.current_version.outputs.version }}"
|
|
187
|
+
|
|
188
|
+
if [ -f "CHANGELOG.json" ]; then
|
|
189
|
+
python3 ./.github/scripts/changelog_manager.py generate-md
|
|
190
|
+
python3 ./.github/scripts/changelog_manager.py export --version $VERSION --output final_release_notes.txt
|
|
191
|
+
|
|
192
|
+
if [ -s final_release_notes.txt ]; then
|
|
193
|
+
echo "RELEASE_NOTES_FOUND=true" >> $GITHUB_ENV
|
|
194
|
+
else
|
|
195
|
+
echo "RELEASE_NOTES_FOUND=false" >> $GITHUB_ENV
|
|
196
|
+
echo "v$VERSION 업데이트" > final_release_notes.txt
|
|
197
|
+
fi
|
|
198
|
+
else
|
|
199
|
+
echo "RELEASE_NOTES_FOUND=false" >> $GITHUB_ENV
|
|
200
|
+
echo "v$VERSION 업데이트" > final_release_notes.txt
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
- name: Upload release notes
|
|
204
|
+
uses: actions/upload-artifact@v4
|
|
205
|
+
with:
|
|
206
|
+
name: release-notes
|
|
207
|
+
path: final_release_notes.txt
|
|
208
|
+
retention-days: 1
|
|
209
|
+
|
|
210
|
+
- name: Upload project files
|
|
211
|
+
uses: actions/upload-artifact@v4
|
|
212
|
+
with:
|
|
213
|
+
name: project-files
|
|
214
|
+
path: |
|
|
215
|
+
${{ env.ENV_FILE_PATH }}
|
|
216
|
+
ios/Flutter/Secrets.xcconfig
|
|
217
|
+
pubspec.yaml
|
|
218
|
+
lib/
|
|
219
|
+
assets/
|
|
220
|
+
retention-days: 1
|
|
221
|
+
|
|
222
|
+
# ============================================
|
|
223
|
+
# iOS 빌드 (xcodebuild 직접 사용)
|
|
224
|
+
# ============================================
|
|
225
|
+
build-ios:
|
|
226
|
+
name: iOS 앱 빌드
|
|
227
|
+
runs-on: macos-26
|
|
228
|
+
defaults:
|
|
229
|
+
run:
|
|
230
|
+
working-directory: ${{ env.FLUTTER_ROOT }}
|
|
231
|
+
needs: prepare-build
|
|
232
|
+
|
|
233
|
+
steps:
|
|
234
|
+
- name: Checkout repository
|
|
235
|
+
uses: actions/checkout@v5
|
|
236
|
+
with:
|
|
237
|
+
ref: main
|
|
238
|
+
fetch-depth: 0
|
|
239
|
+
|
|
240
|
+
- name: Pull latest changes
|
|
241
|
+
run: git pull origin main
|
|
242
|
+
|
|
243
|
+
- name: Select Xcode version
|
|
244
|
+
run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
245
|
+
|
|
246
|
+
- name: Install iOS device platform
|
|
247
|
+
env:
|
|
248
|
+
# xcode-select만으로는 서브프로세스에 전파되지 않아 명시적으로 지정
|
|
249
|
+
DEVELOPER_DIR: /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
250
|
+
run: |
|
|
251
|
+
echo "=== iPhoneOS.platform check ==="
|
|
252
|
+
ls "$DEVELOPER_DIR/Platforms/iPhoneOS.platform/" 2>/dev/null && echo "iPhoneOS.platform EXISTS" || echo "iPhoneOS.platform NOT FOUND"
|
|
253
|
+
echo "=== Installing MobileDevice packages ==="
|
|
254
|
+
sudo installer -pkg /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Resources/Packages/MobileDevice.pkg -target /
|
|
255
|
+
sudo installer -pkg /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Resources/Packages/MobileDeviceDevelopment.pkg -target /
|
|
256
|
+
echo "=== Accepting Xcode license ==="
|
|
257
|
+
sudo xcodebuild -license accept
|
|
258
|
+
echo "=== Initializing Xcode components ==="
|
|
259
|
+
xcrun simctl list >/dev/null 2>&1 || true
|
|
260
|
+
echo "=== Downloading iOS platform ==="
|
|
261
|
+
xcodebuild -downloadPlatform iOS
|
|
262
|
+
|
|
263
|
+
- name: Download project files
|
|
264
|
+
uses: actions/download-artifact@v4
|
|
265
|
+
with:
|
|
266
|
+
name: project-files
|
|
267
|
+
path: .
|
|
268
|
+
|
|
269
|
+
- name: Ensure .env file exists
|
|
270
|
+
run: |
|
|
271
|
+
if [ ! -f "${{ env.ENV_FILE_PATH }}" ]; then
|
|
272
|
+
cat << 'EOF' > ${{ env.ENV_FILE_PATH }}
|
|
273
|
+
${{ secrets.ENV_FILE || secrets.ENV }}
|
|
274
|
+
EOF
|
|
275
|
+
echo "✅ ${{ env.ENV_FILE_PATH }} file created (fallback)"
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
- name: Set up Flutter
|
|
279
|
+
uses: subosito/flutter-action@v2
|
|
280
|
+
with:
|
|
281
|
+
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
282
|
+
cache: true
|
|
283
|
+
|
|
284
|
+
- name: Cache Flutter dependencies
|
|
285
|
+
uses: actions/cache@v4
|
|
286
|
+
with:
|
|
287
|
+
path: ~/.pub-cache
|
|
288
|
+
key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
|
|
289
|
+
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
290
|
+
|
|
291
|
+
- name: Install dependencies
|
|
292
|
+
run: flutter pub get
|
|
293
|
+
|
|
294
|
+
- name: Set up Ruby
|
|
295
|
+
uses: ruby/setup-ruby@v1
|
|
296
|
+
with:
|
|
297
|
+
ruby-version: "3.1"
|
|
298
|
+
bundler-cache: true
|
|
299
|
+
|
|
300
|
+
- name: Install CocoaPods
|
|
301
|
+
run: |
|
|
302
|
+
gem install cocoapods
|
|
303
|
+
cd ios && pod install
|
|
304
|
+
|
|
305
|
+
- name: Import Code-Signing Certificates
|
|
306
|
+
uses: Apple-Actions/import-codesign-certs@v2
|
|
307
|
+
with:
|
|
308
|
+
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
|
|
309
|
+
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
|
310
|
+
|
|
311
|
+
- name: Install Provisioning Profile
|
|
312
|
+
run: |
|
|
313
|
+
echo "${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}" | base64 --decode > profile.mobileprovision
|
|
314
|
+
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
|
315
|
+
cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
|
|
316
|
+
uuid=$(grep -A1 -a "UUID" profile.mobileprovision | grep string | sed -e "s/<string>//" -e "s/<\/string>//" -e "s/[[:space:]]//g")
|
|
317
|
+
cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/$uuid.mobileprovision
|
|
318
|
+
|
|
319
|
+
- name: Verify ExportOptions.plist
|
|
320
|
+
run: |
|
|
321
|
+
cd ios
|
|
322
|
+
if [ ! -f "ExportOptions.plist" ]; then
|
|
323
|
+
exit 1
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
- name: Flutter build (no codesign)
|
|
327
|
+
env:
|
|
328
|
+
# xcode-select만으로는 서브프로세스에 전파되지 않아 명시적으로 지정
|
|
329
|
+
DEVELOPER_DIR: /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
330
|
+
run: |
|
|
331
|
+
flutter build ios --release --no-codesign \
|
|
332
|
+
--build-name="${{ needs.prepare-build.outputs.version }}" \
|
|
333
|
+
--build-number="${{ needs.prepare-build.outputs.build_number }}"
|
|
334
|
+
|
|
335
|
+
- name: Create Archive
|
|
336
|
+
env:
|
|
337
|
+
IOS_PROVISIONING_PROFILE_NAME: ${{ secrets.IOS_PROVISIONING_PROFILE_NAME }}
|
|
338
|
+
# xcode-select만으로는 서브프로세스에 전파되지 않아 명시적으로 지정
|
|
339
|
+
DEVELOPER_DIR: /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
340
|
+
run: |
|
|
341
|
+
cd ios
|
|
342
|
+
xcodebuild -workspace Runner.xcworkspace \
|
|
343
|
+
-scheme Runner \
|
|
344
|
+
-archivePath build/Runner.xcarchive \
|
|
345
|
+
-destination 'generic/platform=iOS' \
|
|
346
|
+
archive \
|
|
347
|
+
CODE_SIGN_STYLE=Manual \
|
|
348
|
+
PROVISIONING_PROFILE_SPECIFIER="$IOS_PROVISIONING_PROFILE_NAME" \
|
|
349
|
+
CODE_SIGN_IDENTITY="Apple Distribution"
|
|
350
|
+
|
|
351
|
+
- name: Export IPA
|
|
352
|
+
run: |
|
|
353
|
+
cd ios
|
|
354
|
+
xcodebuild -exportArchive \
|
|
355
|
+
-archivePath build/Runner.xcarchive \
|
|
356
|
+
-exportPath build/ipa \
|
|
357
|
+
-exportOptionsPlist ExportOptions.plist
|
|
358
|
+
|
|
359
|
+
- name: Upload IPA artifact
|
|
360
|
+
uses: actions/upload-artifact@v4
|
|
361
|
+
with:
|
|
362
|
+
name: ios-ipa
|
|
363
|
+
path: ${{ env.FLUTTER_ROOT }}/ios/build/ipa/*.ipa
|
|
364
|
+
retention-days: 1
|
|
365
|
+
|
|
366
|
+
# ============================================
|
|
367
|
+
# TestFlight 배포 (마법사 생성 Fastfile 사용)
|
|
368
|
+
# ============================================
|
|
369
|
+
deploy-testflight:
|
|
370
|
+
name: TestFlight 배포
|
|
371
|
+
runs-on: macos-26
|
|
372
|
+
defaults:
|
|
373
|
+
run:
|
|
374
|
+
working-directory: ${{ env.FLUTTER_ROOT }}
|
|
375
|
+
needs: [prepare-build, build-ios]
|
|
376
|
+
|
|
377
|
+
steps:
|
|
378
|
+
- name: Checkout repository
|
|
379
|
+
uses: actions/checkout@v5
|
|
380
|
+
with:
|
|
381
|
+
ref: main
|
|
382
|
+
fetch-depth: 0
|
|
383
|
+
|
|
384
|
+
- name: Setup App Store Connect API Key
|
|
385
|
+
run: |
|
|
386
|
+
mkdir -p ~/.appstoreconnect/private_keys
|
|
387
|
+
echo "${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}" | base64 --decode > ~/.appstoreconnect/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8
|
|
388
|
+
|
|
389
|
+
- name: Download IPA artifact
|
|
390
|
+
uses: actions/download-artifact@v4
|
|
391
|
+
with:
|
|
392
|
+
name: ios-ipa
|
|
393
|
+
path: ${{ env.FLUTTER_ROOT }}/ios/build/ipa/
|
|
394
|
+
|
|
395
|
+
- name: Download release notes
|
|
396
|
+
uses: actions/download-artifact@v4
|
|
397
|
+
with:
|
|
398
|
+
name: release-notes
|
|
399
|
+
path: .
|
|
400
|
+
|
|
401
|
+
- name: Verify Fastfile exists
|
|
402
|
+
run: |
|
|
403
|
+
if [ ! -f "ios/fastlane/Fastfile" ]; then
|
|
404
|
+
echo "❌ ios/fastlane/Fastfile이 없습니다!"
|
|
405
|
+
echo "웹 마법사를 실행하여 설정 파일을 생성하세요:"
|
|
406
|
+
echo " 브라우저에서 .github/util/flutter/ios-testflight-setup-wizard/index.html 열기"
|
|
407
|
+
exit 1
|
|
408
|
+
fi
|
|
409
|
+
echo "✅ Fastfile 확인됨: ios/fastlane/Fastfile"
|
|
410
|
+
|
|
411
|
+
- name: Install Fastlane
|
|
412
|
+
working-directory: ${{ env.FLUTTER_ROOT }}/ios
|
|
413
|
+
run: |
|
|
414
|
+
# multi_json: google-apis transitive 의존성이 선언 누락한 upstream 버그 회피 (gemspec 미선언 → Gem::LoadError)
|
|
415
|
+
printf 'source "https://rubygems.org"\ngem "fastlane"\ngem "multi_json"\n' > Gemfile
|
|
416
|
+
bundle install
|
|
417
|
+
echo "✅ Fastlane installed (Bundler)"
|
|
418
|
+
|
|
419
|
+
- name: Upload to TestFlight (+ App Store Submit if store_submit)
|
|
420
|
+
env:
|
|
421
|
+
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
|
422
|
+
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
|
|
423
|
+
SKIP_WAITING_FOR_BUILD_PROCESSING: ${{ env.SKIP_WAITING_FOR_BUILD_PROCESSING }}
|
|
424
|
+
run: |
|
|
425
|
+
# IPA 절대경로 탐색
|
|
426
|
+
IPA_PATH=$(find "$GITHUB_WORKSPACE/${FLUTTER_ROOT}/ios/build/ipa" -name "*.ipa" | head -1)
|
|
427
|
+
echo "📦 Found IPA at: $IPA_PATH"
|
|
428
|
+
|
|
429
|
+
if [ ! -f "$IPA_PATH" ]; then
|
|
430
|
+
echo "❌ IPA 파일을 찾을 수 없습니다!"; ls -la ios/build/ipa/ || true; exit 1
|
|
431
|
+
fi
|
|
432
|
+
|
|
433
|
+
# Release notes 준비 (final_release_notes.txt·truncate 스크립트는 레포 루트 기준 — working-directory가 FLUTTER_ROOT이므로 절대경로 사용)
|
|
434
|
+
if [ -f "$GITHUB_WORKSPACE/final_release_notes.txt" ]; then
|
|
435
|
+
# App Store Connect "What to Test" 한도(4000 바이트)에 맞춰 절단 (마진 두어 3800byte)
|
|
436
|
+
bash "$GITHUB_WORKSPACE/.github/scripts/truncate_release_notes.sh" "$GITHUB_WORKSPACE/final_release_notes.txt" 3800 byte
|
|
437
|
+
RELEASE_NOTES=$(cat "$GITHUB_WORKSPACE/final_release_notes.txt")
|
|
438
|
+
echo "📝 Release Notes:"
|
|
439
|
+
echo "$RELEASE_NOTES"
|
|
440
|
+
else
|
|
441
|
+
RELEASE_NOTES="v${{ needs.prepare-build.outputs.version }} 업데이트"
|
|
442
|
+
echo "📝 기본 Release Notes: $RELEASE_NOTES"
|
|
443
|
+
fi
|
|
444
|
+
|
|
445
|
+
export APP_STORE_CONNECT_API_KEY_ID="${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}"
|
|
446
|
+
export APP_STORE_CONNECT_ISSUER_ID="${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}"
|
|
447
|
+
export API_KEY_PATH="$HOME/.appstoreconnect/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8"
|
|
448
|
+
export IPA_PATH="$IPA_PATH"
|
|
449
|
+
export RELEASE_NOTES="$RELEASE_NOTES"
|
|
450
|
+
export APP_IDENTIFIER="${{ secrets.IOS_BUNDLE_ID || vars.IOS_BUNDLE_ID }}"
|
|
451
|
+
export DEPLOY_MODE="${{ env.DEPLOY_MODE }}"
|
|
452
|
+
export APP_VERSION="${{ needs.prepare-build.outputs.version }}"
|
|
453
|
+
export BUILD_NUMBER="${{ needs.prepare-build.outputs.build_number }}"
|
|
454
|
+
|
|
455
|
+
echo "🔧 DEPLOY_MODE: $DEPLOY_MODE / APP_IDENTIFIER: $APP_IDENTIFIER"
|
|
456
|
+
cd ios
|
|
457
|
+
bundle exec fastlane deploy
|
|
458
|
+
|
|
459
|
+
- name: Notify TestFlight Upload Success
|
|
460
|
+
if: success()
|
|
461
|
+
run: |
|
|
462
|
+
echo "✅ TestFlight 업로드 성공!"
|
|
463
|
+
echo "📱 버전: ${{ needs.prepare-build.outputs.version }}"
|
|
464
|
+
echo "🔢 빌드번호: ${{ needs.prepare-build.outputs.build_number }}"
|
|
465
|
+
echo "📝 커밋: ${{ github.sha }}"
|
|
466
|
+
|
|
467
|
+
- name: Notify on Failure
|
|
468
|
+
if: failure()
|
|
469
|
+
run: |
|
|
470
|
+
echo "❌ TestFlight 업로드 실패!"
|
|
471
|
+
echo "로그를 확인해주세요."
|