project-auto-wizard 0.9.1 → 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 (54) 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/scripts/changelog_manager.py +2 -2
  11. package/payload/scripts/version_manager.py +2 -2
  12. package/payload/version.yml.template +1 -0
  13. package/payload/workflows/common/secret-backup/PROJECT-COMMON-SECRET-FILE-UPLOAD.yaml +12 -12
  14. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +48 -20
  15. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +44 -19
  16. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +28 -36
  17. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +33 -46
  18. package/payload/workflows/flutter/{PROJECT-FLUTTER-SUH-LAB-APP-BUILD-TRIGGER.yaml → PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml} +38 -38
  19. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +123 -32
  20. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +57 -42
  21. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +61 -40
  22. package/payload/workflows/go/PROJECT-GO-CI.yaml +47 -0
  23. package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +40 -40
  24. package/payload/workflows/next/PROJECT-NEXT-CI.yaml +47 -0
  25. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +47 -0
  26. package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +40 -40
  27. package/payload/workflows/react/PROJECT-REACT-CI.yaml +47 -0
  28. package/payload/workflows/spring/nexus/PROJECT-SPRING-GITHUB-PACKAGES-PUBLISH.yml +1 -0
  29. package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-CI.yml +47 -0
  30. package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-PUBLISH.yml +1 -0
  31. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +39 -39
  32. package/src/cli/args.js +37 -0
  33. package/src/cli/help.js +4 -0
  34. package/src/commands/doctor.js +55 -2
  35. package/src/commands/dry-run.js +10 -0
  36. package/src/commands/full.js +28 -3
  37. package/src/commands/interactive-flutter.js +95 -0
  38. package/src/commands/interactive.js +51 -5
  39. package/src/commands/status.js +20 -2
  40. package/src/context.js +6 -0
  41. package/src/core/breaking.js +7 -3
  42. package/src/core/copy/flutter-app.js +42 -0
  43. package/src/core/copy/workflows.js +21 -9
  44. package/src/core/detect-fs.js +9 -1
  45. package/src/core/flutter-options.js +118 -0
  46. package/src/core/installed-stores.js +12 -0
  47. package/src/core/verify.js +2 -2
  48. package/src/core/version-yml.js +40 -5
  49. package/src/core/wizard-env.js +26 -4
  50. package/src/index.js +32 -1
  51. package/src/ui/env-plan.js +8 -5
  52. package/src/ui/prompts.js +88 -3
  53. package/src/ui/status-cards.js +13 -1
  54. package/src/ui/summary.js +24 -5
@@ -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:
@@ -115,7 +118,7 @@ jobs:
115
118
  '',
116
119
  '### 💡 확인 사항',
117
120
  '1. 브랜치가 원격 저장소에 push되었는지 확인하세요',
118
- '2. "Guide by SUH-LAB" 댓글의 브랜치명이 올바른지 확인하세요',
121
+ '2. "Issue Helper" 댓글의 브랜치명이 올바른지 확인하세요',
119
122
  '3. 브랜치명에 오타가 없는지 확인하세요',
120
123
  '',
121
124
  `🔗 [워크플로우 로그](${runUrl})`
@@ -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
 
@@ -5,17 +5,14 @@
5
5
  #
6
6
  # 이 워크플로우는 Flutter iOS 앱을 빌드하여 TestFlight에 자동 배포합니다.
7
7
  #
8
- # ★ 마법사 우선 아키텍처 ★
9
- # - 빌드에 필요한 설정 파일들은 웹 마법사가 생성합니다
10
- # - 워크플로우는 마법사가 생성한 파일들을 그대로 사용합니다
11
- # - 마법사 경로: .github/util/flutter/ios-testflight-setup-wizard/index.html
12
- # (브라우저에서 열어서 사용)
8
+ # 설정 파일(ios/fastlane/Fastfile, ios/ExportOptions.plist)은 project-auto-wizard가
9
+ # 없을 때만 설치하며, 이미 있으면 덮어쓰지 않습니다.
13
10
  #
