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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +139 -0
  3. package/bin/project-auto-wizard.js +19 -0
  4. package/package.json +36 -0
  5. package/payload/coderabbit.yaml +19 -0
  6. package/payload/config/breaking-changes.json +1 -0
  7. package/payload/config/wizard-prompts.yml +62 -0
  8. package/payload/scripts/changelog_manager.py +726 -0
  9. package/payload/scripts/version_manager.py +617 -0
  10. package/payload/version.yml.template +48 -0
  11. package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +303 -0
  12. package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +293 -0
  13. package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +289 -0
  14. package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +192 -0
  15. package/payload/workflows/common/secret-backup/PROJECT-COMMON-SECRET-FILE-UPLOAD.yaml +209 -0
  16. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +591 -0
  17. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +700 -0
  18. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +308 -0
  19. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +992 -0
  20. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +689 -0
  21. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +987 -0
  22. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +471 -0
  23. package/payload/workflows/flutter/PROJECT-FLUTTER-SUH-LAB-APP-BUILD-TRIGGER.yaml +500 -0
  24. package/payload/workflows/next/PROJECT-NEXT-CI.yaml +185 -0
  25. package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +271 -0
  26. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +81 -0
  27. package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +2191 -0
  28. package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +386 -0
  29. package/payload/workflows/react/PROJECT-REACT-CI.yaml +194 -0
  30. package/payload/workflows/react/PROJECT-REACT-CICD.yaml +255 -0
  31. package/payload/workflows/spring/PROJECT-SPRING-GITHUB-PACKAGES-PUBLISH.yml +84 -0
  32. package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-CI.yml +316 -0
  33. package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-PUBLISH.yml +58 -0
  34. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +535 -0
  35. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +437 -0
  36. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +2277 -0
  37. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +425 -0
  38. package/src/cli/args.js +95 -0
  39. package/src/cli/help.js +27 -0
  40. package/src/commands/full.js +53 -0
  41. package/src/commands/interactive.js +276 -0
  42. package/src/commands/revert.js +60 -0
  43. package/src/commands/version.js +31 -0
  44. package/src/commands/workflows.js +49 -0
  45. package/src/context.js +26 -0
  46. package/src/core/assets.js +47 -0
  47. package/src/core/branches.js +60 -0
  48. package/src/core/branding.js +15 -0
  49. package/src/core/breaking-check.js +65 -0
  50. package/src/core/breaking.js +26 -0
  51. package/src/core/copy/coderabbit.js +26 -0
  52. package/src/core/copy/gitignore.js +55 -0
  53. package/src/core/copy/readme.js +30 -0
  54. package/src/core/copy/simple.js +23 -0
  55. package/src/core/copy/workflows.js +233 -0
  56. package/src/core/detect-fs.js +106 -0
  57. package/src/core/detect.js +62 -0
  58. package/src/core/fsutil.js +46 -0
  59. package/src/core/options-ask.js +99 -0
  60. package/src/core/paths-resolve.js +261 -0
  61. package/src/core/paths.js +16 -0
  62. package/src/core/version-yml.js +190 -0
  63. package/src/core/wizard-env.js +101 -0
  64. package/src/core/wizard-labels.js +107 -0
  65. package/src/index.js +162 -0
  66. package/src/ui/ansi.js +26 -0
  67. package/src/ui/banner.js +29 -0
  68. package/src/ui/env-plan.js +207 -0
  69. package/src/ui/prompts.js +99 -0
  70. package/src/ui/readline-engine.js +257 -0
  71. package/src/ui/status-cards.js +56 -0
  72. 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 "로그를 확인해주세요."