project-auto-wizard 0.10.0 → 0.11.0

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 (46) hide show
  1. package/README.md +83 -4
  2. package/package.json +1 -1
  3. package/payload/config/breaking-changes.json +24 -1
  4. package/payload/flutter-app/android/fastlane/Fastfile.playstore +86 -0
  5. package/payload/flutter-app/ios/ExportOptions.plist +31 -0
  6. package/payload/flutter-app/ios/fastlane/Fastfile +117 -0
  7. package/payload/scripts/__pycache__/changelog_manager.cpython-314.pyc +0 -0
  8. package/payload/scripts/__pycache__/issue_helper.cpython-314.pyc +0 -0
  9. package/payload/scripts/__pycache__/version_manager.cpython-314.pyc +0 -0
  10. package/payload/version.yml.template +1 -0
  11. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +48 -20
  12. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +44 -19
  13. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +28 -36
  14. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +32 -45
  15. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +123 -32
  16. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +56 -41
  17. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +61 -40
  18. package/payload/workflows/go/PROJECT-GO-CI.yaml +47 -0
  19. package/payload/workflows/next/PROJECT-NEXT-CI.yaml +47 -0
  20. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +47 -0
  21. package/payload/workflows/react/PROJECT-REACT-CI.yaml +47 -0
  22. package/payload/workflows/spring/nexus/PROJECT-SPRING-GITHUB-PACKAGES-PUBLISH.yml +1 -0
  23. package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-CI.yml +47 -0
  24. package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-PUBLISH.yml +1 -0
  25. package/src/cli/args.js +37 -0
  26. package/src/cli/help.js +4 -0
  27. package/src/commands/doctor.js +55 -2
  28. package/src/commands/dry-run.js +10 -0
  29. package/src/commands/full.js +28 -3
  30. package/src/commands/interactive-flutter.js +95 -0
  31. package/src/commands/interactive.js +51 -5
  32. package/src/commands/status.js +20 -2
  33. package/src/context.js +6 -0
  34. package/src/core/breaking.js +7 -3
  35. package/src/core/copy/flutter-app.js +42 -0
  36. package/src/core/copy/workflows.js +21 -9
  37. package/src/core/detect-fs.js +9 -1
  38. package/src/core/flutter-options.js +118 -0
  39. package/src/core/installed-stores.js +12 -0
  40. package/src/core/version-yml.js +40 -5
  41. package/src/core/wizard-env.js +26 -4
  42. package/src/index.js +32 -1
  43. package/src/ui/env-plan.js +8 -5
  44. package/src/ui/prompts.js +88 -3
  45. package/src/ui/status-cards.js +13 -1
  46. package/src/ui/summary.js +24 -5
@@ -32,7 +32,10 @@
32
32
  # FLUTTER_VERSION : Flutter SDK 버전
33
33
  # JAVA_VERSION : Java 버전 (Android 빌드용)
34
34
  # XCODE_VERSION : Xcode 버전 (iOS 빌드용)
35
- # ENV_FILE_PATH : .env 파일 경로 (기본값: 루트의 .env)
35
+ # ENV_FILE_PATH : dotenv 모드에서 .env를 만들 경로 (Flutter 루트 기준, 기본값: .env)
36
+ # ENV_MODE : 환경변수 전달 방식 — dart-define(--dart-define-from-file) | dotenv(.env 파일)
37
+ # FLUTTER_PROJECT_DIR : Flutter 루트 경로 (설치 시 자동 설정, 모노레포는 app 등)
38
+ # PROJECT_PATH : 변경 감지 기준 경로 (설치 시 자동 설정, "."이면 항상 실행)
36
39
  #
37
40
  # ==========================================================================================
38
41
  # 🔑 필수 GitHub Secrets
@@ -40,6 +43,8 @@
40
43
  # ENV_FILE (선택): .env 파일 내용
41
44
  #
42
45
  # ※ CI는 빌드 검증 목적이므로 서명/배포 관련 Secrets 불필요