14
11
  # 빌드 파이프라인:
15
12
  # 1. flutter build ios --no-codesign (Flutter 빌드)
16
13
  # 2. xcodebuild archive (Xcode 아카이브 생성)
17
14
  # 3. xcodebuild -exportArchive (IPA 생성, ExportOptions.plist 사용)
18
- # 4. fastlane deploy (마법사 생성 Fastfile 사용, DEPLOY_MODE에 따라 TestFlight/심사 제출)
15
+ # 4. fastlane deploy (ios/fastlane/Fastfile 사용, DEPLOY_MODE에 따라 TestFlight/심사 제출)
19
16
  #
20
17
  # ===================================================================
21
18
  # 🔑 필수 GitHub Secrets
@@ -31,6 +28,8 @@
31
28
  # APP_STORE_CONNECT_API_KEY_ID: API Key ID (10자리)
32
29
  # APP_STORE_CONNECT_ISSUER_ID: Issuer ID (UUID 형식)
33
30
  # APP_STORE_CONNECT_API_KEY_BASE64: AuthKey_XXXXXX.p8 파일 (base64 인코딩)
31
+ # IOS_BUNDLE_ID (Secret 우선, 없으면 Variable): 앱 번들 ID —
32
+ # store_prepare/store_submit을 쓰면 필수(비어 있으면 Fastfile이 명확한 오류로 중단)
34
33
  #
35
34
  # 📝 환경 설정 (선택):
36
35
  # ENV_FILE (선택): .env 파일 내용
@@ -40,17 +39,13 @@
40
39
  # 🛠️ 초기 설정 방법
41
40
  # ===================================================================
42
41
  #
43
- # 1. 웹 마법사 실행:
44
- # 브라우저에서 .github/util/flutter/ios-testflight-setup-wizard/index.html 열기
45
- # → 필요한 정보 입력 후 설정 파일 다운로드
42
+ # 1. GitHub Secrets 설정 (위 목록 참고)
46
43
  #
47
- # 2. GitHub Secrets 설정 (위 목록 참고)
48
- #
49
- # 3. 생성된 파일 커밋:
44
+ # 2. ios/ExportOptions.plist의 플레이스홀더(팀 ID·번들 ID·프로비저닝 프로파일 이름)를 채워 커밋:
50
45
  # git add ios/
51
46
  # git commit -m "chore: iOS TestFlight 배포 설정"
52
47
  #
53
- # 4. {{MAIN_BRANCH}} 브랜치로 푸시하여 배포 시작
48
+ # 3. {{MAIN_BRANCH}} 브랜치로 푸시하여 배포 시작
54
49
  #
55
50
  # ===================================================================
56
51
 
@@ -59,6 +54,7 @@ name: PROJECT-iOS-TestFlight-Deploy
59
54
  on:
60
55
  push:
61
56
  branches: ["{{MAIN_BRANCH}}"]
57
+ # @wizard paths-anchor (모노레포일 때 integrator가 paths 필터를 여기 주입)
62
58
  workflow_dispatch:
63
59
  inputs:
64
60
  deploy_mode:
@@ -76,6 +72,8 @@ on:
76
72
  # ============================================
77
73
  env:
78
74
  FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
75
+ # 환경변수 전달 방식 (dart-define | dotenv) — 설치 시 선택값으로 자동 설정
76
+ ENV_MODE: "dart-define" # @wizard auto:flutter-env-mode
79
77
  FLUTTER_VERSION: "3.35.5"
80
78
  XCODE_VERSION: "26.0"
81
79
  PROJECT_TYPE: "flutter"
@@ -83,7 +81,7 @@ env:
83
81
  # ============================================
84
82
  # 📝 환경 파일 설정
85
83
  # ============================================
