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.
- 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/version.yml.template +1 -0
- 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 +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/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
|
@@ -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
|
|
@@ -37,6 +37,8 @@ env:
|
|
|
37
37
|
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
38
38
|
NODE_VERSION: "20.15.0"
|
|
39
39
|
ENV_FILE: ".env"
|
|
40
|
+
# 모노레포에서 이 프로젝트가 있는 하위 경로 ('.'이면 항상 검증)
|
|
41
|
+
PROJECT_PATH: "." # @wizard auto:project-path
|
|
40
42
|
|
|
41
43
|
permissions:
|
|
42
44
|
contents: read
|
|
@@ -44,9 +46,35 @@ permissions:
|
|
|
44
46
|
issues: write
|
|
45
47
|
|
|
46
48
|
jobs:
|
|
49
|
+
# ===================================================================
|
|
50
|
+
# 변경 감지 (모노레포에서 이 프로젝트 경로가 바뀌었는지 판별)
|
|
51
|
+
# ===================================================================
|
|
52
|
+
changes:
|
|
53
|
+
name: 변경 감지
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
permissions:
|
|
56
|
+
contents: read
|
|
57
|
+
pull-requests: read
|
|
58
|
+
outputs:
|
|
59
|
+
project: ${{ steps.filter.outputs.project }}
|
|
60
|
+
steps:
|
|
61
|
+
- name: Checkout repository
|
|
62
|
+
uses: actions/checkout@v7
|
|
63
|
+
|
|
64
|
+
- name: 프로젝트 경로 변경 여부 판별
|
|
65
|
+
id: filter
|
|
66
|
+
if: ${{ github.event_name != 'workflow_dispatch' }}
|
|
67
|
+
uses: dorny/paths-filter@v4
|
|
68
|
+
with:
|
|
69
|
+
filters: |
|
|
70
|
+
project:
|
|
71
|
+
- '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
|
|
72
|
+
|
|
47
73
|
build:
|
|
48
74
|
name: Next.js 애플리케이션 빌드
|
|
49
75
|
runs-on: ubuntu-latest
|
|
76
|
+
needs: changes
|
|
77
|
+
if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true' }}
|
|
50
78
|
|
|
51
79
|
steps:
|
|
52
80
|
# 1. 소스코드 체크아웃
|
|
@@ -184,3 +212,22 @@ jobs:
|
|
|
184
212
|
echo " 🕐 시간: $(date '+%Y-%m-%d %H:%M:%S UTC')"
|
|
185
213
|
echo ""
|
|
186
214
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
215
|
+
|
|
216
|
+
# ===================================================================
|
|
217
|
+
# CI Gate (required check로 등록할 단일 job)
|
|
218
|
+
# ===================================================================
|
|
219
|
+
ci-gate:
|
|
220
|
+
name: CI Gate
|
|
221
|
+
if: ${{ always() }}
|
|
222
|
+
needs: [changes, build]
|
|
223
|
+
runs-on: ubuntu-latest
|
|
224
|
+
steps:
|
|
225
|
+
- name: 결과 집계
|
|
226
|
+
env:
|
|
227
|
+
RESULTS: ${{ toJSON(needs.*.result) }}
|
|
228
|
+
run: |
|
|
229
|
+
echo "needs 결과: $RESULTS"
|
|
230
|
+
if echo "$RESULTS" | grep -Eq '"(failure|cancelled)"'; then
|
|
231
|
+
echo "실패하거나 취소된 job이 있습니다."
|
|
232
|
+
exit 1
|
|
233
|
+
fi
|
|
@@ -29,14 +29,42 @@ on:
|
|
|
29
29
|
env:
|
|
30
30
|
# 프로젝트 고유 식별자 (컨테이너명, 이미지명, 도메인에 사용)
|
|
31
31
|
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
32
|
+
# 모노레포에서 이 프로젝트가 있는 하위 경로 ('.'이면 항상 검증)
|
|
33
|
+
PROJECT_PATH: "." # @wizard auto:project-path
|
|
32
34
|
|
|
33
35
|
jobs:
|
|
36
|
+
# ===================================================================
|
|
37
|
+
# 변경 감지 (모노레포에서 이 프로젝트 경로가 바뀌었는지 판별)
|
|
38
|
+
# ===================================================================
|
|
39
|
+
changes:
|
|
40
|
+
name: 변경 감지
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
permissions:
|
|
43
|
+
contents: read
|
|
44
|
+
pull-requests: read
|
|
45
|
+
outputs:
|
|
46
|
+
project: ${{ steps.filter.outputs.project }}
|
|
47
|
+
steps:
|
|
48
|
+
- name: Checkout repository
|
|
49
|
+
uses: actions/checkout@v7
|
|
50
|
+
|
|
51
|
+
- name: 프로젝트 경로 변경 여부 판별
|
|
52
|
+
id: filter
|
|
53
|
+
if: ${{ github.event_name != 'workflow_dispatch' }}
|
|
54
|
+
uses: dorny/paths-filter@v4
|
|
55
|
+
with:
|
|
56
|
+
filters: |
|
|
57
|
+
project:
|
|
58
|
+
- '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
|
|
59
|
+
|
|
34
60
|
# ===================================================================
|
|
35
61
|
# 빌드 검증 작업
|
|
36
62
|
# ===================================================================
|
|
37
63
|
build-check:
|
|
38
64
|
name: Python FastAPI 빌드 검증
|
|
39
65
|
runs-on: ubuntu-latest
|
|
66
|
+
needs: changes
|
|
67
|
+
if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true' }}
|
|
40
68
|
|
|
41
69
|
steps:
|
|
42
70
|
# 1. 소스코드 체크아웃
|
|
@@ -77,3 +105,22 @@ jobs:
|
|
|
77
105
|
echo " 🌿 브랜치: ${{ github.ref_name }}"
|
|
78
106
|
echo " 📝 커밋: ${{ github.sha }}"
|
|
79
107
|
echo " ⏰ 검증 시간: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
108
|
+
|
|
109
|
+
# ===================================================================
|
|
110
|
+
# CI Gate (required check로 등록할 단일 job)
|
|
111
|
+
# ===================================================================
|
|
112
|
+
ci-gate:
|
|
113
|
+
name: CI Gate
|
|
114
|
+
if: ${{ always() }}
|
|
115
|
+
needs: [changes, build-check]
|
|
116
|
+
runs-on: ubuntu-latest
|
|
117
|
+
steps:
|
|
118
|
+
- name: 결과 집계
|
|
119
|
+
env:
|
|
120
|
+
RESULTS: ${{ toJSON(needs.*.result) }}
|
|
121
|
+
run: |
|
|
122
|
+
echo "needs 결과: $RESULTS"
|
|
123
|
+
if echo "$RESULTS" | grep -Eq '"(failure|cancelled)"'; then
|
|
124
|
+
echo "실패하거나 취소된 job이 있습니다."
|
|
125
|
+
exit 1
|
|
126
|
+
fi
|
|
@@ -36,6 +36,8 @@ env:
|
|
|
36
36
|
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
37
37
|
NODE_VERSION: "20.15.0"
|
|
38
38
|
ENV_FILE: ".env"
|
|
39
|
+
# 모노레포에서 이 프로젝트가 있는 하위 경로 ('.'이면 항상 검증)
|
|
40
|
+
PROJECT_PATH: "." # @wizard auto:project-path
|
|
39
41
|
|
|
40
42
|
permissions:
|
|
41
43
|
contents: read
|
|
@@ -43,9 +45,35 @@ permissions:
|
|
|
43
45
|
issues: write
|
|
44
46
|
|
|
45
47
|
jobs:
|
|
48
|
+
# ===================================================================
|
|
49
|
+
# 변경 감지 (모노레포에서 이 프로젝트 경로가 바뀌었는지 판별)
|
|
50
|
+
# ===================================================================
|
|
51
|
+
changes:
|
|
52
|
+
name: 변경 감지
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
permissions:
|
|
55
|
+
contents: read
|
|
56
|
+
pull-requests: read
|
|
57
|
+
outputs:
|
|
58
|
+
project: ${{ steps.filter.outputs.project }}
|
|
59
|
+
steps:
|
|
60
|
+
- name: Checkout repository
|
|
61
|
+
uses: actions/checkout@v7
|
|
62
|
+
|
|
63
|
+
- name: 프로젝트 경로 변경 여부 판별
|
|
64
|
+
id: filter
|
|
65
|
+
if: ${{ github.event_name != 'workflow_dispatch' }}
|
|
66
|
+
uses: dorny/paths-filter@v4
|
|
67
|
+
with:
|
|
68
|
+
filters: |
|
|
69
|
+
project:
|
|
70
|
+
- '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
|
|
71
|
+
|
|
46
72
|
build:
|
|
47
73
|
name: React/Next.js 애플리케이션 빌드
|
|
48
74
|
runs-on: ubuntu-latest
|
|
75
|
+
needs: changes
|
|
76
|
+
if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true' }}
|
|
49
77
|
|
|
50
78
|
steps:
|
|
51
79
|
# 1. 소스코드 체크아웃
|
|
@@ -193,3 +221,22 @@ jobs:
|
|
|
193
221
|
echo "❌ 빌드 중 오류가 발생했습니다."
|
|
194
222
|
echo "💡 Actions 탭에서 'build-log' 아티팩트를 다운로드하여 전체 로그를 확인하세요."
|
|
195
223
|
fi
|
|
224
|
+
|
|
225
|
+
# ===================================================================
|
|
226
|
+
# CI Gate (required check로 등록할 단일 job)
|
|
227
|
+
# ===================================================================
|
|
228
|
+
ci-gate:
|
|
229
|
+
name: CI Gate
|
|
230
|
+
if: ${{ always() }}
|
|
231
|
+
needs: [changes, build]
|
|
232
|
+
runs-on: ubuntu-latest
|
|
233
|
+
steps:
|
|
234
|
+
- name: 결과 집계
|
|
235
|
+
env:
|
|
236
|
+
RESULTS: ${{ toJSON(needs.*.result) }}
|
|
237
|
+
run: |
|
|
238
|
+
echo "needs 결과: $RESULTS"
|
|
239
|
+
if echo "$RESULTS" | grep -Eq '"(failure|cancelled)"'; then
|
|
240
|
+
echo "실패하거나 취소된 job이 있습니다."
|
|
241
|
+
exit 1
|
|
242
|
+
fi
|
|
@@ -26,11 +26,39 @@ permissions:
|
|
|
26
26
|
|
|
27
27
|
env:
|
|
28
28
|
JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:@jdk
|
|
29
|
+
# 모노레포에서 이 프로젝트가 있는 하위 경로 ('.'이면 항상 검증)
|
|
30
|
+
PROJECT_PATH: "." # @wizard auto:project-path
|
|
29
31
|
|
|
30
32
|
jobs:
|
|
33
|
+
# ===================================================================
|
|
34
|
+
# 변경 감지 (모노레포에서 이 프로젝트 경로가 바뀌었는지 판별)
|
|
35
|
+
# ===================================================================
|
|
36
|
+
changes:
|
|
37
|
+
name: 변경 감지
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
permissions:
|
|
40
|
+
contents: read
|
|
41
|
+
pull-requests: read
|
|
42
|
+
outputs:
|
|
43
|
+
project: ${{ steps.filter.outputs.project }}
|
|
44
|
+
steps:
|
|
45
|
+
- name: Checkout repository
|
|
46
|
+
uses: actions/checkout@v7
|
|
47
|
+
|
|
48
|
+
- name: 프로젝트 경로 변경 여부 판별
|
|
49
|
+
id: filter
|
|
50
|
+
if: ${{ github.event_name != 'workflow_dispatch' }}
|
|
51
|
+
uses: dorny/paths-filter@v4
|
|
52
|
+
with:
|
|
53
|
+
filters: |
|
|
54
|
+
project:
|
|
55
|
+
- '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
|
|
56
|
+
|
|
31
57
|
build-check:
|
|
32
58
|
name: Build Verification
|
|
33
59
|
runs-on: ubuntu-latest
|
|
60
|
+
needs: changes
|
|
61
|
+
if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true' }}
|
|
34
62
|
|
|
35
63
|
steps:
|
|
36
64
|
- name: Checkout Repository
|
|
@@ -318,3 +346,22 @@ jobs:
|
|
|
318
346
|
run: |
|
|
319
347
|
echo "❌ 빌드가 실패했습니다. 위의 에러 메시지를 확인해주세요."
|
|
320
348
|
exit 1
|
|
349
|
+
|
|
350
|
+
# ===================================================================
|
|
351
|
+
# CI Gate (required check로 등록할 단일 job)
|
|
352
|
+
# ===================================================================
|
|
353
|
+
ci-gate:
|
|
354
|
+
name: CI Gate
|
|
355
|
+
if: ${{ always() }}
|
|
356
|
+
needs: [changes, build-check]
|
|
357
|
+
runs-on: ubuntu-latest
|
|
358
|
+
steps:
|
|
359
|
+
- name: 결과 집계
|
|
360
|
+
env:
|
|
361
|
+
RESULTS: ${{ toJSON(needs.*.result) }}
|
|
362
|
+
run: |
|
|
363
|
+
echo "needs 결과: $RESULTS"
|
|
364
|
+
if echo "$RESULTS" | grep -Eq '"(failure|cancelled)"'; then
|
|
365
|
+
echo "실패하거나 취소된 job이 있습니다."
|
|
366
|
+
exit 1
|
|
367
|
+
fi
|
package/src/cli/args.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// CLI 인자 파싱 (.sh top-level while-case 등가) — template_integrator.sh 842~920.
|
|
2
2
|
import { VALID_TYPES, VALID_MODES } from "../context.js";
|
|
3
3
|
import { DEPLOY_STYLES, isDeployStyle, NO_DEPLOY_STYLE } from "../core/deploy-style.js";
|
|
4
|
+
import {
|
|
5
|
+
ENV_MODES, DEPLOY_MODES, STORE_PLATFORMS, NO_STORE, isEnvMode, isDeployMode, parseStoreList,
|
|
6
|
+
} from "../core/flutter-options.js";
|
|
4
7
|
|
|
5
8
|
// argv(process.argv.slice(2)) → 파싱 결과. 오류 시 throw(호출부에서 exit 1).
|
|
6
9
|
export function parseArgs(argv) {
|
|
@@ -16,6 +19,10 @@ export function parseArgs(argv) {
|
|
|
16
19
|
mainBranch: "", // 릴리스 브랜치 (--main-branch). 빈값=감지된 default branch
|
|
17
20
|
developBranch: "", // 개발 브랜치 (--develop-branch). 빈값=develop
|
|
18
21
|
deployStyle: "", // 서버 배포 방식 (--deploy-style). 빈값=version.yml 저장값 → simple
|
|
22
|
+
flutterEnvMode: "", // Flutter 환경변수 방식 (--flutter-env-mode). 빈값=저장값 → 신규 dart-define/기존 dotenv
|
|
23
|
+
flutterStore: null, // Flutter 스토어 배포 대상 string[] (--flutter-store). null=미지정, none → []
|
|
24
|
+
androidDeployMode: "", // Play Store 배포 모드 (--android-deploy-mode). 빈값=저장값 → store_only
|
|
25
|
+
iosDeployMode: "", // iOS 배포 모드 (--ios-deploy-mode). 빈값=저장값 → store_only
|
|
19
26
|
force: false,
|
|
20
27
|
help: false,
|
|
21
28
|
showVersion: false, // -v/--version → 패키지 버전 출력 (npm 관례)
|
|
@@ -84,6 +91,36 @@ export function parseArgs(argv) {
|
|
|
84
91
|
}
|
|
85
92
|
result.deployStyle = v; break;
|
|
86
93
|
}
|
|
94
|
+
case "--flutter-env-mode": {
|
|
95
|
+
const v = args.shift();
|
|
96
|
+
if (!isEnvMode(v)) {
|
|
97
|
+
throw new CliError(`--flutter-env-mode 값이 올바르지 않습니다: ${v ?? "(없음)"} (${ENV_MODES.join(" | ")})`);
|
|
98
|
+
}
|
|
99
|
+
result.flutterEnvMode = v; break;
|
|
100
|
+
}
|
|
101
|
+
case "--flutter-store": {
|
|
102
|
+
const v = args.shift();
|
|
103
|
+
// 빈 문자열은 parseStoreList가 []로 보지만, 스토어를 안 고르겠다는 뜻은 명시적인 none으로만 받는다.
|
|
104
|
+
const stores = v ? parseStoreList(v) : null;
|
|
105
|
+
if (stores === null) {
|
|
106
|
+
throw new CliError(`--flutter-store 값이 올바르지 않습니다: ${v || "(없음)"} (${[STORE_PLATFORMS.join(","), ...STORE_PLATFORMS, NO_STORE].join(" | ")})`);
|
|
107
|
+
}
|
|
108
|
+
result.flutterStore = stores; break;
|
|
109
|
+
}
|
|
110
|
+
case "--android-deploy-mode": {
|
|
111
|
+
const v = args.shift();
|
|
112
|
+
if (!isDeployMode(v)) {
|
|
113
|
+
throw new CliError(`--android-deploy-mode 값이 올바르지 않습니다: ${v ?? "(없음)"} (${DEPLOY_MODES.join(" | ")})`);
|
|
114
|
+
}
|
|
115
|
+
result.androidDeployMode = v; break;
|
|
116
|
+
}
|
|
117
|
+
case "--ios-deploy-mode": {
|
|
118
|
+
const v = args.shift();
|
|
119
|
+
if (!isDeployMode(v)) {
|
|
120
|
+
throw new CliError(`--ios-deploy-mode 값이 올바르지 않습니다: ${v ?? "(없음)"} (${DEPLOY_MODES.join(" | ")})`);
|
|
121
|
+
}
|
|
122
|
+
result.iosDeployMode = v; break;
|
|
123
|
+
}
|
|
87
124
|
case "--nexus":
|
|
88
125
|
if (seenFlags.has("--no-nexus")) throw new CliError("--nexus와 --no-nexus는 동시에 지정할 수 없습니다");
|
|
89
126
|
seenFlags.add("--nexus"); result.includeNexus = true; break;
|
package/src/cli/help.js
CHANGED
|
@@ -17,6 +17,10 @@ export const HELP_TEXT = `project-auto-wizard — One command DevOps: GitHub-nat
|
|
|
17
17
|
--main-branch B 릴리스 브랜치 (기본: 감지된 default branch)
|
|
18
18
|
--develop-branch B 개발 브랜치 (기본: develop). 릴리스 브랜치와 같으면 trunk-based 모드
|
|
19
19
|
--deploy-style STYLE 서버 배포 방식: simple | nginx | traefik | none (기본: simple)
|
|
20
|
+
--flutter-env-mode MODE Flutter 환경변수 방식: dart-define | dotenv (기본: 신규 설치 dart-define, 기존 설치는 저장값·dotenv 유지)
|
|
21
|
+
--flutter-store CSV Flutter 스토어 배포 대상: android,ios | android | ios | none (기본: 둘 다 설치)
|
|
22
|
+
--android-deploy-mode MODE Play Store 배포 모드: store_only | store_prepare | store_submit (기본: store_only)
|
|
23
|
+
--ios-deploy-mode MODE iOS 배포 모드: store_only | store_prepare | store_submit (기본: store_only)
|
|
20
24
|
--nexus / --no-nexus 라이브러리 publish 워크플로우 포함/제외 (Nexus + GitHub Packages)
|
|
21
25
|
--secret-backup / --no-secret-backup Secret 백업 워크플로우 포함/제외
|
|
22
26
|
--semver-auto / --no-semver-auto 커밋 타입 기반 자동 major/minor/patch 승격 (기본: 사용함)
|