project-auto-wizard 0.1.25 → 0.1.31
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 +1 -1
- package/package.json +1 -1
- package/payload/scripts/__pycache__/changelog_manager.cpython-314.pyc +0 -0
- package/payload/scripts/__pycache__/version_manager.cpython-314.pyc +0 -0
- package/payload/scripts/version_manager.py +7 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +13 -2
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +24 -10
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +3 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +29 -26
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +29 -3
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +21 -17
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +22 -11
- package/src/commands/interactive.js +4 -3
- package/src/core/detect-fs.js +8 -1
- package/src/core/detect.js +39 -0
- package/src/index.js +3 -3
- package/src/ui/summary.js +5 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ npx project-auto-wizard
|
|
|
17
17
|
[](package.json)
|
|
18
18
|
|
|
19
19
|
<!-- AUTO-VERSION-SECTION: DO NOT EDIT MANUALLY -->
|
|
20
|
-
## 최신 버전 : v0.1.
|
|
20
|
+
## 최신 버전 : v0.1.30 (2026-08-08)
|
|
21
21
|
|
|
22
22
|
[전체 버전 기록 보기](CHANGELOG.md)
|
|
23
23
|
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -221,6 +221,13 @@ def get_version_code():
|
|
|
221
221
|
|
|
222
222
|
|
|
223
223
|
def set_version_code(new_code):
|
|
224
|
+
current = read_scalar_key("version_code", None)
|
|
225
|
+
if current not in (None, "", "null"):
|
|
226
|
+
try:
|
|
227
|
+
if int(new_code) < int(current):
|
|
228
|
+
log(f"WARNING: version_code {new_code} is lower than current {current} — writing anyway (regression?)")
|
|
229
|
+
except ValueError:
|
|
230
|
+
pass
|
|
224
231
|
text = read_text()
|
|
225
232
|
pattern = re.compile(r'^version_code:[ \t]*.*$', re.MULTILINE)
|
|
226
233
|
replacement = f'version_code: {new_code} # app build number'
|
|
@@ -112,7 +112,11 @@ jobs:
|
|
|
112
112
|
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
113
113
|
|
|
114
114
|
- name: Install dependencies
|
|
115
|
-
run:
|
|
115
|
+
run: |
|
|
116
|
+
flutter pub get
|
|
117
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
118
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
119
|
+
fi
|
|
116
120
|
|
|
117
121
|
# version_manager.py를 사용하여 현재 버전 정보 가져오기
|
|
118
122
|
- name: 현재 버전 정보 가져오기
|
|
@@ -265,7 +269,11 @@ jobs:
|
|
|
265
269
|
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
266
270
|
|
|
267
271
|
- name: Install dependencies
|
|
268
|
-
run:
|
|
272
|
+
run: |
|
|
273
|
+
flutter pub get
|
|
274
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
275
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
276
|
+
fi
|
|
269
277
|
|
|
270
278
|
- name: Set up Java
|
|
271
279
|
uses: actions/setup-java@v5
|
|
@@ -328,6 +336,9 @@ jobs:
|
|
|
328
336
|
echo "🔄 Flutter clean 및 의존성 재설치..."
|
|
329
337
|
flutter clean
|
|
330
338
|
flutter pub get
|
|
339
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
340
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
341
|
+
fi
|
|
331
342
|
|
|
332
343
|
# local.properties에 버전 정보 추가 (Flutter 플래그가 무시되는 문제 해결)
|
|
333
344
|
echo "🔧 local.properties에 버전 정보 설정..."
|
|
@@ -47,7 +47,7 @@ env:
|
|
|
47
47
|
PROJECT_TYPE: "flutter"
|
|
48
48
|
# Flutter 루트 경로(레포 루트 기준). 단일레포면 ".", 모노레포면 "app" 등.
|
|
49
49
|
# 통합 시점에 integrator의 flutter-root resolver가 project_paths.flutter 값으로 1회 치환.
|
|
50
|
-
|
|
50
|
+
FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
|
|
51
51
|
# 배포 모드 (store_only | store_prepare | store_submit)
|
|
52
52
|
DEPLOY_MODE: ${{ github.event.inputs.deploy_mode || vars.ANDROID_DEPLOY_MODE || 'store_only' }}
|
|
53
53
|
|
|
@@ -57,7 +57,7 @@ jobs:
|
|
|
57
57
|
runs-on: ubuntu-latest
|
|
58
58
|
defaults:
|
|
59
59
|
run:
|
|
60
|
-
working-directory: ${{ env.
|
|
60
|
+
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
|
|
61
61
|
# CHANGELOG 워크플로우가 성공한 경우에만 실행
|
|
62
62
|
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
|
|
63
63
|
|
|
@@ -97,7 +97,11 @@ jobs:
|
|
|
97
97
|
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
98
98
|
|
|
99
99
|
- name: Install dependencies
|
|
100
|
-
run:
|
|
100
|
+
run: |
|
|
101
|
+
flutter pub get
|
|
102
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
103
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
104
|
+
fi
|
|
101
105
|
|
|
102
106
|
# version_manager.py를 사용하여 현재 버전 정보 가져오기
|
|
103
107
|
- name: 현재 버전 정보 가져오기
|
|
@@ -167,6 +171,7 @@ jobs:
|
|
|
167
171
|
name: release-notes
|
|
168
172
|
path: final_release_notes.txt
|
|
169
173
|
retention-days: 1
|
|
174
|
+
if-no-files-found: error
|
|
170
175
|
|
|
171
176
|
# 프로젝트 파일들을 아티팩트로 업로드
|
|
172
177
|
# .env는 보안상 아티팩트에 포함하지 않고, 각 job에서 secrets로 재생성
|
|
@@ -179,6 +184,7 @@ jobs:
|
|
|
179
184
|
lib/
|
|
180
185
|
assets/
|
|
181
186
|
retention-days: 1
|
|
187
|
+
if-no-files-found: error
|
|
182
188
|
|
|
183
189
|
build-android:
|
|
184
190
|
name: Android AAB 빌드
|
|
@@ -186,7 +192,7 @@ jobs:
|
|
|
186
192
|
needs: prepare-build
|
|
187
193
|
defaults:
|
|
188
194
|
run:
|
|
189
|
-
working-directory: ${{ env.
|
|
195
|
+
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
|
|
190
196
|
|
|
191
197
|
steps:
|
|
192
198
|
- name: Checkout repository
|
|
@@ -255,7 +261,11 @@ jobs:
|
|
|
255
261
|
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
256
262
|
|
|
257
263
|
- name: Install dependencies
|
|
258
|
-
run:
|
|
264
|
+
run: |
|
|
265
|
+
flutter pub get
|
|
266
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
267
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
268
|
+
fi
|
|
259
269
|
|
|
260
270
|
- name: Set up Java
|
|
261
271
|
uses: actions/setup-java@v5
|
|
@@ -318,6 +328,9 @@ jobs:
|
|
|
318
328
|
echo "🔄 Flutter clean 및 의존성 재설치..."
|
|
319
329
|
flutter clean
|
|
320
330
|
flutter pub get
|
|
331
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
332
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
333
|
+
fi
|
|
321
334
|
|
|
322
335
|
# local.properties에 버전 정보 추가 (Flutter 플래그가 무시되는 문제 해결)
|
|
323
336
|
echo "🔧 local.properties에 버전 정보 설정..."
|
|
@@ -503,8 +516,9 @@ jobs:
|
|
|
503
516
|
uses: actions/upload-artifact@v7
|
|
504
517
|
with:
|
|
505
518
|
name: android-aab
|
|
506
|
-
path: ${{ env.
|
|
519
|
+
path: ${{ env.FLUTTER_PROJECT_DIR }}/build/app/outputs/bundle/release/app-release.aab
|
|
507
520
|
retention-days: 1
|
|
521
|
+
if-no-files-found: error
|
|
508
522
|
|
|
509
523
|
deploy-playstore:
|
|
510
524
|
name: Play Store 내부 테스트 배포
|
|
@@ -512,7 +526,7 @@ jobs:
|
|
|
512
526
|
needs: [prepare-build, build-android]
|
|
513
527
|
defaults:
|
|
514
528
|
run:
|
|
515
|
-
working-directory: ${{ env.
|
|
529
|
+
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
|
|
516
530
|
|
|
517
531
|
steps:
|
|
518
532
|
- name: Checkout repository
|
|
@@ -533,7 +547,7 @@ jobs:
|
|
|
533
547
|
uses: actions/download-artifact@v8
|
|
534
548
|
with:
|
|
535
549
|
name: android-aab
|
|
536
|
-
path: ${{ env.
|
|
550
|
+
path: ${{ env.FLUTTER_PROJECT_DIR }}/build/app/outputs/bundle/release/
|
|
537
551
|
|
|
538
552
|
# 릴리즈 노트 다운로드
|
|
539
553
|
- name: Download release notes
|
|
@@ -549,7 +563,7 @@ jobs:
|
|
|
549
563
|
ruby-version: "3.4.1"
|
|
550
564
|
|
|
551
565
|
- name: Install Fastlane
|
|
552
|
-
working-directory: ${{ env.
|
|
566
|
+
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/android
|
|
553
567
|
run: |
|
|
554
568
|
# multi_json: google-apis transitive 의존성이 선언 누락한 upstream 버그 회피 (gemspec 미선언 → Gem::LoadError)
|
|
555
569
|
printf 'source "https://rubygems.org"\ngem "fastlane"\ngem "multi_json"\n' > Gemfile
|
|
@@ -650,7 +664,7 @@ jobs:
|
|
|
650
664
|
|
|
651
665
|
if [ -f "$GITHUB_WORKSPACE/final_release_notes.txt" ]; then
|
|
652
666
|
# Google Play release notes 한도(500 글자)에 맞춰 절단 (마진 두어 480자)
|
|
653
|
-
# final_release_notes.txt·truncate 스크립트는 레포 루트 기준 — working-directory가
|
|
667
|
+
# final_release_notes.txt·truncate 스크립트는 레포 루트 기준 — working-directory가 FLUTTER_PROJECT_DIR이므로 절대경로 사용
|
|
654
668
|
bash "$GITHUB_WORKSPACE/.github/scripts/truncate_release_notes.sh" "$GITHUB_WORKSPACE/final_release_notes.txt" 480 char
|
|
655
669
|
cp "$GITHUB_WORKSPACE/final_release_notes.txt" "$CHANGELOG_DIR/${VERSION_CODE}.txt"
|
|
656
670
|
echo "✅ Changelog 준비 완료 (버전 코드: $VERSION_CODE)"
|
|
@@ -333,11 +333,11 @@ jobs:
|
|
|
333
333
|
KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
|
|
334
334
|
run: |
|
|
335
335
|
cat > android/key.properties << EOF
|
|
336
|
-
storeFile=keystore/key.jks
|
|
337
|
-
storePassword=$STORE_PASSWORD
|
|
338
|
-
keyAlias=$KEY_ALIAS
|
|
339
|
-
keyPassword=$KEY_PASSWORD
|
|
340
|
-
EOF
|
|
336
|
+
storeFile=keystore/key.jks
|
|
337
|
+
storePassword=$STORE_PASSWORD
|
|
338
|
+
keyAlias=$KEY_ALIAS
|
|
339
|
+
keyPassword=$KEY_PASSWORD
|
|
340
|
+
EOF
|
|
341
341
|
echo "✅ key.properties 생성 완료"
|
|
342
342
|
|
|
343
343
|
# google-services.json 생성 (선택적)
|
|
@@ -388,6 +388,9 @@ EOF
|
|
|
388
388
|
- name: Install dependencies
|
|
389
389
|
run: |
|
|
390
390
|
flutter pub get
|
|
391
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
392
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
393
|
+
fi
|
|
391
394
|
echo "✅ Dependencies installed"
|
|
392
395
|
|
|
393
396
|
# Gradle 셋업
|
|
@@ -634,24 +637,24 @@ EOF
|
|
|
634
637
|
BUILD_DATE=$(date '+%Y-%m-%d %H:%M:%S')
|
|
635
638
|
|
|
636
639
|
cat > build-info.txt << EOF
|
|
637
|
-
테스트 APK 빌드 정보
|
|
640
|
+
테스트 APK 빌드 정보
|
|
638
641
|
|
|
639
|
-
빌드 번호: #$BUILD_NUMBER
|
|
640
|
-
EOF
|
|
642
|
+
빌드 번호: #$BUILD_NUMBER
|
|
643
|
+
EOF
|
|
641
644
|
|
|
642
645
|
if [ -n "$PR_NUMBER" ]; then
|
|
643
646
|
cat >> build-info.txt << EOF
|
|
644
|
-
PR 번호: #$PR_NUMBER
|
|
645
|
-
EOF
|
|
647
|
+
PR 번호: #$PR_NUMBER
|
|
648
|
+
EOF
|
|
646
649
|
fi
|
|
647
650
|
|
|
648
651
|
cat >> build-info.txt << EOF
|
|
649
|
-
브랜치: $BRANCH_NAME
|
|
650
|
-
커밋: $COMMIT_SHORT
|
|
651
|
-
전체 커밋 해시: $COMMIT_SHA
|
|
652
|
-
빌드 날짜: $BUILD_DATE
|
|
652
|
+
브랜치: $BRANCH_NAME
|
|
653
|
+
커밋: $COMMIT_SHORT
|
|
654
|
+
전체 커밋 해시: $COMMIT_SHA
|
|
655
|
+
빌드 날짜: $BUILD_DATE
|
|
653
656
|
|
|
654
|
-
EOF
|
|
657
|
+
EOF
|
|
655
658
|
|
|
656
659
|
# 이슈 정보가 있으면 추가
|
|
657
660
|
if [ -n "${{ needs.prepare-test-build.outputs.issue_number }}" ]; then
|
|
@@ -660,11 +663,11 @@ EOF
|
|
|
660
663
|
ISSUE_URL="${{ needs.prepare-test-build.outputs.issue_url }}"
|
|
661
664
|
|
|
662
665
|
cat >> build-info.txt << EOF
|
|
663
|
-
관련 이슈:
|
|
664
|
-
- #$ISSUE_NUMBER: $ISSUE_TITLE
|
|
665
|
-
- URL: $ISSUE_URL
|
|
666
|
+
관련 이슈:
|
|
667
|
+
- #$ISSUE_NUMBER: $ISSUE_TITLE
|
|
668
|
+
- URL: $ISSUE_URL
|
|
666
669
|
|
|
667
|
-
EOF
|
|
670
|
+
EOF
|
|
668
671
|
fi
|
|
669
672
|
|
|
670
673
|
echo "📋 빌드 정보 파일 생성 완료:"
|
|
@@ -674,13 +677,13 @@ EOF
|
|
|
674
677
|
- name: Create build metadata file
|
|
675
678
|
run: |
|
|
676
679
|
cat > build-metadata.json << EOF
|
|
677
|
-
{
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
683
|
-
EOF
|
|
680
|
+
{
|
|
681
|
+
"pr_number": "${{ needs.prepare-test-build.outputs.pr_number }}",
|
|
682
|
+
"build_number": "${{ needs.prepare-test-build.outputs.build_number }}",
|
|
683
|
+
"issue_number": "${{ needs.prepare-test-build.outputs.issue_number }}",
|
|
684
|
+
"branch_name": "${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}"
|
|
685
|
+
}
|
|
686
|
+
EOF
|
|
684
687
|
cat build-metadata.json
|
|
685
688
|
|
|
686
689
|
# APK와 빌드 정보를 아티팩트로 업로드 (브랜치명 포함)
|
|
@@ -278,6 +278,9 @@ jobs:
|
|
|
278
278
|
- name: Install dependencies
|
|
279
279
|
run: |
|
|
280
280
|
flutter pub get
|
|
281
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
282
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
283
|
+
fi
|
|
281
284
|
echo "✅ Dependencies installed"
|
|
282
285
|
|
|
283
286
|
# Flutter Analyze 실행
|
|
@@ -377,6 +380,9 @@ jobs:
|
|
|
377
380
|
- name: Install dependencies
|
|
378
381
|
run: |
|
|
379
382
|
flutter pub get
|
|
383
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
384
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
385
|
+
fi
|
|
380
386
|
echo "✅ Dependencies installed"
|
|
381
387
|
|
|
382
388
|
# Java 설정
|
|
@@ -398,12 +404,12 @@ jobs:
|
|
|
398
404
|
chmod +x gradlew
|
|
399
405
|
echo "✅ Gradle wrapper permissions set"
|
|
400
406
|
|
|
401
|
-
# APK 빌드
|
|
407
|
+
# APK 빌드 (CI는 빌드 검증 목적이므로 서명 불필요한 debug 빌드 사용)
|
|
402
408
|
- name: Build APK
|
|
403
409
|
id: build
|
|
404
410
|
run: |
|
|
405
|
-
echo "📦 Flutter APK 빌드
|
|
406
|
-
flutter build apk --
|
|
411
|
+
echo "📦 Flutter APK 빌드 시작 (debug)..."
|
|
412
|
+
flutter build apk --debug
|
|
407
413
|
echo "✅ APK 빌드 완료"
|
|
408
414
|
ls -la ./build/app/outputs/flutter-apk/ || true
|
|
409
415
|
|
|
@@ -456,6 +462,23 @@ jobs:
|
|
|
456
462
|
- name: Select Xcode version
|
|
457
463
|
run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
458
464
|
|
|
465
|
+
- name: Install iOS device platform
|
|
466
|
+
env:
|
|
467
|
+
# xcode-select만으로는 서브프로세스에 전파되지 않아 명시적으로 지정
|
|
468
|
+
DEVELOPER_DIR: /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
|
|
469
|
+
run: |
|
|
470
|
+
echo "=== iPhoneOS.platform check ==="
|
|
471
|
+
ls "$DEVELOPER_DIR/Platforms/iPhoneOS.platform/" 2>/dev/null && echo "iPhoneOS.platform EXISTS" || echo "iPhoneOS.platform NOT FOUND"
|
|
472
|
+
echo "=== Installing MobileDevice packages ==="
|
|
473
|
+
sudo installer -pkg /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Resources/Packages/MobileDevice.pkg -target /
|
|
474
|
+
sudo installer -pkg /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Resources/Packages/MobileDeviceDevelopment.pkg -target /
|
|
475
|
+
echo "=== Accepting Xcode license ==="
|
|
476
|
+
sudo xcodebuild -license accept
|
|
477
|
+
echo "=== Initializing Xcode components ==="
|
|
478
|
+
xcrun simctl list >/dev/null 2>&1 || true
|
|
479
|
+
echo "=== Downloading iOS platform ==="
|
|
480
|
+
xcodebuild -downloadPlatform iOS
|
|
481
|
+
|
|
459
482
|
# .env 파일 생성
|
|
460
483
|
- name: Create .env file
|
|
461
484
|
run: |
|
|
@@ -488,6 +511,9 @@ jobs:
|
|
|
488
511
|
- name: Install dependencies
|
|
489
512
|
run: |
|
|
490
513
|
flutter pub get
|
|
514
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
515
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
516
|
+
fi
|
|
491
517
|
echo "✅ Dependencies installed"
|
|
492
518
|
|
|
493
519
|
# Ruby 및 CocoaPods 설정
|
|
@@ -319,13 +319,13 @@ jobs:
|
|
|
319
319
|
echo "commit_hash=$COMMIT_SHORT" >> $GITHUB_OUTPUT
|
|
320
320
|
|
|
321
321
|
cat > final_release_notes.txt << EOF
|
|
322
|
-
테스트 빌드 #$BUILD_NUMBER
|
|
322
|
+
테스트 빌드 #$BUILD_NUMBER
|
|
323
323
|
|
|
324
|
-
브랜치: $BRANCH_NAME
|
|
325
|
-
커밋: $COMMIT_SHORT
|
|
326
|
-
날짜: $BUILD_DATE
|
|
324
|
+
브랜치: $BRANCH_NAME
|
|
325
|
+
커밋: $COMMIT_SHORT
|
|
326
|
+
날짜: $BUILD_DATE
|
|
327
327
|
|
|
328
|
-
EOF
|
|
328
|
+
EOF
|
|
329
329
|
|
|
330
330
|
# 이슈 정보가 있으면 추가
|
|
331
331
|
if [ -n "${{ steps.issue_info.outputs.issue_number }}" ]; then
|
|
@@ -334,11 +334,11 @@ EOF
|
|
|
334
334
|
ISSUE_URL="${{ steps.issue_info.outputs.issue_url }}"
|
|
335
335
|
|
|
336
336
|
cat >> final_release_notes.txt << EOF
|
|
337
|
-
관련 이슈:
|
|
338
|
-
- #$ISSUE_NUMBER: $ISSUE_TITLE
|
|
339
|
-
- URL: $ISSUE_URL
|
|
337
|
+
관련 이슈:
|
|
338
|
+
- #$ISSUE_NUMBER: $ISSUE_TITLE
|
|
339
|
+
- URL: $ISSUE_URL
|
|
340
340
|
|
|
341
|
-
EOF
|
|
341
|
+
EOF
|
|
342
342
|
fi
|
|
343
343
|
|
|
344
344
|
echo "📋 생성된 릴리즈 노트:"
|
|
@@ -542,7 +542,11 @@ EOF
|
|
|
542
542
|
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
543
543
|
|
|
544
544
|
- name: Install dependencies
|
|
545
|
-
run:
|
|
545
|
+
run: |
|
|
546
|
+
flutter pub get
|
|
547
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
548
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
549
|
+
fi
|
|
546
550
|
|
|
547
551
|
- name: Set up Ruby
|
|
548
552
|
uses: ruby/setup-ruby@v1
|
|
@@ -618,13 +622,13 @@ EOF
|
|
|
618
622
|
- name: Create build metadata file
|
|
619
623
|
run: |
|
|
620
624
|
cat > build-metadata.json << EOF
|
|
621
|
-
{
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
EOF
|
|
625
|
+
{
|
|
626
|
+
"pr_number": "${{ needs.prepare-test-build.outputs.pr_number }}",
|
|
627
|
+
"build_number": "${{ needs.prepare-test-build.outputs.build_number }}",
|
|
628
|
+
"issue_number": "${{ needs.prepare-test-build.outputs.issue_number }}",
|
|
629
|
+
"branch_name": "${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}"
|
|
630
|
+
}
|
|
631
|
+
EOF
|
|
628
632
|
cat build-metadata.json
|
|
629
633
|
|
|
630
634
|
- name: Upload IPA artifact
|
|
@@ -75,7 +75,7 @@ on:
|
|
|
75
75
|
# 🔧 프로젝트별 설정 (아래 값들을 수정하세요)
|
|
76
76
|
# ============================================
|
|
77
77
|
env:
|
|
78
|
-
|
|
78
|
+
FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
|
|
79
79
|
FLUTTER_VERSION: "3.35.5"
|
|
80
80
|
XCODE_VERSION: "26.0"
|
|
81
81
|
PROJECT_TYPE: "flutter"
|
|
@@ -102,7 +102,7 @@ jobs:
|
|
|
102
102
|
runs-on: macos-26
|
|
103
103
|
defaults:
|
|
104
104
|
run:
|
|
105
|
-
working-directory: ${{ env.
|
|
105
|
+
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
|
|
106
106
|
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name != 'workflow_run' }}
|
|
107
107
|
|
|
108
108
|
outputs:
|
|
@@ -154,7 +154,11 @@ jobs:
|
|
|
154
154
|
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
155
155
|
|
|
156
156
|
- name: Install dependencies
|
|
157
|
-
run:
|
|
157
|
+
run: |
|
|
158
|
+
flutter pub get
|
|
159
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
160
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
161
|
+
fi
|
|
158
162
|
|
|
159
163
|
- name: Set up Ruby
|
|
160
164
|
uses: ruby/setup-ruby@v1
|
|
@@ -207,6 +211,7 @@ jobs:
|
|
|
207
211
|
name: release-notes
|
|
208
212
|
path: final_release_notes.txt
|
|
209
213
|
retention-days: 1
|
|
214
|
+
if-no-files-found: error
|
|
210
215
|
|
|
211
216
|
- name: Upload project files
|
|
212
217
|
uses: actions/upload-artifact@v7
|
|
@@ -219,6 +224,7 @@ jobs:
|
|
|
219
224
|
lib/
|
|
220
225
|
assets/
|
|
221
226
|
retention-days: 1
|
|
227
|
+
if-no-files-found: error
|
|
222
228
|
|
|
223
229
|
# ============================================
|
|
224
230
|
# iOS 빌드 (xcodebuild 직접 사용)
|
|
@@ -228,7 +234,7 @@ jobs:
|
|
|
228
234
|
runs-on: macos-26
|
|
229
235
|
defaults:
|
|
230
236
|
run:
|
|
231
|
-
working-directory: ${{ env.
|
|
237
|
+
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
|
|
232
238
|
needs: prepare-build
|
|
233
239
|
|
|
234
240
|
steps:
|
|
@@ -290,7 +296,11 @@ jobs:
|
|
|
290
296
|
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
291
297
|
|
|
292
298
|
- name: Install dependencies
|
|
293
|
-
run:
|
|
299
|
+
run: |
|
|
300
|
+
flutter pub get
|
|
301
|
+
if grep -q "build_runner" pubspec.yaml; then
|
|
302
|
+
dart run build_runner build --delete-conflicting-outputs
|
|
303
|
+
fi
|
|
294
304
|
|
|
295
305
|
- name: Set up Ruby
|
|
296
306
|
uses: ruby/setup-ruby@v1
|
|
@@ -361,8 +371,9 @@ jobs:
|
|
|
361
371
|
uses: actions/upload-artifact@v7
|
|
362
372
|
with:
|
|
363
373
|
name: ios-ipa
|
|
364
|
-
path: ${{ env.
|
|
374
|
+
path: ${{ env.FLUTTER_PROJECT_DIR }}/ios/build/ipa/*.ipa
|
|
365
375
|
retention-days: 1
|
|
376
|
+
if-no-files-found: error
|
|
366
377
|
|
|
367
378
|
# ============================================
|
|
368
379
|
# TestFlight 배포 (마법사 생성 Fastfile 사용)
|
|
@@ -372,7 +383,7 @@ jobs:
|
|
|
372
383
|
runs-on: macos-26
|
|
373
384
|
defaults:
|
|
374
385
|
run:
|
|
375
|
-
working-directory: ${{ env.
|
|
386
|
+
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}
|
|
376
387
|
needs: [prepare-build, build-ios]
|
|
377
388
|
|
|
378
389
|
steps:
|
|
@@ -391,7 +402,7 @@ jobs:
|
|
|
391
402
|
uses: actions/download-artifact@v8
|
|
392
403
|
with:
|
|
393
404
|
name: ios-ipa
|
|
394
|
-
path: ${{ env.
|
|
405
|
+
path: ${{ env.FLUTTER_PROJECT_DIR }}/ios/build/ipa/
|
|
395
406
|
|
|
396
407
|
- name: Download release notes
|
|
397
408
|
uses: actions/download-artifact@v8
|
|
@@ -410,7 +421,7 @@ jobs:
|
|
|
410
421
|
echo "✅ Fastfile 확인됨: ios/fastlane/Fastfile"
|
|
411
422
|
|
|
412
423
|
- name: Install Fastlane
|
|
413
|
-
working-directory: ${{ env.
|
|
424
|
+
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/ios
|
|
414
425
|
run: |
|
|
415
426
|
# multi_json: google-apis transitive 의존성이 선언 누락한 upstream 버그 회피 (gemspec 미선언 → Gem::LoadError)
|
|
416
427
|
printf 'source "https://rubygems.org"\ngem "fastlane"\ngem "multi_json"\n' > Gemfile
|
|
@@ -424,14 +435,14 @@ jobs:
|
|
|
424
435
|
SKIP_WAITING_FOR_BUILD_PROCESSING: ${{ env.SKIP_WAITING_FOR_BUILD_PROCESSING }}
|
|
425
436
|
run: |
|
|
426
437
|
# IPA 절대경로 탐색
|
|
427
|
-
IPA_PATH=$(find "$GITHUB_WORKSPACE/${
|
|
438
|
+
IPA_PATH=$(find "$GITHUB_WORKSPACE/${FLUTTER_PROJECT_DIR}/ios/build/ipa" -name "*.ipa" | head -1)
|
|
428
439
|
echo "📦 Found IPA at: $IPA_PATH"
|
|
429
440
|
|
|
430
441
|
if [ ! -f "$IPA_PATH" ]; then
|
|
431
442
|
echo "❌ IPA 파일을 찾을 수 없습니다!"; ls -la ios/build/ipa/ || true; exit 1
|
|
432
443
|
fi
|
|
433
444
|
|
|
434
|
-
# Release notes 준비 (final_release_notes.txt·truncate 스크립트는 레포 루트 기준 — working-directory가
|
|
445
|
+
# Release notes 준비 (final_release_notes.txt·truncate 스크립트는 레포 루트 기준 — working-directory가 FLUTTER_PROJECT_DIR이므로 절대경로 사용)
|
|
435
446
|
if [ -f "$GITHUB_WORKSPACE/final_release_notes.txt" ]; then
|
|
436
447
|
# App Store Connect "What to Test" 한도(4000 바이트)에 맞춰 절단 (마진 두어 3800byte)
|
|
437
448
|
bash "$GITHUB_WORKSPACE/.github/scripts/truncate_release_notes.sh" "$GITHUB_WORKSPACE/final_release_notes.txt" 3800 byte
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { existsSync, readFileSync } from "node:fs";
|
|
7
7
|
import { resolvePayloadRoot, assertPayload, readTemplateVersion } from "../core/assets.js";
|
|
8
|
-
import { detectTypes, detectVersion, detectDefaultBranch, detectRepoName, makeResolvers } from "../core/detect-fs.js";
|
|
8
|
+
import { detectTypes, detectVersion, detectDefaultBranch, detectRepoName, makeResolvers, detectBuildNumber } from "../core/detect-fs.js";
|
|
9
9
|
import { parseExisting } from "../core/version-yml.js";
|
|
10
10
|
import { runBreakingCheck } from "../core/breaking-check.js";
|
|
11
11
|
import { resolveProjectPaths } from "../core/paths-resolve.js";
|
|
@@ -85,7 +85,6 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), payloadRoot
|
|
|
85
85
|
let version = (existing?.version) || detectVersion(cwd);
|
|
86
86
|
let branch = detectDefaultBranch(cwd);
|
|
87
87
|
const repoName = detectRepoName(cwd);
|
|
88
|
-
const versionCode = existing?.versionCode ?? 1; // 기존 빌드번호 보존
|
|
89
88
|
// 선택 워크플로우 초기값: version.yml 저장 옵션 (.sh read_template_options L2361 등가)
|
|
90
89
|
let includeNexus = existing?.options?.nexus ?? false;
|
|
91
90
|
let includeSecretBackup = existing?.options?.secretBackup ?? false;
|
|
@@ -166,6 +165,8 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), payloadRoot
|
|
|
166
165
|
}
|
|
167
166
|
}
|
|
168
167
|
|
|
168
|
+
const versionCode = existing?.versionCode ?? detectBuildNumber(cwd, { types }) ?? 1; // 기존 빌드번호 보존, 신규 통합 시 프로젝트 파일에서 감지 (이슈 #41)
|
|
169
|
+
|
|
169
170
|
// 신규 질문 ① — 브랜치 설정 (DESIGN-SPEC §4). full/workflows만 질문, version은 기본값 기록.
|
|
170
171
|
// 저장값(version.yml metadata.template.branches)이 있으면 재질문 없이 재사용 (업데이트 모드).
|
|
171
172
|
let branches = existing?.branches
|
|
@@ -250,7 +251,7 @@ export async function runInteractive(baseCtx, { cwd = process.cwd(), payloadRoot
|
|
|
250
251
|
|
|
251
252
|
// 완료 요약 (.sh print_summary L5438)
|
|
252
253
|
io.summary?.({
|
|
253
|
-
mode, types, version, branches,
|
|
254
|
+
mode, types, version, versionCode, branches,
|
|
254
255
|
copiedFiles: result?.workflows?.copiedFiles ?? [],
|
|
255
256
|
gitignoreUpdated: result?.gitignoreUpdated === true,
|
|
256
257
|
});
|
package/src/core/detect-fs.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
4
|
import { join, basename } from "node:path";
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
|
-
import { detectTypesFromMarkers, detectVersionFromFiles } from "./detect.js";
|
|
6
|
+
import { detectTypesFromMarkers, detectVersionFromFiles, detectBuildNumberFromFiles } from "./detect.js";
|
|
7
7
|
import { parseExisting } from "./version-yml.js";
|
|
8
8
|
|
|
9
9
|
const hasFile = (root) => (rel) => existsSync(join(root, rel));
|
|
@@ -35,6 +35,13 @@ export function detectVersion(root, { warn = (m) => console.error(m) } = {}) {
|
|
|
35
35
|
return detectVersionFromFiles({ read, readJson, gitTag, warn });
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// 빌드 번호 감지 — 신규 통합 시 pubspec.yaml/build.gradle/app.json에서 실제 빌드 번호를 읽는다 (이슈 #41).
|
|
39
|
+
export function detectBuildNumber(root, { types = [], warn = (m) => console.error(m) } = {}) {
|
|
40
|
+
const read = readFile(root);
|
|
41
|
+
const readJson = (rel) => { const c = read(rel); try { return c ? JSON.parse(c) : null; } catch { return null; } };
|
|
42
|
+
return detectBuildNumberFromFiles({ types, read, readJson, warn });
|
|
43
|
+
}
|
|
44
|
+
|
|
38
45
|
// 기본 브랜치 감지 — symbolic-ref → remote show → main.
|
|
39
46
|
export function detectDefaultBranch(root) {
|
|
40
47
|
let b = gitOut(root, ["symbolic-ref", "refs/remotes/origin/HEAD"]);
|
package/src/core/detect.js
CHANGED
|
@@ -62,3 +62,42 @@ export function markerForType(type) {
|
|
|
62
62
|
export function extraMarkers(type) {
|
|
63
63
|
return { python: ["setup.py", "requirements.txt"], spring: ["build.gradle.kts", "pom.xml"] }[type] || [];
|
|
64
64
|
}
|
|
65
|
+
|
|
66
|
+
// 빌드 번호 감지 (이슈 #41) — 신규 통합 시 pubspec.yaml/build.gradle/app.json에 이미 기록된
|
|
67
|
+
// 빌드 번호를 읽어 version_code가 항상 1로 초기화되는 걸 막는다. types 배열에서 먼저 매칭되는
|
|
68
|
+
// 첫 타입만 사용한다(다른 감지 로직의 types[0]=primary 관례와 동일). read(rel)=>string|null,
|
|
69
|
+
// readJson(rel)=>object|null 로 주입.
|
|
70
|
+
export function detectBuildNumberFromFiles({ types = [], read, readJson, warn }) {
|
|
71
|
+
const tryFlutter = () => {
|
|
72
|
+
const content = read("pubspec.yaml");
|
|
73
|
+
if (content == null) return null;
|
|
74
|
+
const m = content.match(/^version:\s*\d+\.\d+\.\d+\+(\d+)/m);
|
|
75
|
+
if (m) return parseInt(m[1], 10);
|
|
76
|
+
warn?.("⚠️ pubspec.yaml에 빌드 번호(+N)가 없어 version_code를 감지하지 못했습니다 — 기본값 1을 사용합니다. 실제 빌드 번호를 확인하세요.");
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
const tryReactNative = () => {
|
|
80
|
+
const content = read("android/app/build.gradle");
|
|
81
|
+
if (content == null) return null;
|
|
82
|
+
// 앵커 + m 플래그로 한 줄 전체가 "versionCode N"인 라인만 매칭 — 주석 처리된
|
|
83
|
+
// "// versionCode 2"나 다른 블록의 versionCode 참조에 오매칭되지 않도록 함.
|
|
84
|
+
const m = content.match(/^\s*versionCode\s+(\d+)\s*$/m);
|
|
85
|
+
if (m) return parseInt(m[1], 10);
|
|
86
|
+
warn?.("⚠️ android/app/build.gradle에 versionCode가 없어 version_code를 감지하지 못했습니다 — 기본값 1을 사용합니다. 실제 빌드 번호를 확인하세요.");
|
|
87
|
+
return null;
|
|
88
|
+
};
|
|
89
|
+
const tryExpo = () => {
|
|
90
|
+
const data = readJson?.("app.json");
|
|
91
|
+
if (data == null) return null;
|
|
92
|
+
const code = data?.expo?.android?.versionCode;
|
|
93
|
+
if (Number.isInteger(code)) return code;
|
|
94
|
+
warn?.("⚠️ app.json의 expo.android.versionCode가 없어 version_code를 감지하지 못했습니다 — 기본값 1을 사용합니다. 실제 빌드 번호를 확인하세요.");
|
|
95
|
+
return null;
|
|
96
|
+
};
|
|
97
|
+
for (const t of types) {
|
|
98
|
+
if (t === "flutter") return tryFlutter();
|
|
99
|
+
if (t === "react-native") return tryReactNative();
|
|
100
|
+
if (t === "react-native-expo") return tryExpo();
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
package/src/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { parseArgs, parsePathsCsv, CliError } from "./cli/args.js";
|
|
|
9
9
|
import { HELP_TEXT } from "./cli/help.js";
|
|
10
10
|
import { createContext } from "./context.js";
|
|
11
11
|
import { resolvePayloadRoot, assertPayload, readTemplateVersion } from "./core/assets.js";
|
|
12
|
-
import { detectTypes, detectVersion, detectDefaultBranch, detectRepoName, makeResolvers } from "./core/detect-fs.js";
|
|
12
|
+
import { detectTypes, detectVersion, detectDefaultBranch, detectRepoName, makeResolvers, detectBuildNumber } from "./core/detect-fs.js";
|
|
13
13
|
import { parseExisting } from "./core/version-yml.js";
|
|
14
14
|
import { runBreakingCheck } from "./core/breaking-check.js";
|
|
15
15
|
import { resolveProjectPaths } from "./core/paths-resolve.js";
|
|
@@ -237,7 +237,7 @@ export async function run(argv, {
|
|
|
237
237
|
const types = opts.types.length ? opts.types : detectTypes(cwd);
|
|
238
238
|
// version: 기존 version.yml 최우선(SSoT — 재실행 시 덮어쓰기 방지) → CLI 지정 → 파일 감지
|
|
239
239
|
const version = (existing?.version) || opts.version || detectVersion(cwd);
|
|
240
|
-
const versionCode = existing?.versionCode ?? 1; // 기존 빌드번호
|
|
240
|
+
const versionCode = existing?.versionCode ?? detectBuildNumber(cwd, { types }) ?? 1; // 기존 빌드번호 보존, 신규 통합 시 프로젝트 파일에서 감지 (.sh L2208~2221, 이슈 #41)
|
|
241
241
|
const branch = detectDefaultBranch(cwd);
|
|
242
242
|
const repoName = detectRepoName(cwd);
|
|
243
243
|
// 경로 확정 (.sh resolve_project_paths 비대화형 경로 — --paths 우선 → 저장값 → 후보 1개 자동 → 에러)
|
|
@@ -314,7 +314,7 @@ export async function run(argv, {
|
|
|
314
314
|
|
|
315
315
|
// 완료 요약 (.sh print_summary — CLI 모드에서도 출력)
|
|
316
316
|
printSummary({
|
|
317
|
-
mode: opts.mode, types, version, branches,
|
|
317
|
+
mode: opts.mode, types, version, versionCode, branches,
|
|
318
318
|
copiedFiles: result?.workflows?.copiedFiles ?? [],
|
|
319
319
|
gitignoreUpdated: result?.gitignoreUpdated === true,
|
|
320
320
|
});
|
package/src/ui/summary.js
CHANGED
|
@@ -6,7 +6,7 @@ import { paint, A, colorEnabled } from "./ansi.js";
|
|
|
6
6
|
const SEPARATOR = "────────────────────────────────────────";
|
|
7
7
|
|
|
8
8
|
export function printSummary(ctx) {
|
|
9
|
-
const { mode, types = [], version = "", copiedFiles = [], branches = null, gitignoreUpdated = false } = ctx || {};
|
|
9
|
+
const { mode, types = [], version = "", versionCode = null, copiedFiles = [], branches = null, gitignoreUpdated = false } = ctx || {};
|
|
10
10
|
const err = (s = "") => process.stderr.write(`${s}\n`);
|
|
11
11
|
// 색상은 ansi.js의 공용 가드로 통일 (NO_COLOR + stderr TTY 여부)
|
|
12
12
|
const enabled = colorEnabled(process.stderr);
|
|
@@ -56,6 +56,10 @@ export function printSummary(ctx) {
|
|
|
56
56
|
err("");
|
|
57
57
|
err("추가된 파일:");
|
|
58
58
|
err(` 📄 version.yml (버전: ${version}, 타입: ${types.join(",")})`);
|
|
59
|
+
const BUILD_NUMBER_TYPES = new Set(["flutter", "react-native", "react-native-expo"]);
|
|
60
|
+
if (versionCode != null && types.some((t) => BUILD_NUMBER_TYPES.has(t))) {
|
|
61
|
+
err(` 빌드 번호: ${versionCode}`);
|
|
62
|
+
}
|
|
59
63
|
err(" 📝 README.md (버전 섹션 추가)");
|
|
60
64
|
err("");
|
|
61
65
|
err("추가된 워크플로우:");
|