86
- # .env 파일 생성 경로 (프로젝트 루트 기준)
84
+ # dotenv 모드에서 .env 파일을 만들 경로 (Flutter 루트 기준)
87
85
  ENV_FILE_PATH: ".env"
88
86
 
89
87
  # ============================================
@@ -94,7 +92,7 @@ env:
94
92
  # ============================================
95
93
  # 📝 배포 모드 (store_only | store_prepare | store_submit)
96
94
  # ============================================
97
- DEPLOY_MODE: ${{ github.event.inputs.deploy_mode || vars.IOS_DEPLOY_MODE || 'store_only' }}
95
+ DEPLOY_MODE: ${{ github.event.inputs.deploy_mode || vars.IOS_DEPLOY_MODE || 'store_only' }} # @wizard fallback:ios-deploy-mode
98
96
 
99
97
  jobs:
100
98
  prepare-build:
@@ -123,12 +121,19 @@ jobs:
123
121
  - name: Select Xcode version
124
122
  run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
125
123
 
126
- - name: Create .env file
124
+ - name: Prepare env file
125
+ env:
126
+ ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
127
127
  run: |
128
- cat << 'EOF' > ${{ env.ENV_FILE_PATH }}
129
- ${{ secrets.ENV_FILE || secrets.ENV }}
130
- EOF
131
- echo "✅ ${{ env.ENV_FILE_PATH }} file created"
128
+ if [ "$ENV_MODE" = "dotenv" ]; then
129
+ printf '%s\n' "$ENV_CONTENT" > "$ENV_FILE_PATH"
130
+ echo "✅ $ENV_FILE_PATH file created"
131
+ elif [ -n "$ENV_CONTENT" ]; then
132
+ DART_DEFINE_FILE="$RUNNER_TEMP/dart-define.env"
133
+ printf '%s\n' "$ENV_CONTENT" > "$DART_DEFINE_FILE"
134
+ echo "DART_DEFINE_FILE=$DART_DEFINE_FILE" >> "$GITHUB_ENV"
135
+ echo "dart-define file prepared"
136
+ fi
132
137
 
133
138
  - name: Create Secrets.xcconfig file
134
139
  run: |
@@ -150,7 +155,7 @@ jobs:
150
155
  uses: actions/cache@v6
151
156
  with:
152
157
  path: ~/.pub-cache
153
- key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
158
+ key: ${{ runner.os }}-flutter-pub-${{ hashFiles(format('{0}/**/pubspec.lock', env.FLUTTER_PROJECT_DIR)) }}
154
159
  restore-keys: ${{ runner.os }}-flutter-pub-
155
160
 
156
161
  - name: Install dependencies
@@ -218,11 +223,10 @@ jobs:
218
223
  with:
219
224
  name: project-files
220
225
  path: |
221
- ${{ env.ENV_FILE_PATH }}
222
- ios/Flutter/Secrets.xcconfig
223
- pubspec.yaml
224
- lib/
225
- assets/
226
+ ${{ env.FLUTTER_PROJECT_DIR }}/ios/Flutter/Secrets.xcconfig
227
+ ${{ env.FLUTTER_PROJECT_DIR }}/pubspec.yaml
228
+ ${{ env.FLUTTER_PROJECT_DIR }}/lib/
229
+ ${{ env.FLUTTER_PROJECT_DIR }}/assets/
226
230
  retention-days: 1
227
231
  if-no-files-found: error
228
232
 
@@ -271,15 +275,20 @@ jobs:
271
275
  uses: actions/download-artifact@v8
272
276
  with:
273
277
  name: project-files
274
- path: .
278
+ path: ${{ env.FLUTTER_PROJECT_DIR }}
275
279
 
276
- - name: Ensure .env file exists
280
+ - name: Prepare env file
281
+ env:
282
+ ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
277
283
  run: |
