project-auto-wizard 0.10.0 → 0.12.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 +104 -18
- package/package.json +2 -2
- 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 +83 -30
- package/payload/version.yml.template +5 -0
- package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +30 -7
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +36 -8
- package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +25 -1
- 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 +32 -45
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +123 -32
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +56 -41
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +61 -40
- package/payload/workflows/go/PROJECT-GO-CI.yaml +47 -0
- package/payload/workflows/next/PROJECT-NEXT-CI.yaml +47 -0
- package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +47 -0
- 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/src/cli/args.js +44 -0
- package/src/cli/help.js +6 -1
- package/src/commands/doctor.js +59 -5
- 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 +60 -5
- package/src/commands/status.js +21 -2
- package/src/context.js +7 -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 +1 -1
- package/src/core/version-yml.js +50 -6
- package/src/core/wizard-env.js +26 -4
- package/src/index.js +34 -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 +28 -9
|
@@ -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
|
|
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:
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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('
|
|
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:
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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('
|
|
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('
|
|
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:
|
|
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
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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('
|
|
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 (
|
|
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.
|
|
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
|
|