46
+ # ※ 모노레포에서 이 CI를 required check로 쓰려면 개별 job이 아니라 `ci-gate` 하나만 등록하세요.
47
+ # (경로가 바뀌지 않아 건너뛴 job은 Success로 집계되고, ci-gate가 최종 결과를 판정합니다)
43
48
  # ==========================================================================================
44
49
 
45
50
  name: PROJECT-Flutter-CI
@@ -73,6 +78,13 @@ permissions:
73
78
  # 🔧 프로젝트별 설정 (아래 값들을 수정하세요)
74
79
  # ==========================================================================================
75
80
  env:
81
+ # 🎯 모노레포 변경 감지 경로 (설치 시 자동 설정, 단일 레포는 ".")
82
+ PROJECT_PATH: "." # @wizard auto:project-path
83
+ # Flutter 루트 경로(레포 루트 기준). 단일레포면 ".", 모노레포면 "app" 등 — 설치 시 자동 설정
84
+ FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
85
+ # 환경변수 전달 방식 (dart-define | dotenv) — 설치 시 선택값으로 자동 설정
86
+ ENV_MODE: "dart-define" # @wizard auto:flutter-env-mode
87
+
76
88
  # 🎯 CI 모드 설정
77
89
  ANALYZE_ONLY: "false" # true: analyze만 실행 (빌드 스킵), false: analyze + 빌드
78
90
 
@@ -84,18 +96,44 @@ env:
84
96
  FLUTTER_VERSION: "3.35.5"
85
97
  JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:17
86
98
  XCODE_VERSION: "26.0"
87
- ENV_FILE_PATH: ".env" # 환경변수 파일 경로 (커스터마이징 가능)
99
+ ENV_FILE_PATH: ".env" # dotenv 모드에서 .env를 만들 경로 (Flutter 루트 기준, 커스터마이징 가능)
88
100
 
89
101
  concurrency:
90
102
  group: flutter-ci-${{ github.ref }}
91
103
  cancel-in-progress: true
92
104
 
93
105
  jobs:
106
+ # ===================================================================
107
+ # Job 0: 변경 감지 (PROJECT_PATH 아래가 바뀌었을 때만 이후 job 실행)
108
+ # ===================================================================
109
+ changes:
110
+ name: 변경 감지
111
+ runs-on: ubuntu-latest
112
+ permissions:
113
+ contents: read
114
+ pull-requests: read
115
+ outputs:
116
+ project: ${{ steps.filter.outputs.project }}
117
+ steps:
118
+ - name: Checkout repository
119
+ uses: actions/checkout@v7
120
+
121
+ - name: 프로젝트 경로 변경 여부 판별
122
+ id: filter
123
+ if: ${{ github.event_name != 'workflow_dispatch' }}
124
+ uses: dorny/paths-filter@v4
125
+ with:
126
+ filters: |
127
+ project:
128
+ - '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
129
+
94
130
  # ===================================================================
95
131
  # Job 1: 준비 단계
96
132
  # ===================================================================
97
133
  prepare:
98
134
  name: CI 준비
