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,591 @@
1
+ # ===================================================================
2
+ # Flutter Android Firebase App Distribution 배포
3
+ # ===================================================================
4
+ #
5
+ # {{MAIN_BRANCH}} 브랜치 푸시 시 Android AAB를 빌드하고
6
+ # Firebase App Distribution에 자동 배포합니다.
7
+ #
8
+ # PlayStore CICD와 동일한 빌드 과정(AAB + 버전 검증)을 거친 후
9
+ # Play Store 대신 Firebase App Distribution에 업로드합니다.
10
+ #
11
+ # ===================================================================
12
+ # 🔑 필수 GitHub Secrets
13
+ # ===================================================================
14
+ #
15
+ # 🔐 Release Keystore:
16
+ # RELEASE_KEYSTORE_BASE64: 서명용 keystore (base64)
17
+ # RELEASE_KEYSTORE_PASSWORD: keystore 비밀번호
18
+ # RELEASE_KEY_ALIAS: key alias
19
+ # RELEASE_KEY_PASSWORD: key 비밀번호
20
+ #
21
+ # 🔥 Firebase:
22
+ # FIREBASE_SERVICE_ACCOUNT_JSON_BASE64: Firebase 서비스 계정 JSON (base64)
23
+ #
24
+ # 📝 환경 설정 (선택):
25
+ # ENV_FILE 또는 ENV: .env 파일 내용 (둘 중 하나, ENV_FILE 우선)
26
+ # GOOGLE_SERVICES_JSON: Firebase google-services.json
27
+ #
28
+ # 🪄 빠른 설정: .github/util/flutter/firebase-wizard/firebase-wizard.html을
29
+ # 브라우저에서 열어 5단계 마법사로 Firebase 배포를 자동 설정할 수 있습니다.
30
+ #
31
+ # ===================================================================
32
+ #
33
+ # 🔧 프로젝트별 설정 (env 섹션에서 수정)
34
+ # ===================================================================
35
+ # APP_ARTIFACT_NAME : 프로젝트 이름
36
+ # FLUTTER_VERSION : Flutter SDK 버전
37
+ # JAVA_VERSION : Java 버전
38
+ # FIREBASE_APP_ID : Firebase 앱 ID (Firebase Console에서 확인)
39
+ # FIREBASE_TESTER_GROUP : Firebase 테스터 그룹명
40
+ # ===================================================================
41
+ #
42
+ # 🛠️ 사전 설정:
43
+ # 1. Firebase Console → App Distribution → 테스터 그룹 생성
44
+ # 2. Firebase 서비스 계정 JSON 생성 (base64 인코딩)
45
+ # 3. GitHub Secrets에 FIREBASE_SERVICE_ACCOUNT_JSON_BASE64 등록
46
+ # 4. env 섹션의 FIREBASE_APP_ID, FIREBASE_TESTER_GROUP 수정
47
+ #
48
+ # ===================================================================
49
+
50
+ name: PROJECT-FLUTTER-ANDROID-FIREBASE-APPDIST-CICD
51
+
52
+ on:
53
+ push:
54
+ branches: ["{{MAIN_BRANCH}}"]
55
+ workflow_dispatch:
56
+
57
+ env:
58
+ # ============================================
59
+ # 🔧 프로젝트별 설정 (아래 값들을 수정하세요)
60
+ # ============================================
61
+ # 프로젝트 이름
62
+ APP_ARTIFACT_NAME: "__APP_ARTIFACT_NAME__" # @wizard ask:@repo
63
+ # Flutter/Java 버전
64
+ FLUTTER_VERSION: "3.35.5"
65
+ JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:17
66
+ PROJECT_TYPE: "flutter"
67
+ # Firebase App Distribution 설정
68
+ FIREBASE_APP_ID: "your-firebase-app-id"
69
+ FIREBASE_TESTER_GROUP: "testers"
70
+
71
+ jobs:
72
+ prepare-build:
73
+ name: 환경 설정 및 준비
74
+ runs-on: ubuntu-latest
75
+ # CHANGELOG 워크플로우가 성공한 경우에만 실행
76
+ if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
77
+
78
+ outputs:
79
+ version: ${{ steps.current_version.outputs.version }}
80
+ version_code: ${{ steps.current_version.outputs.version_code }}
81
+ project_type: ${{ steps.current_version.outputs.project_type }}
82
+
83
+ steps:
84
+ - name: Checkout repository
85
+ uses: actions/checkout@v5
86
+ with:
87
+ ref: main
88
+ fetch-depth: 0
89
+
90
+ - name: Pull latest changes
91
+ run: git pull origin main
92
+
93
+ - name: Create .env file
94
+ env:
95
+ ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
96
+ run: |
97
+ printf '%s\n' "$ENV_CONTENT" > .env
98
+ echo ".env file created"
99
+
100
+ - name: Set up Flutter
101
+ uses: subosito/flutter-action@v2
102
+ with:
103
+ flutter-version: ${{ env.FLUTTER_VERSION }}
104
+ cache: true
105
+
106
+ - name: Cache Flutter dependencies
107
+ uses: actions/cache@v4
108
+ with:
109
+ path: ~/.pub-cache
110
+ key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
111
+ restore-keys: ${{ runner.os }}-flutter-pub-
112
+
113
+ - name: Install dependencies
114
+ run: flutter pub get
115
+
116
+ # version_manager.py를 사용하여 현재 버전 정보 가져오기
117
+ - name: 현재 버전 정보 가져오기
118
+ id: current_version
119
+ run: |
120
+ # VERSION 가져오기 (x.y.z 형식)
121
+ VERSION=$(python3 .github/scripts/version_manager.py get | tail -n 1)
122
+
123
+ # VERSION_CODE 가져오기 (version.yml에서 관리되는 단순 증가 번호)
124
+ VERSION_CODE=$(python3 .github/scripts/version_manager.py get-code | tail -n 1)
125
+
126
+ # 결과 출력
127
+ echo "✅ VERSION: $VERSION"
128
+ echo "✅ VERSION_CODE: $VERSION_CODE"
129
+ echo "🔧 프로젝트 타입: ${{ env.PROJECT_TYPE }}"
130
+
131
+ # GitHub outputs 설정
132
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
133
+ echo "version_code=$VERSION_CODE" >> $GITHUB_OUTPUT
134
+ echo "project_type=${{ env.PROJECT_TYPE }}" >> $GITHUB_OUTPUT
135
+
136
+ # 환경변수로도 설정 (스크립트에서 사용)
137
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
138
+ echo "VERSION_CODE=$VERSION_CODE" >> $GITHUB_ENV
139
+ echo "TODAY=$(date '+%Y-%m-%d')" >> $GITHUB_ENV
140
+ echo "TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV
141
+ echo "PROJECT_TYPE=${{ env.PROJECT_TYPE }}" >> $GITHUB_ENV
142
+
143
+ # CHANGELOG.json에서 릴리즈 노트 추출 - changelog_manager.py 사용
144
+ - name: 릴리즈 노트 생성
145
+ id: release_notes
146
+ run: |
147
+ VERSION="${{ steps.current_version.outputs.version }}"
148
+
149
+ if [ -f "CHANGELOG.json" ]; then
150
+ echo "📄 CHANGELOG.json에서 v$VERSION 릴리즈 노트 추출 중..."
151
+ # CHANGELOG.md 생성
152
+ python3 ./.github/scripts/changelog_manager.py generate-md
153
+
154
+ # changelog_manager.py export로 릴리즈 노트 추출
155
+ python3 ./.github/scripts/changelog_manager.py export --version $VERSION --output final_release_notes.txt
156
+
157
+ if [ -s final_release_notes.txt ]; then
158
+ echo "✅ 릴리즈 노트 추출 성공!"
159
+ echo "📋 추출된 릴리즈 노트:"
160
+ echo "----------------------------------------"
161
+ cat final_release_notes.txt
162
+ echo "----------------------------------------"
163
+ echo "RELEASE_NOTES_FOUND=true" >> $GITHUB_ENV
164
+ else
165
+ echo "❌ 릴리즈 노트 추출 실패"
166
+ echo "RELEASE_NOTES_FOUND=false" >> $GITHUB_ENV
167
+ echo "v$VERSION 업데이트" > final_release_notes.txt
168
+ fi
169
+ else
170
+ echo "⚠️ CHANGELOG.json 파일이 없습니다. 기본 릴리즈 노트를 사용합니다."
171
+ echo "RELEASE_NOTES_FOUND=false" >> $GITHUB_ENV
172
+ echo "v$VERSION 업데이트" > final_release_notes.txt
173
+ fi
174
+
175
+ # 릴리즈 노트를 아티팩트로 업로드
176
+ - name: Upload release notes
177
+ uses: actions/upload-artifact@v4
178
+ with:
179
+ name: release-notes
180
+ path: final_release_notes.txt
181
+ retention-days: 1
182
+
183
+ # 프로젝트 파일들을 아티팩트로 업로드
184
+ # .env는 보안상 아티팩트에 포함하지 않고, 각 job에서 secrets로 재생성
185
+ - name: Upload project files
186
+ uses: actions/upload-artifact@v4
187
+ with:
188
+ name: project-files
189
+ path: |
190
+ pubspec.yaml
191
+ lib/
192
+ assets/
193
+ retention-days: 1
194
+
195
+ build-android:
196
+ name: Android AAB 빌드
197
+ runs-on: ubuntu-latest
198
+ needs: prepare-build
199
+
200
+ steps:
201
+ - name: Checkout repository
202
+ uses: actions/checkout@v5
203
+ with:
204
+ ref: main
205
+ fetch-depth: 0
206
+
207
+ - name: Pull latest changes
208
+ run: git pull origin main
209
+
210
+ # 프로젝트 파일들 다운로드
211
+ - name: Download project files
212
+ uses: actions/download-artifact@v4
213
+ with:
214
+ name: project-files
215
+ path: .
216
+
217
+ # .env 파일 생성 (보안을 위해 아티팩트가 아닌 시크릿에서 생성)
218
+ - name: Create .env file
219
+ env:
220
+ ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
221
+ run: |
222
+ printf '%s\n' "$ENV_CONTENT" > .env
223
+ echo "✅ .env 파일 생성됨 (크기: $(wc -c < .env) bytes)"
224
+
225
+ # Release Keystore 설정
226
+ - name: Setup Release Keystore
227
+ run: |
228
+ mkdir -p android/app/keystore
229
+ echo "${{ secrets.RELEASE_KEYSTORE_BASE64 }}" | base64 -d > android/app/keystore/key.jks
230
+ echo "✅ Release Keystore 생성 완료"
231
+ ls -la android/app/keystore/
232
+
233
+ # key.properties 생성 (Release 서명 정보)
234
+ - name: Create key.properties
235
+ run: |
236
+ cat > android/key.properties << EOF
237
+ storeFile=app/keystore/key.jks
238
+ storePassword=${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
239
+ keyAlias=${{ secrets.RELEASE_KEY_ALIAS }}
240
+ keyPassword=${{ secrets.RELEASE_KEY_PASSWORD }}
241
+ EOF
242
+ echo "✅ key.properties 생성 완료"
243
+ cat android/key.properties
244
+
245
+ # Google-services.json 생성
246
+ - name: Create Google-services.json
247
+ run: |
248
+ cat << 'EOF' > android/app/google-services.json
249
+ ${{ secrets.GOOGLE_SERVICES_JSON }}
250
+ EOF
251
+ echo "✅ Google-services.json 생성 완료"
252
+
253
+ - name: Set up Flutter
254
+ uses: subosito/flutter-action@v2
255
+ with:
256
+ flutter-version: ${{ env.FLUTTER_VERSION }}
257
+ cache: true
258
+
259
+ - name: Cache Flutter dependencies
260
+ uses: actions/cache@v4
261
+ with:
262
+ path: ~/.pub-cache
263
+ key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
264
+ restore-keys: ${{ runner.os }}-flutter-pub-
265
+
266
+ - name: Install dependencies
267
+ run: flutter pub get
268
+
269
+ - name: Set up Java
270
+ uses: actions/setup-java@v4
271
+ with:
272
+ distribution: "temurin"
273
+ java-version: ${{ env.JAVA_VERSION }}
274
+
275
+ - name: Cache Gradle dependencies
276
+ uses: actions/cache@v4
277
+ with:
278
+ path: |
279
+ ~/.gradle/caches
280
+ ~/.gradle/wrapper
281
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle*', '**/gradle-wrapper.properties') }}
282
+ restore-keys: |
283
+ ${{ runner.os }}-gradle-
284
+
285
+ # Android build.gradle.kts 수정 (release 서명 설정)
286
+ - name: Configure Android build for release
287
+ run: |
288
+ # build.gradle.kts에 release 서명 설정이 없으면 추가
289
+ if ! grep -q "signingConfigs" android/app/build.gradle.kts; then
290
+ echo "⚠️ build.gradle.kts에 서명 설정 추가 필요"
291
+ # 서명 설정은 이미 되어있다고 가정 (수동으로 미리 설정해야 함)
292
+ fi
293
+ echo "✅ Android 빌드 설정 확인 완료"
294
+
295
+ # pubspec.yaml은 x.y.z 형식 그대로 유지
296
+ # Flutter build 명령어의 --build-name, --build-number 플래그가 pubspec.yaml을 오버라이드함
297
+
298
+ # Flutter build appbundle (AAB 생성)
299
+ - name: Build Android App Bundle (AAB)
300
+ run: |
301
+ VERSION_NAME="${{ needs.prepare-build.outputs.version }}"
302
+ VERSION_CODE="${{ needs.prepare-build.outputs.version_code }}"
303
+
304
+ echo "=========================================="
305
+ echo "🚀 AAB 빌드 시작"
306
+ echo "=========================================="
307
+ echo "🎯 빌드 버전: $VERSION_NAME"
308
+ echo "🎯 빌드 코드: $VERSION_CODE"
309
+
310
+ # 입력 검증
311
+ if [ -z "$VERSION_NAME" ] || [ -z "$VERSION_CODE" ]; then
312
+ echo "❌ VERSION_NAME 또는 VERSION_CODE가 비어있습니다!"
313
+ exit 1
314
+ fi
315
+
316
+ # Flutter 환경 정보 출력
317
+ echo "🔍 Flutter 환경 정보:"
318
+ flutter --version
319
+ flutter doctor -v | head -20
320
+
321
+ # 빌드 디렉토리 정리
322
+ echo "🧹 이전 빌드 결과물 정리..."
323
+ rm -rf build/app/outputs/bundle/release/
324
+ mkdir -p build/app/outputs/bundle/release/
325
+
326
+ # Flutter clean 및 pub get
327
+ echo "🔄 Flutter clean 및 의존성 재설치..."
328
+ flutter clean
329
+ flutter pub get
330
+
331
+ # local.properties에 버전 정보 추가 (Flutter 플래그가 무시되는 문제 해결)
332
+ echo "🔧 local.properties에 버전 정보 설정..."
333
+
334
+ # 기존 local.properties가 있으면 버전 정보 제거
335
+ if [ -f "android/local.properties" ]; then
336
+ grep -v "flutter.versionName\|flutter.versionCode" android/local.properties > android/local.properties.tmp || true
337
+ mv android/local.properties.tmp android/local.properties
338
+ else
339
+ touch android/local.properties
340
+ fi
341
+
342
+ # 버전 정보 추가
343
+ echo "flutter.versionName=$VERSION_NAME" >> android/local.properties
344
+ echo "flutter.versionCode=$VERSION_CODE" >> android/local.properties
345
+
346
+ echo "✅ local.properties 업데이트 완료"
347
+ echo "📋 버전 정보: VERSION_NAME=$VERSION_NAME, VERSION_CODE=$VERSION_CODE"
348
+
349
+ # Gradle 성능 최적화 설정
350
+ export GRADLE_OPTS="-Dorg.gradle.daemon=false -Dorg.gradle.parallel=true -Dorg.gradle.workers.max=4"
351
+ export JAVA_OPTS="-Xmx4g"
352
+ echo "✅ Gradle 최적화 설정 완료"
353
+
354
+ # Flutter build 명령어에 버전 정보 전달
355
+ # --build-name과 --build-number 플래그가 pubspec.yaml의 version을 오버라이드함
356
+ # pubspec.yaml은 x.y.z 형식 유지, VERSION_CODE는 version.yml에서 자동 관리
357
+ echo "🔨 Flutter build 실행..."
358
+ echo "📋 명령어: flutter build appbundle --release --build-name=$VERSION_NAME --build-number=$VERSION_CODE --verbose"
359
+ echo "⏱️ 빌드 시작 시간: $(date '+%Y-%m-%d %H:%M:%S')"
360
+
361
+ flutter build appbundle \
362
+ --release \
363
+ --build-name="$VERSION_NAME" \
364
+ --build-number="$VERSION_CODE" \
365
+ --verbose
366
+
367
+ BUILD_EXIT_CODE=$?
368
+
369
+ if [ $BUILD_EXIT_CODE -ne 0 ]; then
370
+ echo "❌ Flutter build 실패! Exit code: $BUILD_EXIT_CODE"
371
+ echo "🔍 빌드 디렉토리 상태:"
372
+ find build/ -name "*.aab" -o -name "*.apk" 2>/dev/null || echo "AAB/APK 파일을 찾을 수 없습니다"
373
+ exit 1
374
+ fi
375
+
376
+ echo "✅ Flutter build 성공!"
377
+
378
+ # 빌드 결과 확인
379
+ echo "🔍 빌드 결과 확인:"
380
+ ls -lah build/app/outputs/bundle/release/
381
+
382
+ # AAB 파일 존재 확인
383
+ AAB_FILE="build/app/outputs/bundle/release/app-release.aab"
384
+ if [ ! -f "$AAB_FILE" ]; then
385
+ echo "❌ AAB 파일이 생성되지 않았습니다!"
386
+ echo "🔍 전체 빌드 디렉토리 내용:"
387
+ find build/ -type f -name "*.aab" -o -name "*.apk" 2>/dev/null || echo "AAB/APK 파일을 찾을 수 없습니다"
388
+ echo "🔍 outputs 디렉토리 구조:"
389
+ find build/app/outputs/ -type f 2>/dev/null || echo "outputs 디렉토리가 없습니다"
390
+ exit 1
391
+ fi
392
+
393
+ echo "✅ AAB 파일 생성 확인"
394
+ echo "📱 AAB 파일 크기: $(du -h "$AAB_FILE" | cut -f1)"
395
+ echo "📋 AAB 파일 정보: $(ls -lah "$AAB_FILE")"
396
+
397
+ # bundletool 다운로드 및 설치
398
+ echo "📦 bundletool 다운로드 중..."
399
+ BUNDLETOOL_VERSION="1.15.6"
400
+ BUNDLETOOL_URL="https://github.com/google/bundletool/releases/download/${BUNDLETOOL_VERSION}/bundletool-all-${BUNDLETOOL_VERSION}.jar"
401
+
402
+ echo "📋 다운로드 URL: $BUNDLETOOL_URL"
403
+ wget -q "$BUNDLETOOL_URL" -O bundletool.jar
404
+
405
+ if [ ! -f "bundletool.jar" ]; then
406
+ echo "❌ bundletool 다운로드 실패"
407
+ echo "🔄 대체 URL로 재시도..."
408
+ wget -q "https://github.com/google/bundletool/releases/latest/download/bundletool-all-1.15.6.jar" -O bundletool.jar
409
+
410
+ if [ ! -f "bundletool.jar" ]; then
411
+ echo "❌ bundletool 다운로드 완전 실패"
412
+ exit 1
413
+ fi
414
+ fi
415
+
416
+ echo "✅ bundletool 다운로드 완료"
417
+ echo "📋 bundletool 파일 크기: $(du -h bundletool.jar | cut -f1)"
418
+
419
+ # Java 환경 확인
420
+ echo "🔍 Java 환경 확인:"
421
+ java -version
422
+
423
+ # AAB 파일 내부의 실제 버전 정보 확인
424
+ # Flutter CLI 플래그로 전달한 VERSION_CODE가 제대로 적용되었는지 검증
425
+ echo "=========================================="
426
+ echo "🔍 AAB 파일 내부 버전 정보 검증"
427
+ echo "=========================================="
428
+
429
+ # AndroidManifest.xml에서 버전 정보 추출
430
+ echo "📋 AAB에서 실제 버전 정보 추출 중..."
431
+ echo "📋 실행할 명령어: java -jar bundletool.jar dump manifest --bundle='$AAB_FILE'"
432
+
433
+ MANIFEST_OUTPUT=$(java -jar bundletool.jar dump manifest --bundle="$AAB_FILE" 2>&1)
434
+ MANIFEST_EXIT_CODE=$?
435
+
436
+ echo "📋 bundletool 실행 결과 (Exit code: $MANIFEST_EXIT_CODE):"
437
+ echo "$MANIFEST_OUTPUT"
438
+
439
+ if [ $MANIFEST_EXIT_CODE -eq 0 ]; then
440
+ echo "✅ Manifest 추출 성공"
441
+
442
+ # 실제 버전 코드와 버전 이름 추출
443
+ ACTUAL_VERSION_CODE=$(echo "$MANIFEST_OUTPUT" | grep -o 'android:versionCode="[0-9]*"' | grep -o '[0-9]*' | head -1)
444
+ ACTUAL_VERSION_NAME=$(echo "$MANIFEST_OUTPUT" | grep -o 'android:versionName="[^"]*"' | sed 's/android:versionName="//;s/"//' | head -1)
445
+
446
+ echo "🎯 버전 정보 비교:"
447
+ echo " 예상 VERSION_CODE: '$VERSION_CODE'"
448
+ echo " 실제 VERSION_CODE: '$ACTUAL_VERSION_CODE'"
449
+ echo " 예상 VERSION_NAME: '$VERSION_NAME'"
450
+ echo " 실제 VERSION_NAME: '$ACTUAL_VERSION_NAME'"
451
+
452
+ # VERSION_CODE 검증 - version.yml에서 관리되는 단순 증가 번호
453
+ if [ -z "$ACTUAL_VERSION_CODE" ]; then
454
+ echo "❌ 치명적 오류: AAB에서 VERSION_CODE를 추출할 수 없습니다!"
455
+ echo "🔍 전체 Manifest 내용:"
456
+ echo "$MANIFEST_OUTPUT"
457
+ exit 1
458
+ elif [ "$ACTUAL_VERSION_CODE" != "$VERSION_CODE" ]; then
459
+ echo "❌ 치명적 오류: AAB의 VERSION_CODE가 예상과 다릅니다!"
460
+ echo " 이는 Google Play에서 '이미 사용된 버전 코드' 오류를 발생시킵니다."
461
+ echo ""
462
+ echo "🔍 디버깅 정보:"
463
+ echo " version.yml의 VERSION_CODE: $VERSION_CODE"
464
+ echo " AAB 파일 내부의 VERSION_CODE: $ACTUAL_VERSION_CODE"
465
+ echo ""
466
+ echo " Flutter build 명령어에 전달된 플래그:"
467
+ echo " --build-name='$VERSION_NAME'"
468
+ echo " --build-number='$VERSION_CODE'"
469
+ echo ""
470
+ echo " 전체 Manifest 내용:"
471
+ echo "$MANIFEST_OUTPUT" | grep -E "versionCode|versionName|package"
472
+ exit 1
473
+ else
474
+ echo "✅ VERSION_CODE 검증 성공: $ACTUAL_VERSION_CODE"
475
+ fi
476
+
477
+ # 버전 이름도 확인 (경고만)
478
+ if [ -z "$ACTUAL_VERSION_NAME" ]; then
479
+ echo "⚠️ 경고: AAB에서 VERSION_NAME을 추출할 수 없습니다"
480
+ elif [ "$ACTUAL_VERSION_NAME" != "$VERSION_NAME" ]; then
481
+ echo "⚠️ 경고: VERSION_NAME이 예상과 다릅니다 (치명적이지 않음)"
482
+ echo " 예상: '$VERSION_NAME', 실제: '$ACTUAL_VERSION_NAME'"
483
+ else
484
+ echo "✅ VERSION_NAME 검증 성공: $ACTUAL_VERSION_NAME"
485
+ fi
486
+
487
+ else
488
+ echo "❌ bundletool로 Manifest 추출 실패"
489
+ echo "🔄 대체 방법으로 검증 시도..."
490
+
491
+ # unzip으로 직접 확인 시도
492
+ echo "📋 unzip을 사용한 직접 추출 시도..."
493
+ UNZIP_OUTPUT=$(unzip -p "$AAB_FILE" base/manifest/AndroidManifest.xml 2>/dev/null | strings | grep -E "versionCode|versionName")
494
+
495
+ if [ -n "$UNZIP_OUTPUT" ]; then
496
+ echo "✅ unzip으로 버전 정보 추출 성공:"
497
+ echo "$UNZIP_OUTPUT"
498
+ else
499
+ echo "❌ unzip을 사용한 직접 추출도 실패"
500
+ echo "⚠️ AAB 검증을 건너뛰고 계속 진행합니다."
501
+ fi
502
+ fi
503
+
504
+ echo "=========================================="
505
+ echo "✅ AAB 빌드 및 검증 완료"
506
+ echo "🎯 최종 AAB: $AAB_FILE"
507
+ echo "📱 파일 크기: $(du -h "$AAB_FILE" | cut -f1)"
508
+ echo "=========================================="
509
+
510
+ # AAB 파일을 아티팩트로 업로드
511
+ - name: Upload AAB artifact
512
+ uses: actions/upload-artifact@v4
513
+ with:
514
+ name: android-aab
515
+ path: build/app/outputs/bundle/release/app-release.aab
516
+ retention-days: 1
517
+
518
+ deploy-firebase:
519
+ name: Firebase App Distribution 배포
520
+ runs-on: ubuntu-latest
521
+ needs: [prepare-build, build-android]
522
+
523
+ steps:
524
+ - name: Checkout repository
525
+ uses: actions/checkout@v5
526
+ with:
527
+ ref: main
528
+ fetch-depth: 0
529
+
530
+ # AAB 파일 다운로드
531
+ - name: Download AAB artifact
532
+ uses: actions/download-artifact@v4
533
+ with:
534
+ name: android-aab
535
+ path: build/app/outputs/bundle/release/
536
+
537
+ # 릴리즈 노트 다운로드
538
+ - name: Download release notes
539
+ uses: actions/download-artifact@v4
540
+ with:
541
+ name: release-notes
542
+ path: .
543
+
544
+ # Firebase 서비스 계정 설정
545
+ - name: Setup Firebase credentials file
546
+ run: |
547
+ echo "${{ secrets.FIREBASE_SERVICE_ACCOUNT_JSON_BASE64 }}" | base64 --decode > firebase-service-account.json
548
+ echo "✅ Firebase 서비스 계정 JSON 파일 생성 완료"
549
+
550
+ # Firebase App Distribution release notes 길이 제한 (안전망)
551
+ - name: Truncate release notes
552
+ run: |
553
+ if [ -f "final_release_notes.txt" ]; then
554
+ # Firebase App Distribution release notes 길이 제한 안전망 (4000자)
555
+ bash ./.github/scripts/truncate_release_notes.sh final_release_notes.txt 4000 char
556
+ fi
557
+
558
+ # Firebase App Distribution에 AAB 업로드
559
+ - name: Upload to Firebase App Distribution
560
+ id: firebase_upload
561
+ uses: wzieba/Firebase-Distribution-Github-Action@v1
562
+ with:
563
+ appId: ${{ env.FIREBASE_APP_ID }}
564
+ serviceCredentialsFile: firebase-service-account.json
565
+ groups: ${{ env.FIREBASE_TESTER_GROUP }}
566
+ file: build/app/outputs/bundle/release/app-release.aab
567
+ releaseNotesFile: final_release_notes.txt
568
+
569
+ # Firebase 인증 파일 삭제 (보안)
570
+ - name: Cleanup Firebase credentials
571
+ if: always()
572
+ run: rm -f firebase-service-account.json
573
+
574
+ # 성공 알림
575
+ - name: Notify Firebase Upload Success
576
+ if: success()
577
+ run: |
578
+ echo "✅ Firebase App Distribution 배포 성공!"
579
+ echo "버전: ${{ needs.prepare-build.outputs.version }} (${{ needs.prepare-build.outputs.version_code }})"
580
+ echo "커밋: ${{ github.sha }}"
581
+ echo ""
582
+ echo "📱 테스터 확인 방법:"
583
+ echo "1. Firebase App Tester 앱 실행"
584
+ echo "2. 앱 선택"
585
+ echo "3. 최신 빌드 다운로드"
586
+
587
+ - name: Notify on Failure
588
+ if: failure()
589
+ run: |
590
+ echo "❌ Firebase App Distribution 배포 실패!"
591
+ echo "로그를 확인해주세요."