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.
- package/README.md +83 -4
- package/package.json +1 -1
- package/payload/config/breaking-changes.json +24 -1
- package/payload/flutter-app/android/fastlane/Fastfile.playstore +86 -0
- package/payload/flutter-app/ios/ExportOptions.plist +31 -0
- package/payload/flutter-app/ios/fastlane/Fastfile +117 -0
- package/payload/scripts/__pycache__/changelog_manager.cpython-314.pyc +0 -0
- package/payload/scripts/__pycache__/issue_helper.cpython-314.pyc +0 -0
- package/payload/scripts/__pycache__/version_manager.cpython-314.pyc +0 -0
- package/payload/scripts/changelog_manager.py +2 -2
- package/payload/scripts/version_manager.py +2 -2
- package/payload/version.yml.template +1 -0
- package/payload/workflows/common/secret-backup/PROJECT-COMMON-SECRET-FILE-UPLOAD.yaml +12 -12
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +48 -20
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +44 -19
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +28 -36
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +33 -46
- package/payload/workflows/flutter/{PROJECT-FLUTTER-SUH-LAB-APP-BUILD-TRIGGER.yaml → PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml} +38 -38
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +123 -32
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +57 -42
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +61 -40
- package/payload/workflows/go/PROJECT-GO-CI.yaml +47 -0
- package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +40 -40
- package/payload/workflows/next/PROJECT-NEXT-CI.yaml +47 -0
- package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +47 -0
- package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +40 -40
- package/payload/workflows/react/PROJECT-REACT-CI.yaml +47 -0
- package/payload/workflows/spring/nexus/PROJECT-SPRING-GITHUB-PACKAGES-PUBLISH.yml +1 -0
- package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-CI.yml +47 -0
- package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-PUBLISH.yml +1 -0
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +39 -39
- package/src/cli/args.js +37 -0
- package/src/cli/help.js +4 -0
- package/src/commands/doctor.js +55 -2
- package/src/commands/dry-run.js +10 -0
- package/src/commands/full.js +28 -3
- package/src/commands/interactive-flutter.js +95 -0
- package/src/commands/interactive.js +51 -5
- package/src/commands/status.js +20 -2
- package/src/context.js +6 -0
- package/src/core/breaking.js +7 -3
- package/src/core/copy/flutter-app.js +42 -0
- package/src/core/copy/workflows.js +21 -9
- package/src/core/detect-fs.js +9 -1
- package/src/core/flutter-options.js +118 -0
- package/src/core/installed-stores.js +12 -0
- package/src/core/verify.js +2 -2
- package/src/core/version-yml.js +40 -5
- package/src/core/wizard-env.js +26 -4
- package/src/index.js +32 -1
- package/src/ui/env-plan.js +8 -5
- package/src/ui/prompts.js +88 -3
- package/src/ui/status-cards.js +13 -1
- 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 (
|
|
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. "
|
|
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.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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:
|
|
525
|
+
- name: Prepare env file
|
|
523
526
|
env:
|
|
524
527
|
ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
|
|
525
528
|
run: |
|
|
526
|
-
if [
|
|
527
|
-
printf '%s\n' "$ENV_CONTENT" > $
|
|
528
|
-
echo "✅ $
|
|
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('
|
|
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 배포 (
|
|
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
|
-
#
|
|
809
|
-
|
|
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 실행 (
|
|
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 (
|
|
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.
|
|
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
|
-
#
|
|
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:
|
|
124
|
+
- name: Prepare env file
|
|
125
|
+
env:
|
|
126
|
+
ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
|
|
127
127
|
run: |
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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('
|
|
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.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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:
|
|
280
|
+
- name: Prepare env file
|
|
281
|
+
env:
|
|
282
|
+
ENV_CONTENT: ${{ secrets.ENV_FILE || secrets.ENV }}
|
|
277
283
|
run: |
|
|
278
|
-
if [
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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('
|
|
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 배포 (
|
|
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
|
-
#
|
|
427
|
-
|
|
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
|