278
- if [ ! -f "${{ env.ENV_FILE_PATH }}" ]; then
279
- cat << 'EOF' > ${{ env.ENV_FILE_PATH }}
280
- ${{ secrets.ENV_FILE || secrets.ENV }}
281
- EOF
282
- echo "✅ ${{ env.ENV_FILE_PATH }} file created (fallback)"
284
+ if [ "$ENV_MODE" = "dotenv" ]; then
285
+ printf '%s\n' "$ENV_CONTENT" > "$ENV_FILE_PATH"
286
+ echo "✅ $ENV_FILE_PATH file created"
287
+ elif [ -n "$ENV_CONTENT" ]; then
288
+ DART_DEFINE_FILE="$RUNNER_TEMP/dart-define.env"
289
+ printf '%s\n' "$ENV_CONTENT" > "$DART_DEFINE_FILE"
290
+ echo "DART_DEFINE_FILE=$DART_DEFINE_FILE" >> "$GITHUB_ENV"
291
+ echo "dart-define file prepared"
283
292
  fi
284
293
 
285
294
  - name: Set up Flutter
@@ -292,7 +301,7 @@ jobs:
292
301
  uses: actions/cache@v6
293
302
  with:
294
303
  path: ~/.pub-cache
295
- key: ${{ runner.os }}-flutter-pub-${{ hashFiles('**/pubspec.lock') }}
304
+ key: ${{ runner.os }}-flutter-pub-${{ hashFiles(format('{0}/**/pubspec.lock', env.FLUTTER_PROJECT_DIR)) }}
296
305
  restore-keys: ${{ runner.os }}-flutter-pub-
297
306
 
298
307
  - name: Install dependencies
@@ -333,6 +342,13 @@ jobs:
333
342
  if [ ! -f "ExportOptions.plist" ]; then
334
343
  exit 1
335
344
  fi
345
+ # 설치기가 만든 템플릿의 플레이스홀더를 채우지 않으면 xcodebuild가 이해하기 어려운 오류로 끝난다
346
+ if grep -Eq '__[A-Z][A-Z0-9_]*__' ExportOptions.plist; then
347
+ echo "❌ ios/ExportOptions.plist에 채워지지 않은 플레이스홀더가 남아 있습니다:"
348
+ grep -En '__[A-Z][A-Z0-9_]*__' ExportOptions.plist
349
+ echo "팀 ID·번들 ID·프로비저닝 프로파일 이름으로 바꿔 커밋한 뒤 다시 실행하세요."
350
+ exit 1
351
+ fi
336
352
 
337
353
  - name: Flutter build (no codesign)
338
354
  env:
@@ -341,7 +357,8 @@ jobs:
341
357
  run: |
342
358
  flutter build ios --release --no-codesign \
343
359
  --build-name="${{ needs.prepare-build.outputs.version }}" \
344
- --build-number="${{ needs.prepare-build.outputs.build_number }}"
360
+ --build-number="${{ needs.prepare-build.outputs.build_number }}" \
361
+ ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
345
362
 
346
363
  - name: Create Archive
347
364
  env:
@@ -376,7 +393,7 @@ jobs:
376
393
  if-no-files-found: error
377
394
 
378
395
  # ============================================
379
- # TestFlight 배포 (마법사 생성 Fastfile 사용)
396
+ # TestFlight 배포 (ios/fastlane/Fastfile 사용)
380
397
  # ============================================
381
398
  deploy-testflight:
382
399
  name: TestFlight 배포
@@ -414,8 +431,7 @@ jobs:
414
431
  run: |
415
432
  if [ ! -f "ios/fastlane/Fastfile" ]; then
416
433
  echo "❌ ios/fastlane/Fastfile이 없습니다!"
417
- echo "웹 마법사를 실행하여 설정 파일을 생성하세요:"
418
- echo " 브라우저에서 .github/util/flutter/ios-testflight-setup-wizard/index.html 열기"
434
+ echo "ios/fastlane/Fastfile을 저장소에 추가(커밋)하세요. 필요한 Secrets는 워크플로우 상단 주석을 참고하세요."
419
435
  exit 1