135
+ needs: changes
136
+ if: ${{ (github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true') }}
99
137
  runs-on: ubuntu-latest
100
138
 
101
139
  outputs:
@@ -229,7 +267,11 @@ jobs:
229
267
  analyze:
230
268
  name: 코드 분석
231
269
  runs-on: ubuntu-latest
232
- needs: prepare
270
+ needs: [changes, prepare]
271
+ if: ${{ (github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true') }}
272
+ defaults:
273
+ run:
274
+ working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
233
275
 
234
276
  outputs:
235
277
  status: ${{ steps.result.outputs.status }}
@@ -246,13 +288,20 @@ jobs:
246
288
  - name: Checkout repository
247
289
  uses: actions/checkout@v7
248
290
 
249
- # .env 파일 생성
250
- - name: Create .env file
291
+ # 환경변수 준비 — ENV_MODE=dotenv면 .env 파일, dart-define이면 프로젝트 밖 임시 파일(--dart-define-from-file)
292
+ - name: Prepare env file
293
+ env:
294
+ ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
251
295
  run: |
252
- cat << 'EOF' > ${{ env.ENV_FILE_PATH }}
253
- ${{ secrets.ENV_FILE || secrets.ENV }}
254
- EOF
255
- echo "✅ ${{ env.ENV_FILE_PATH }} file created"
296
+ if [ "$ENV_MODE" = "dotenv" ]; then
297
+ printf '%s\n' "$ENV_CONTENT" > "$ENV_FILE_PATH"
298
+ echo "✅ $ENV_FILE_PATH file created"
299
+ elif [ -n "$ENV_CONTENT" ]; then
300
+ DART_DEFINE_FILE="$RUNNER_TEMP/dart-define.env"
301
+ printf '%s\n' "$ENV_CONTENT" > "$DART_DEFINE_FILE"
302
+ echo "DART_DEFINE_FILE=$DART_DEFINE_FILE" >> "$GITHUB_ENV"
303
+ echo "dart-define file prepared"
304
+ fi
256
305
 
257
306
  # Flutter 설정
258
307
  - name: Set up Flutter
@@ -271,7 +320,7 @@ jobs:
271
320
  uses: actions/cache@v6
272
321
  with:
273
322
  path: ~/.pub-cache
274
- key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
323
+ key: ${{ runner.os }}-flutter-pub-${{ hashFiles(format('{0}/**/pubspec.lock', env.FLUTTER_PROJECT_DIR)) }}
275
324
  restore-keys: ${{ runner.os }}-flutter-pub-
276
325
 
277
326
  # 프로젝트 의존성 설치
@@ -317,10 +366,14 @@ jobs:
317
366
  build-android:
318
367
  name: Android 빌드
319
368
  if: |
369
+ (github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true') &&
320
370
  needs.prepare.outputs.analyze_only != 'true' &&
321
371
  needs.prepare.outputs.enable_android == 'true'
322
372
  runs-on: ubuntu-latest
323
- needs: prepare
373
+ needs: [changes, prepare]
374
+ defaults:
375
+ run:
376
+ working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
324
377
 
325
378
  outputs:
326
379
  status: ${{ steps.result.outputs.status }}
@@ -337,13 +390,20 @@ jobs:
337
390
  - name: Checkout repository
338
391
  uses: actions/checkout@v7
339
392
 
340
- # .env 파일 생성
341
- - name: Create .env file
393
+ # 환경변수 준비 — ENV_MODE=dotenv면 .env 파일, dart-define이면 프로젝트 밖 임시 파일(--dart-define-from-file)
394
+ - name: Prepare env file
395
+ env:
396
+ ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
342
397
  run: |
343
- cat << 'EOF' > ${{ env.ENV_FILE_PATH }}
344
- ${{ secrets.ENV_FILE || secrets.ENV }}
345
- EOF
346
- echo "✅ ${{ env.ENV_FILE_PATH }} file created"
398
+ if [ "$ENV_MODE" = "dotenv" ]; then
399
+ printf '%s\n' "$ENV_CONTENT" > "$ENV_FILE_PATH"
400
+ echo "✅ $ENV_FILE_PATH file created"
401
+ elif [ -n "$ENV_CONTENT" ]; then
402
+ DART_DEFINE_FILE="$RUNNER_TEMP/dart-define.env"
403
+ printf '%s\n' "$ENV_CONTENT" > "$DART_DEFINE_FILE"
404
+ echo "DART_DEFINE_FILE=$DART_DEFINE_FILE" >> "$GITHUB_ENV"
405
+ echo "dart-define file prepared"
406
+ fi
347
407
 
348
408
  # Flutter 설정
349
409
  - name: Set up Flutter
@@ -362,7 +422,7 @@ jobs:
362
422
  uses: actions/cache@v6
363
423
  with:
364
424
  path: ~/.pub-cache
365
- key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
425
+ key: ${{ runner.os }}-flutter-pub-${{ hashFiles(format('{0}/**/pubspec.lock', env.FLUTTER_PROJECT_DIR)) }}
366
426
  restore-keys: ${{ runner.os }}-flutter-pub-
367
427
 
368
428
  # Gradle 캐시
@@ -372,7 +432,7 @@ jobs:
372
432
  path: |
373
433
  ~/.gradle/caches
374
434
  ~/.gradle/wrapper
375
- key: ${{ runner.os }}-gradle-${{ hashFiles('**/build.gradle', '**/gradle-wrapper.properties') }}
435
+ key: ${{ runner.os }}-gradle-${{ hashFiles(format('{0}/android/**/build.gradle', env.FLUTTER_PROJECT_DIR), format('{0}/android/**/gradle-wrapper.properties', env.FLUTTER_PROJECT_DIR)) }}
376
436
  restore-keys: |
377
437
  ${{ runner.os }}-gradle-
378
438
 
@@ -399,7 +459,7 @@ jobs:
399
459
 
400
460
  # Gradle 셋업
401
461
  - name: Setup Gradle
402
- working-directory: android
462
+ working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/android
403
463
  run: |
404
464
  chmod +x gradlew
405
465
  echo "✅ Gradle wrapper permissions set"
@@ -409,7 +469,7 @@ jobs:
409
469
  id: build
410
470
  run: |
411
471
  echo "📦 Flutter APK 빌드 시작 (debug)..."
412
- flutter build apk --debug
472
+ flutter build apk --debug ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
413
473
  echo "✅ APK 빌드 완료"
414
474
  ls -la ./build/app/outputs/flutter-apk/ || true
415
475
 
@@ -439,10 +499,14 @@ jobs:
439
499
  build-ios:
440
500
  name: iOS 빌드
441
501
  if: |
502
+ (github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true') &&
442
503
  needs.prepare.outputs.analyze_only != 'true' &&
443
504
  needs.prepare.outputs.enable_ios == 'true'
444
505
  runs-on: macos-26
445
- needs: prepare
506
+ needs: [changes, prepare]
507
+ defaults:
508
+ run:
509
+ working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
446
510
 
447
511
  outputs:
448
512
  status: ${{ steps.result.outputs.status }}
@@ -479,13 +543,20 @@ jobs:
479
543
  echo "=== Downloading iOS platform ==="
480
544
  xcodebuild -downloadPlatform iOS
481
545
 
482
- # .env 파일 생성
483
- - name: Create .env file
546
+ # 환경변수 준비 — ENV_MODE=dotenv면 .env 파일, dart-define이면 프로젝트 밖 임시 파일(--dart-define-from-file)
547
+ - name: Prepare env file
548
+ env:
549
+ ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
484
550
  run: |
485
- cat << 'EOF' > ${{ env.ENV_FILE_PATH }}
486
- ${{ secrets.ENV_FILE || secrets.ENV }}
487
- EOF
488
- echo "✅ ${{ env.ENV_FILE_PATH }} file created"
551
+ if [ "$ENV_MODE" = "dotenv" ]; then
552
+ printf '%s\n' "$ENV_CONTENT" > "$ENV_FILE_PATH"
553
+ echo "✅ $ENV_FILE_PATH file created"
554
+ elif [ -n "$ENV_CONTENT" ]; then
555
+ DART_DEFINE_FILE="$RUNNER_TEMP/dart-define.env"
556
+ printf '%s\n' "$ENV_CONTENT" > "$DART_DEFINE_FILE"
557
+ echo "DART_DEFINE_FILE=$DART_DEFINE_FILE" >> "$GITHUB_ENV"
558
+ echo "dart-define file prepared"
559
+ fi
489
560
 
490
561
  # Flutter 설정
491
562
  - name: Set up Flutter
@@ -504,7 +575,7 @@ jobs:
504
575
  uses: actions/cache@v6
505
576
  with:
506
577
  path: ~/.pub-cache
507
- key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
578
+ key: ${{ runner.os }}-flutter-pub-${{ hashFiles(format('{0}/**/pubspec.lock', env.FLUTTER_PROJECT_DIR)) }}
508
579
  restore-keys: ${{ runner.os }}-flutter-pub-
509
580
 
510
581
  # 프로젝트 의존성 설치
@@ -534,7 +605,7 @@ jobs:
534
605
  id: build
535
606
  run: |
536
607
  echo "📦 Flutter iOS 빌드 시작..."
537
- flutter build ios --release --no-codesign
608
+ flutter build ios --release --no-codesign ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
538
609
  echo "✅ iOS 빌드 완료"
539
610
 
540
611
  # 빌드 결과 기록
@@ -562,9 +633,9 @@ jobs:
562
633
  # ===================================================================
563
634
  report:
564
635
  name: 결과 보고
565
- if: always() && github.event_name == 'pull_request'
636
+ if: always() && github.event_name == 'pull_request' && needs.changes.outputs.project == 'true'
566
637
  runs-on: ubuntu-latest
567
- needs: [prepare, analyze, build-android, build-ios]
638
+ needs: [changes, prepare, analyze, build-android, build-ios]
568
639
 
569
640
  steps:
570
641
  - name: 최종 결과 댓글 업데이트
@@ -714,3 +785,23 @@ jobs:
714
785
  } else {
715
786
  console.log('⚠️ 댓글 ID가 없어 업데이트를 건너뜁니다.');
716
787
  }
788
+
789
+ # ===================================================================
790
+ # Job 6: CI Gate (required check로 등록할 단일 진입점)
791
+ # ===================================================================
792
+ # 건너뛴(skipped) job은 통과로 보고 failure/cancelled가 하나라도 있으면 실패한다.
793
+ ci-gate:
794
+ name: CI Gate
795
+ if: ${{ always() }}
796
+ needs: [changes, prepare, analyze, build-android, build-ios, report]
797
+ runs-on: ubuntu-latest
798
+ steps:
799
+ - name: 결과 집계
800
+ env:
801
+ RESULTS: ${{ toJSON(needs.*.result) }}
802
+ run: |
803
+ echo "needs 결과: $RESULTS"
804
+ if echo "$RESULTS" | grep -Eq '"(failure|cancelled)"'; then
805
+ echo "실패하거나 취소된 job이 있습니다."
806
+ exit 1
807
+ fi
@@ -6,10 +6,8 @@
6
6
  # 이 워크플로우는 기능 브랜치에서 수동으로 실행하여
7
7
  # 테스트용 iOS 빌드를 TestFlight에 배포합니다.
8
8
  #
9
- # ★ 마법사 우선 아키텍처 ★
10
- # - 빌드에 필요한 설정 파일들은 웹 마법사가 생성합니다
11
- # - 워크플로우는 마법사가 생성한 파일들을 그대로 사용합니다
12
- # - 마법사 경로: .github/util/flutter/ios-testflight-setup-wizard/index.html
9
+ # 설정 파일(ios/fastlane/Fastfile, ios/ExportOptions.plist)은 project-auto-wizard가
10
+ # 없을 때만 설치하며, 이미 있으면 덮어쓰지 않습니다.
13
11
  #
14
12
  # 주요 특징:
15
13
  # - workflow_dispatch로 수동 실행 또는 repository_dispatch로 트리거
@@ -22,7 +20,7 @@
22
20
  # 1. flutter build ios --no-codesign (Flutter 빌드)
23
21
  # 2. xcodebuild archive (Xcode 아카이브 생성)
24
22
  # 3. xcodebuild -exportArchive (IPA 생성, ExportOptions.plist 사용)
25
- # 4. fastlane upload_testflight (마법사 생성 Fastfile 사용)
23
+ # 4. fastlane upload_testflight (ios/fastlane/Fastfile 사용)
26
24
  #
27
25
  # ===================================================================
28
26
  # 🔑 필수 GitHub Secrets
@@ -63,6 +61,11 @@ env:
63
61
  FLUTTER_VERSION: "3.35.5"
64
62
  XCODE_VERSION: "26.0"
65
63
  PROJECT_TYPE: "flutter"
64
+ # Flutter 루트 경로(레포 루트 기준). 단일레포면 ".", 모노레포면 "app" 등 — 설치 시 자동 설정
65
+ FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
66
+ # 환경변수 전달 방식 (dart-define | dotenv) — 설치 시 선택값으로 자동 설정
67
+ ENV_MODE: "dart-define" # @wizard auto:flutter-env-mode
68
+ # dotenv 모드에서 .env 파일을 만들 경로 (Flutter 루트 기준)
66
69
  ENV_FILE_PATH: ".env"
67
70
 
68
71
  jobs:
@@ -186,6 +189,9 @@ jobs:
186
189
  prepare-test-build:
187
190
  name: 테스트 빌드 준비
188
191
  runs-on: macos-26
192
+ defaults:
193
+ run:
194
+ working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
189
195
  needs: notify-start
190
196
  if: |
191
197
  always() &&
@@ -214,13 +220,6 @@ jobs:
214
220
  - name: Select Xcode version
215
221
  run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
216
222
 
217
- - name: Create .env file
218
- env:
219
- ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
220
- run: |
221
- printf '%s\n' "$ENV_CONTENT" > ${{ env.ENV_FILE_PATH }}
222
- echo "✅ ${{ env.ENV_FILE_PATH }} file created"
223
-
224
223
  - name: Create Secrets.xcconfig file
225
224
  env:
226
225
  SECRETS_XCCONFIG_CONTENT: ${{ secrets.SECRETS_XCCONFIG }}
@@ -237,6 +236,7 @@ jobs:
237
236
  # 테스트 빌드 버전 설정 (version.yml에서 동적 읽기)
238
237
  - name: 테스트 빌드 버전 설정
239
238
  id: test_version
239
+ working-directory: ${{ github.workspace }} # version_manager.py가 레포 루트 version.yml을 읽어야 함
240
240
  run: |
241
241
  # version_manager.py로 버전 가져오기 (프로덕션 워크플로우와 동일한 방식)
242
242
  VERSION=$(python3 .github/scripts/version_manager.py get | tail -n 1)
@@ -309,6 +309,7 @@ jobs:
309
309
  # 릴리즈 노트 생성
310
310
  - name: 릴리즈 노트 생성
311
311
  id: release_notes
312
+ working-directory: ${{ github.workspace }} # final_release_notes.txt(upload 짝)는 레포 루트 기준
312
313
  run: |
313
314
  BUILD_NUMBER="${{ steps.test_version.outputs.build_number }}"
314
315
  BRANCH_NAME="${{ steps.issue_info.outputs.branch_name }}"
@@ -358,11 +359,10 @@ jobs:
358
359
  with:
359
360
  name: project-files
360
361
  path: |
361
- ${{ env.ENV_FILE_PATH }}
362
- ios/Flutter/Secrets.xcconfig
363
- pubspec.yaml
364
- lib/
365
- assets/
362
+ ${{ env.FLUTTER_PROJECT_DIR }}/ios/Flutter/Secrets.xcconfig
363
+ ${{ env.FLUTTER_PROJECT_DIR }}/pubspec.yaml
364
+ ${{ env.FLUTTER_PROJECT_DIR }}/lib/
365
+ ${{ env.FLUTTER_PROJECT_DIR }}/assets/
366
366
  retention-days: 1
367
367
 
368
368
  # 진행 상황 업데이트 - 준비 실패
@@ -419,6 +419,9 @@ jobs:
419
419
  build-ios-test:
420
420
  name: iOS 테스트 빌드
421
421
  runs-on: macos-26
422
+ defaults:
423
+ run:
424
+ working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
422
425
  needs: [notify-start, prepare-test-build]
423
426
  if: |
424
427
  !cancelled() &&
@@ -517,15 +520,20 @@ jobs:
517
520
  uses: actions/download-artifact@v8
518
521
  with:
519
522
  name: project-files
520
- path: .
523
+ path: ${{ env.FLUTTER_PROJECT_DIR }}
521
524
 
522
- - name: Ensure .env file exists
525
+ - name: Prepare env file
523
526
  env:
524
527
  ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
525
528
  run: |
526
- if [ ! -f "${{ env.ENV_FILE_PATH }}" ]; then
527
- printf '%s\n' "$ENV_CONTENT" > ${{ env.ENV_FILE_PATH }}
528
- echo "✅ ${{ env.ENV_FILE_PATH }} file created (fallback)"
529
+ if [ "$ENV_MODE" = "dotenv" ]; then
530
+ printf '%s\n' "$ENV_CONTENT" > "$ENV_FILE_PATH"
531
+ echo "✅ $ENV_FILE_PATH file created"
532
+ elif [ -n "$ENV_CONTENT" ]; then
533
+ DART_DEFINE_FILE="$RUNNER_TEMP/dart-define.env"
534
+ printf '%s\n' "$ENV_CONTENT" > "$DART_DEFINE_FILE"
535
+ echo "DART_DEFINE_FILE=$DART_DEFINE_FILE" >> "$GITHUB_ENV"
536
+ echo "dart-define file prepared"
529
537
  fi
530
538
 
531
539
  - name: Set up Flutter
@@ -538,7 +546,7 @@ jobs:
538
546
  uses: actions/cache@v6
539
547
  with:
540
548
  path: ~/.pub-cache
541
- key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
549
+ key: ${{ runner.os }}-flutter-pub-${{ hashFiles(format('{0}/**/pubspec.lock', env.FLUTTER_PROJECT_DIR)) }}
542
550
  restore-keys: ${{ runner.os }}-flutter-pub-
543
551
 
544
552
  - name: Install dependencies
@@ -579,9 +587,8 @@ jobs:
579
587
  run: |
580
588
  cd ios
581
589
  if [ ! -f "ExportOptions.plist" ]; then
582
- echo "❌ ios/ExportOptions.plist이 없습니다!"
583
- echo "웹 마법사를 실행하여 설정 파일을 생성하세요:"
584
- echo " 브라우저에서 .github/util/flutter/ios-testflight-setup-wizard/index.html 열기"
590
+ echo "❌ ios/ExportOptions.plist가 없습니다!"
591
+ echo "ios/ExportOptions.plist를 저장소에 추가(커밋)하세요. 필요한 Secrets는 워크플로우 상단 주석을 참고하세요."
585
592
  exit 1
586
593
  fi
587
594
  echo "✅ ExportOptions.plist 확인됨"
@@ -593,7 +600,8 @@ jobs:
593
600
  run: |
594
601
  flutter build ios --release --no-codesign \
595
602
  --build-name="${{ needs.prepare-test-build.outputs.version }}" \
596
- --build-number="${{ needs.prepare-test-build.outputs.build_number }}"
603
+ --build-number="${{ needs.prepare-test-build.outputs.build_number }}" \
604
+ ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
597
605
 
598
606
  - name: Create Archive
599
607
  env:
@@ -636,8 +644,8 @@ jobs:
636
644
  with:
637
645
  name: ios-ipa
638
646
  path: |
639
- ios/build/ipa/*.ipa
640
- build-metadata.json
647
+ ${{ env.FLUTTER_PROJECT_DIR }}/ios/build/ipa/*.ipa
648
+ ${{ env.FLUTTER_PROJECT_DIR }}/build-metadata.json
641
649
  retention-days: 1
642
650
 
643
651
  # 진행 상황 업데이트 - IPA 빌드 완료
@@ -755,11 +763,14 @@ jobs:
755
763
  console.log(`❌ 진행 상황 업데이트 완료: IPA 빌드 실패`);
756
764
 
757
765
  # ===================================================================
758
- # TestFlight 배포 (마법사 생성 Fastfile 사용)
766
+ # TestFlight 배포 (ios/fastlane/Fastfile 사용)
759
767
  # ===================================================================
760
768
  deploy-testflight-test:
761
769
  name: TestFlight 테스트 배포
762
770
  runs-on: macos-26
771
+ defaults:
772
+ run:
773
+ working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
763
774
  needs: [notify-start, prepare-test-build, build-ios-test]
764
775
  if: |
765
776
  !cancelled() &&
@@ -784,7 +795,7 @@ jobs:
784
795
  uses: actions/download-artifact@v8
785
796
  with:
786
797
  name: ios-ipa
787
- path: ios/build/ipa/
798
+ path: ${{ env.FLUTTER_PROJECT_DIR }}/ios/build/ipa/
788
799
 
789
800
  - name: Download release notes
790
801
  uses: actions/download-artifact@v8
@@ -796,17 +807,21 @@ jobs:
796
807
  run: |
797
808
  if [ ! -f "ios/fastlane/Fastfile" ]; then
798
809
  echo "❌ ios/fastlane/Fastfile이 없습니다!"
799
- echo "웹 마법사를 실행하여 설정 파일을 생성하세요:"
800
- echo " 브라우저에서 .github/util/flutter/ios-testflight-setup-wizard/index.html 열기"
810
+ echo "ios/fastlane/Fastfile을 저장소에 추가(커밋)하세요. 필요한 Secrets는 워크플로우 상단 주석을 참고하세요."
801
811
  exit 1
802
812
  fi
803
813
  echo "✅ Fastfile 확인됨: ios/fastlane/Fastfile"
804
814
 
805
815
  - name: Install Fastlane
806
- working-directory: ios
816
+ working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/ios
807
817
  run: |
808
- # multi_json: google-apis transitive 의존성이 선언 누락한 upstream 버그 회피 (gemspec 미선언 → Gem::LoadError)
809
- printf 'source "https://rubygems.org"\ngem "fastlane"\ngem "multi_json"\n' > Gemfile
818
+ # 저장소의 Gemfile에 fastlane이 있으면 그대로 쓴다 (Gemfile.lock 커밋 권장). 없으면 생성한다.
819
+ if [ -f Gemfile ] && grep -Eq "['\"]fastlane['\"]" Gemfile; then
820
+ echo "ℹ️ 저장소의 Gemfile을 사용합니다"
821
+ else
822
+ # multi_json: google-apis transitive 의존성이 선언 누락한 upstream 버그 회피 (gemspec 미선언 → Gem::LoadError)
823
+ printf 'source "https://rubygems.org"\ngem "fastlane"\ngem "multi_json"\n' > Gemfile
824
+ fi
810
825
  bundle install
811
826
  echo "✅ Fastlane installed (Bundler)"
812
827
 
@@ -816,7 +831,7 @@ jobs:
816
831
  APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
817
832
  run: |
818
833
  # IPA 파일 찾기
819
- IPA_PATH=$(find $GITHUB_WORKSPACE/ios/build/ipa -name "*.ipa" | head -1)
834
+ IPA_PATH=$(find "$GITHUB_WORKSPACE/${FLUTTER_PROJECT_DIR}/ios/build/ipa" -name "*.ipa" | head -1)
820
835
  echo "📦 Found IPA at: $IPA_PATH"
821
836
 
822
837
  if [ ! -f "$IPA_PATH" ]; then
@@ -825,9 +840,9 @@ jobs:
825
840
  exit 1
826
841
  fi
827
842
 
828
- # Release notes 준비
829
- if [ -f "final_release_notes.txt" ]; then
830
- RELEASE_NOTES=$(cat final_release_notes.txt)
843
+ # Release notes 준비 (final_release_notes.txt는 레포 루트 기준 — working-directory가 FLUTTER_PROJECT_DIR이므로 절대경로 사용)
844
+ if [ -f "$GITHUB_WORKSPACE/final_release_notes.txt" ]; then
845
+ RELEASE_NOTES=$(cat "$GITHUB_WORKSPACE/final_release_notes.txt")
831
846
  echo "📝 Release Notes:"
832
847
  echo "$RELEASE_NOTES"
833
848
  else
@@ -844,7 +859,7 @@ jobs:
844
859
  echo "🔧 IPA_PATH: $IPA_PATH"
845
860
  echo "🔧 API_KEY_PATH: $API_KEY_PATH"
846
861
 
847
- # Fastlane 실행 (마법사가 생성한 Fastfile 사용)
862
+ # Fastlane 실행 (ios/fastlane/Fastfile 사용)
848
863
  cd ios
849
864
  bundle exec fastlane upload_testflight
850
865