420
436
  fi
421
437
  echo "✅ Fastfile 확인됨: ios/fastlane/Fastfile"
@@ -423,8 +439,13 @@ jobs:
423
439
  - name: Install Fastlane
424
440
  working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/ios
425
441
  run: |
426
- # multi_json: google-apis transitive 의존성이 선언 누락한 upstream 버그 회피 (gemspec 미선언 → Gem::LoadError)
427
- printf 'source "https://rubygems.org"\ngem "fastlane"\ngem "multi_json"\n' > Gemfile
442
+ # 저장소의 Gemfile에 fastlane이 있으면 그대로 쓴다 (Gemfile.lock 커밋 권장). 없으면 생성한다.
443
+ if [ -f Gemfile ] && grep -Eq "['\"]fastlane['\"]" Gemfile; then
444
+ echo "ℹ️ 저장소의 Gemfile을 사용합니다"
445
+ else
446
+ # multi_json: google-apis transitive 의존성이 선언 누락한 upstream 버그 회피 (gemspec 미선언 → Gem::LoadError)
447
+ printf 'source "https://rubygems.org"\ngem "fastlane"\ngem "multi_json"\n' > Gemfile
448
+ fi
428
449
  bundle install
429
450
  echo "✅ Fastlane installed (Bundler)"
430
451
 
@@ -30,14 +30,42 @@ on:
30
30
  env:
31
31
  # 프로젝트 고유 식별자
32
32
  PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
33
+ # 모노레포에서 이 프로젝트가 있는 하위 경로 ('.'이면 항상 검증)
34
+ PROJECT_PATH: "." # @wizard auto:project-path
33
35
 
34
36
  jobs:
37
+ # ===================================================================
38
+ # 변경 감지 (모노레포에서 이 프로젝트 경로가 바뀌었는지 판별)
39
+ # ===================================================================
40
+ changes:
41
+ name: 변경 감지
42
+ runs-on: ubuntu-latest
43
+ permissions:
44
+ contents: read
45
+ pull-requests: read
46
+ outputs:
47
+ project: ${{ steps.filter.outputs.project }}
48
+ steps:
49
+ - name: Checkout repository
50
+ uses: actions/checkout@v7
51
+
52
+ - name: 프로젝트 경로 변경 여부 판별
53
+ id: filter
54
+ if: ${{ github.event_name != 'workflow_dispatch' }}
55
+ uses: dorny/paths-filter@v4
56
+ with:
57
+ filters: |
58
+ project:
59
+ - '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
60
+
35
61
  # ===================================================================
36
62
  # 빌드 검증 작업
37
63
  # ===================================================================
38
64
  build-check:
39
65
  name: Go 빌드 검증
40
66
  runs-on: ubuntu-latest
67
+ needs: changes
68
+ if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true' }}
41
69
 
42
70
  steps:
43
71
  # 1. 소스코드 체크아웃
@@ -80,3 +108,22 @@ jobs:
80
108
  echo " 🌿 브랜치: ${{ github.ref_name }}"
81
109
  echo " 📝 커밋: ${{ github.sha }}"
82
110
  echo " ⏰ 검증 시간: $(date '+%Y-%m-%d %H:%M:%S')"
111
+
112
+ # ===================================================================
113
+ # CI Gate (required check로 등록할 단일 job)
114
+ # ===================================================================
115
+ ci-gate:
116
+ name: CI Gate
117
+ if: ${{ always() }}
118
+ needs: [changes, build-check]
119
+ runs-on: ubuntu-latest
120
+ steps:
121
+ - name: 결과 집계
122
+ env:
123
+ RESULTS: ${{ toJSON(needs.*.result) }}
124
+ run: |
125
+ echo "needs 결과: $RESULTS"
126
+ if echo "$RESULTS" | grep -Eq '"(failure|cancelled)"'; then
127
+ echo "실패하거나 취소된 job이 있습니다."
128
+ exit 1
129
+ fi