project-auto-wizard 0.12.2 → 0.13.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.ja.md +165 -0
- package/README.ko.md +165 -0
- package/README.md +107 -286
- package/README.zh-CN.md +165 -0
- package/bin/project-auto-wizard.js +1 -2
- package/package.json +7 -3
- package/payload/config/breaking-changes.json +24 -0
- package/payload/config/wizard-prompts.yml +45 -0
- package/payload/scripts/changelog_manager.py +141 -46
- package/payload/scripts/issue_helper.py +34 -5
- package/payload/scripts/version_manager.py +397 -97
- package/payload/version.yml.template +4 -2
- package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +39 -9
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +165 -38
- package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +14 -5
- package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +220 -6
- package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +89 -3
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +78 -16
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +72 -16
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +62 -20
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +51 -13
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +75 -11
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +52 -3
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +60 -11
- package/payload/workflows/go/PROJECT-GO-CI.yaml +10 -1
- package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +148 -28
- package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +45 -4
- package/payload/workflows/next/PROJECT-NEXT-CI.yaml +25 -3
- package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +83 -9
- package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +70 -6
- package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +148 -28
- package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +45 -4
- package/payload/workflows/react/PROJECT-REACT-CI.yaml +25 -3
- package/payload/workflows/react/PROJECT-REACT-CICD.yaml +84 -10
- package/payload/workflows/spring/PROJECT-SPRING-CI.yml +13 -3
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +47 -5
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +47 -5
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +153 -30
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +47 -5
- package/src/cli/args.js +33 -24
- package/src/cli/help.js +16 -4
- package/src/commands/doctor.js +17 -10
- package/src/commands/dry-run.js +54 -1
- package/src/commands/full.js +127 -50
- package/src/commands/interactive-flutter.js +1 -1
- package/src/commands/interactive.js +124 -56
- package/src/commands/purge.js +59 -17
- package/src/commands/status.js +16 -1
- package/src/commands/uninstall.js +58 -13
- package/src/context.js +5 -5
- package/src/core/baseline.js +20 -5
- package/src/core/branches.js +19 -1
- package/src/core/branding.js +1 -1
- package/src/core/breaking-check.js +14 -19
- package/src/core/breaking.js +7 -3
- package/src/core/copy/gitignore.js +20 -12
- package/src/core/copy/readme.js +24 -5
- package/src/core/copy/simple.js +52 -13
- package/src/core/copy/workflows.js +90 -47
- package/src/core/deploy-style.js +83 -11
- package/src/core/detect-fs.js +127 -24
- package/src/core/detect.js +131 -47
- package/src/core/errors.js +3 -0
- package/src/core/flutter-options.js +4 -3
- package/src/core/fsutil.js +23 -6
- package/src/core/logger.js +55 -19
- package/src/core/paths-resolve.js +50 -47
- package/src/core/paths.js +26 -1
- package/src/core/release-options.js +23 -0
- package/src/core/removal-plan.js +75 -19
- package/src/core/types.js +113 -0
- package/src/core/verify.js +85 -19
- package/src/core/version-yml.js +42 -8
- package/src/core/wizard-env.js +21 -14
- package/src/core/wizard-labels.js +34 -25
- package/src/index.js +122 -49
- package/src/ui/ansi.js +3 -1
- package/src/ui/banner.js +12 -8
- package/src/ui/env-plan.js +68 -44
- package/src/ui/prompts.js +22 -12
- package/src/ui/readline-engine.js +109 -22
- package/src/ui/status-cards.js +10 -5
- package/src/ui/summary.js +71 -33
- 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
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# RELEASE_KEYSTORE_PASSWORD: keystore 비밀번호
|
|
11
11
|
# RELEASE_KEY_ALIAS: key alias
|
|
12
12
|
# RELEASE_KEY_PASSWORD: key 비밀번호
|
|
13
|
-
# GOOGLE_SERVICES_JSON: Firebase google-services.json
|
|
13
|
+
# GOOGLE_SERVICES_JSON (선택): Firebase google-services.json
|
|
14
14
|
# GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_BASE64: Play Console 서비스 계정 (base64)
|
|
15
15
|
# ANDROID_PACKAGE_NAME: 앱 패키지명 (Secret 우선, 없으면 Variable) — fastlane에 PACKAGE_NAME으로 전달
|
|
16
16
|
|
|
@@ -25,7 +25,7 @@ on:
|
|
|
25
25
|
deploy_mode:
|
|
26
26
|
description: "배포 모드 (store_only=internal까지 / store_prepare=production draft / store_submit=심사 자동등록)"
|
|
27
27
|
required: false
|
|
28
|
-
default: "store_only"
|
|
28
|
+
default: "store_only" # @wizard auto:android-deploy-mode
|
|
29
29
|
type: choice
|
|
30
30
|
options:
|
|
31
31
|
- store_only
|
|
@@ -37,7 +37,9 @@ env:
|
|
|
37
37
|
# 프로젝트 이름
|
|
38
38
|
APP_ARTIFACT_NAME: "__APP_ARTIFACT_NAME__" # @wizard ask:@repo
|
|
39
39
|
# Flutter/Java 버전
|
|
40
|
-
|
|
40
|
+
# Flutter SDK 버전 — 비워 두면 stable 채널 최신을 쓴다(최신 flutter create 프로젝트의 SDK 제약과 맞음).
|
|
41
|
+
# 특정 버전에 고정하려면 "3.38.9"처럼 적는다. pubspec.yaml의 environment.sdk 범위를 만족해야 한다.
|
|
42
|
+
FLUTTER_VERSION: ""
|
|
41
43
|
JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:17
|
|
42
44
|
PROJECT_TYPE: "flutter"
|
|
43
45
|
# Flutter 루트 경로(레포 루트 기준). 단일레포면 ".", 모노레포면 "app" 등.
|
|
@@ -67,11 +69,34 @@ jobs:
|
|
|
67
69
|
- name: Checkout repository
|
|
68
70
|
uses: actions/checkout@v7
|
|
69
71
|
with:
|
|
70
|
-
ref:
|
|
72
|
+
ref: "{{MAIN_BRANCH}}"
|
|
71
73
|
fetch-depth: 0
|
|
72
74
|
|
|
75
|
+
# 서명·업로드 Secret이 없으면 빌드를 한참 진행한 뒤에야 실패하므로, 빠진 이름을 먼저 모두 알려 주고 멈춘다
|
|
76
|
+
# GOOGLE_SERVICES_JSON은 선택이라 점검하지 않는다
|
|
77
|
+
- name: 배포 사전 점검 (Secret)
|
|
78
|
+
env:
|
|
79
|
+
RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }}
|
|
80
|
+
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
|
|
81
|
+
RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
|
|
82
|
+
RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
|
|
83
|
+
GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_BASE64: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_BASE64 }}
|
|
84
|
+
ANDROID_PACKAGE_NAME: ${{ secrets.ANDROID_PACKAGE_NAME || vars.ANDROID_PACKAGE_NAME }}
|
|
85
|
+
run: |
|
|
86
|
+
MISSING=""
|
|
87
|
+
for NAME in RELEASE_KEYSTORE_BASE64 RELEASE_KEYSTORE_PASSWORD RELEASE_KEY_ALIAS RELEASE_KEY_PASSWORD GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_BASE64; do
|
|
88
|
+
[ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
|
|
89
|
+
done
|
|
90
|
+
# 패키지명은 Secret 대신 Variable로 등록해도 된다
|
|
91
|
+
[ -n "$ANDROID_PACKAGE_NAME" ] || MISSING="$MISSING ANDROID_PACKAGE_NAME(Secret 또는 Variable)"
|
|
92
|
+
if [ -n "$MISSING" ]; then
|
|
93
|
+
echo "::error title=필수 Secret 누락::등록되지 않은 GitHub Secret:$MISSING — Settings → Secrets and variables → Actions에서 등록하세요"
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
echo "✅ 사전 점검 통과: 필수 Secret이 모두 있습니다"
|
|
97
|
+
|
|
73
98
|
- name: Pull latest changes
|
|
74
|
-
run: git pull origin
|
|
99
|
+
run: git pull origin {{MAIN_BRANCH}}
|
|
75
100
|
|
|
76
101
|
- name: Prepare env file
|
|
77
102
|
env:
|
|
@@ -90,6 +115,7 @@ jobs:
|
|
|
90
115
|
- name: Set up Flutter
|
|
91
116
|
uses: subosito/flutter-action@v2
|
|
92
117
|
with:
|
|
118
|
+
channel: stable
|
|
93
119
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
94
120
|
cache: true
|
|
95
121
|
|
|
@@ -199,11 +225,11 @@ jobs:
|
|
|
199
225
|
- name: Checkout repository
|
|
200
226
|
uses: actions/checkout@v7
|
|
201
227
|
with:
|
|
202
|
-
ref:
|
|
228
|
+
ref: "{{MAIN_BRANCH}}"
|
|
203
229
|
fetch-depth: 0
|
|
204
230
|
|
|
205
231
|
- name: Pull latest changes
|
|
206
|
-
run: git pull origin
|
|
232
|
+
run: git pull origin {{MAIN_BRANCH}}
|
|
207
233
|
|
|
208
234
|
- name: Download project files
|
|
209
235
|
uses: actions/download-artifact@v8
|
|
@@ -227,9 +253,27 @@ jobs:
|
|
|
227
253
|
fi
|
|
228
254
|
|
|
229
255
|
- name: Setup Release Keystore
|
|
256
|
+
env:
|
|
257
|
+
RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }}
|
|
258
|
+
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
|
|
259
|
+
RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
|
|
260
|
+
RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
|
|
230
261
|
run: |
|
|
262
|
+
# 빈 Secret으로 빈 keystore를 만들면 어떤 키로 서명됐는지 알 수 없거나 뒤 단계에서 엉뚱한 오류로 끝난다
|
|
263
|
+
MISSING=""
|
|
264
|
+
for name in RELEASE_KEYSTORE_BASE64 RELEASE_KEYSTORE_PASSWORD RELEASE_KEY_ALIAS RELEASE_KEY_PASSWORD; do
|
|
265
|
+
[ -n "${!name}" ] || MISSING="$MISSING $name"
|
|
266
|
+
done
|
|
267
|
+
if [ -n "$MISSING" ]; then
|
|
268
|
+
echo "::error::서명 Secret이 비어 있습니다:$MISSING (워크플로우 상단 주석 참고)"
|
|
269
|
+
exit 1
|
|
270
|
+
fi
|
|
231
271
|
mkdir -p android/app/keystore
|
|
232
|
-
|
|
272
|
+
printf '%s' "$RELEASE_KEYSTORE_BASE64" | base64 -d > android/app/keystore/key.jks
|
|
273
|
+
if [ ! -s android/app/keystore/key.jks ]; then
|
|
274
|
+
echo "::error::RELEASE_KEYSTORE_BASE64를 디코딩한 keystore가 비어 있습니다"
|
|
275
|
+
exit 1
|
|
276
|
+
fi
|
|
233
277
|
echo "✅ Release Keystore 생성 완료"
|
|
234
278
|
ls -la android/app/keystore/
|
|
235
279
|
|
|
@@ -242,18 +286,24 @@ jobs:
|
|
|
242
286
|
keyPassword=${{ secrets.RELEASE_KEY_PASSWORD }}
|
|
243
287
|
EOF
|
|
244
288
|
echo "✅ key.properties 생성 완료"
|
|
245
|
-
cat android/key.properties
|
|
246
289
|
|
|
247
|
-
|
|
290
|
+
# google-services.json 생성 (선택) — 비어 있으면 빈 파일을 만들지 않는다
|
|
291
|
+
- name: Create google-services.json
|
|
292
|
+
env:
|
|
293
|
+
GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }}
|
|
248
294
|
run: |
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
295
|
+
if [ -n "$GOOGLE_SERVICES_JSON" ]; then
|
|
296
|
+
mkdir -p android/app
|
|
297
|
+
printf '%s' "$GOOGLE_SERVICES_JSON" > android/app/google-services.json
|
|
298
|
+
echo "✅ google-services.json created"
|
|
299
|
+
else
|
|
300
|
+
echo "ℹ️ GOOGLE_SERVICES_JSON secret not provided, skipping"
|
|
301
|
+
fi
|
|
253
302
|
|
|
254
303
|
- name: Set up Flutter
|
|
255
304
|
uses: subosito/flutter-action@v2
|
|
256
305
|
with:
|
|
306
|
+
channel: stable
|
|
257
307
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
258
308
|
cache: true
|
|
259
309
|
|
|
@@ -534,13 +584,19 @@ jobs:
|
|
|
534
584
|
- name: Checkout repository
|
|
535
585
|
uses: actions/checkout@v7
|
|
536
586
|
with:
|
|
537
|
-
ref:
|
|
587
|
+
ref: "{{MAIN_BRANCH}}"
|
|
538
588
|
fetch-depth: 0
|
|
539
589
|
|
|
540
590
|
- name: Setup Google Play Service Account
|
|
591
|
+
env:
|
|
592
|
+
SERVICE_ACCOUNT_BASE64: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_BASE64 }}
|
|
541
593
|
run: |
|
|
594
|
+
if [ -z "$SERVICE_ACCOUNT_BASE64" ]; then
|
|
595
|
+
echo "::error::GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_BASE64 Secret이 비어 있습니다"
|
|
596
|
+
exit 1
|
|
597
|
+
fi
|
|
542
598
|
mkdir -p ~/.config/gcloud
|
|
543
|
-
|
|
599
|
+
printf '%s' "$SERVICE_ACCOUNT_BASE64" | base64 --decode > ~/.config/gcloud/service-account.json
|
|
544
600
|
echo "✅ Service Account JSON 설정 완료"
|
|
545
601
|
|
|
546
602
|
- name: Download AAB artifact
|
|
@@ -6,10 +6,7 @@
|
|
|
6
6
|
#
|
|
7
7
|
# 필수 GitHub Secrets
|
|
8
8
|
# ENV_FILE 또는 ENV: .env 파일 내용 (둘 중 하나, ENV_FILE 우선)
|
|
9
|
-
#
|
|
10
|
-
# RELEASE_KEYSTORE_PASSWORD: keystore 비밀번호
|
|
11
|
-
# RELEASE_KEY_ALIAS: key alias
|
|
12
|
-
# RELEASE_KEY_PASSWORD: key 비밀번호
|
|
9
|
+
# DEBUG_KEYSTORE (선택): 서명용 keystore (base64, 비밀번호 android · alias androiddebugkey) — 없으면 빌드마다 새 debug 키로 서명
|
|
13
10
|
# GOOGLE_SERVICES_JSON (선택): Firebase google-services.json
|
|
14
11
|
# SERVER_HOST: 서버 호스트(SMB 접속 주소)
|
|
15
12
|
# SERVER_USER: SMB 사용자명
|
|
@@ -27,7 +24,9 @@ on:
|
|
|
27
24
|
env:
|
|
28
25
|
# 프로젝트 이름 (APK 파일명, 아티팩트명, 히스토리 파일명에 사용)
|
|
29
26
|
APP_ARTIFACT_NAME: "__APP_ARTIFACT_NAME__" # @wizard ask:@repo
|
|
30
|
-
|
|
27
|
+
# Flutter SDK 버전 — 비워 두면 stable 채널 최신을 쓴다(최신 flutter create 프로젝트의 SDK 제약과 맞음).
|
|
28
|
+
# 특정 버전에 고정하려면 "3.38.9"처럼 적는다. pubspec.yaml의 environment.sdk 범위를 만족해야 한다.
|
|
29
|
+
FLUTTER_VERSION: ""
|
|
31
30
|
JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:17
|
|
32
31
|
# Flutter 루트 경로(레포 루트 기준). 단일레포면 ".", 모노레포면 "app" 등 — 설치 시 자동 설정
|
|
33
32
|
FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
|
|
@@ -53,19 +52,50 @@ jobs:
|
|
|
53
52
|
with:
|
|
54
53
|
fetch-depth: 0
|
|
55
54
|
|
|
55
|
+
# 업로드 Secret이 없으면 빌드를 다 마친 뒤 SMB 업로드에서야 실패하므로, 빠진 이름을 먼저 알려 주고 멈춘다
|
|
56
|
+
# 배포 job과 같은 조건 — 배포하지 않는 브랜치의 수동 실행은 빌드만 한다
|
|
57
|
+
# DEBUG_KEYSTORE·GOOGLE_SERVICES_JSON은 선택이라 점검하지 않는다
|
|
58
|
+
- name: 배포 사전 점검 (Secret)
|
|
59
|
+
if: github.ref == 'refs/heads/{{MAIN_BRANCH}}'
|
|
60
|
+
env:
|
|
61
|
+
SERVER_HOST: ${{ secrets.SERVER_HOST }}
|
|
62
|
+
SERVER_USER: ${{ secrets.SERVER_USER }}
|
|
63
|
+
SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
|
|
64
|
+
run: |
|
|
65
|
+
MISSING=""
|
|
66
|
+
for NAME in SERVER_HOST SERVER_USER SERVER_PASSWORD; do
|
|
67
|
+
[ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
|
|
68
|
+
done
|
|
69
|
+
if [ -n "$MISSING" ]; then
|
|
70
|
+
echo "::error title=필수 Secret 누락::등록되지 않은 GitHub Secret:$MISSING — Settings → Secrets and variables → Actions에서 등록하세요"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
echo "✅ 사전 점검 통과: 필수 Secret이 모두 있습니다"
|
|
74
|
+
|
|
56
75
|
- name: List repository contents
|
|
57
76
|
run: |
|
|
58
77
|
echo "Repository checked out"
|
|
59
78
|
ls -la
|
|
60
79
|
|
|
61
|
-
# Debug Keystore 설정 (
|
|
80
|
+
# Debug Keystore 설정 — 이 워크플로우는 DEBUG_KEYSTORE(비밀번호 android, alias androiddebugkey)로 서명한다
|
|
62
81
|
- name: Setup Debug Keystore
|
|
82
|
+
id: signing
|
|
83
|
+
env:
|
|
84
|
+
DEBUG_KEYSTORE: ${{ secrets.DEBUG_KEYSTORE }}
|
|
63
85
|
run: |
|
|
86
|
+
# 내부 배포용이라 Secret이 없어도 빌드는 진행한다 — 러너가 새로 만든 debug 키로 서명되므로
|
|
87
|
+
# 빌드마다 서명이 달라져 기존 설치본 위에 업데이트할 수 없다는 점을 경고로 남긴다.
|
|
88
|
+
if [ -z "$DEBUG_KEYSTORE" ]; then
|
|
89
|
+
echo "::warning::DEBUG_KEYSTORE Secret이 비어 있어 러너 기본 debug 키로 서명합니다 (빌드마다 서명이 달라집니다)"
|
|
90
|
+
echo "mode=default" >> "$GITHUB_OUTPUT"
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
# 값이 있는데 깨진 keystore는 조용히 넘기지 않고 여기서 실패한다
|
|
64
94
|
mkdir -p ~/.android
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
95
|
+
printf '%s' "$DEBUG_KEYSTORE" | base64 -d > ~/.android/debug.keystore
|
|
96
|
+
keytool -list -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
|
|
97
|
+
echo "mode=keystore" >> "$GITHUB_OUTPUT"
|
|
98
|
+
echo "Debug Keystore created (DEBUG_KEYSTORE로 서명)"
|
|
69
99
|
|
|
70
100
|
# 환경변수 준비 — ENV_MODE=dotenv면 .env 파일, dart-define이면 프로젝트 밖 임시 파일(--dart-define-from-file)
|
|
71
101
|
- name: Prepare env file
|
|
@@ -83,9 +113,11 @@ jobs:
|
|
|
83
113
|
fi
|
|
84
114
|
|
|
85
115
|
- name: Setup Keystore and key.properties
|
|
116
|
+
if: steps.signing.outputs.mode == 'keystore'
|
|
86
117
|
run: |
|
|
87
118
|
mkdir -p android/app/keystore
|
|
88
|
-
|
|
119
|
+
# 앞 단계에서 검증한 keystore를 그대로 쓴다
|
|
120
|
+
cp ~/.android/debug.keystore android/app/keystore/key.jks
|
|
89
121
|
echo "Keystore created from DEBUG_KEYSTORE"
|
|
90
122
|
ls -la android/app/keystore
|
|
91
123
|
echo "storeFile=app/keystore/key.jks" > android/key.properties
|
|
@@ -95,20 +127,25 @@ jobs:
|
|
|
95
127
|
echo "key.properties created"
|
|
96
128
|
ls -la android/
|
|
97
129
|
|
|
98
|
-
|
|
130
|
+
# google-services.json 생성 (선택) — 비어 있으면 빈 파일을 만들지 않는다
|
|
131
|
+
- name: Create google-services.json
|
|
132
|
+
env:
|
|
133
|
+
GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }}
|
|
99
134
|
run: |
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
135
|
+
if [ -n "$GOOGLE_SERVICES_JSON" ]; then
|
|
136
|
+
mkdir -p android/app
|
|
137
|
+
printf '%s' "$GOOGLE_SERVICES_JSON" > android/app/google-services.json
|
|
138
|
+
echo "✅ google-services.json created"
|
|
139
|
+
else
|
|
140
|
+
echo "ℹ️ GOOGLE_SERVICES_JSON secret not provided, skipping"
|
|
141
|
+
fi
|
|
105
142
|
|
|
106
143
|
- name: Set up Flutter
|
|
107
144
|
uses: subosito/flutter-action@v2
|
|
108
145
|
with:
|
|
146
|
+
channel: stable
|
|
109
147
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
110
148
|
cache: true
|
|
111
|
-
cache-key: flutter-${{ runner.os }}-${{ env.FLUTTER_VERSION }}
|
|
112
149
|
|
|
113
150
|
- name: Verify Flutter version
|
|
114
151
|
run: |
|
|
@@ -144,8 +181,13 @@ jobs:
|
|
|
144
181
|
- name: Setup Gradle
|
|
145
182
|
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/android
|
|
146
183
|
run: |
|
|
147
|
-
|
|
148
|
-
|
|
184
|
+
# flutter create 기본 .gitignore는 gradlew를 커밋하지 않는다 — 없으면 flutter build가 만든다
|
|
185
|
+
if [ -f gradlew ]; then
|
|
186
|
+
chmod +x gradlew
|
|
187
|
+
echo "Gradle wrapper permissions set"
|
|
188
|
+
else
|
|
189
|
+
echo "ℹ️ gradlew가 저장소에 없습니다 — flutter build가 생성합니다"
|
|
190
|
+
fi
|
|
149
191
|
|
|
150
192
|
- name: Set up Java
|
|
151
193
|
uses: actions/setup-java@v5
|
|
@@ -20,20 +20,20 @@
|
|
|
20
20
|
# 3. "Run workflow" 버튼 클릭
|
|
21
21
|
# 4. 빌드 완료 후 아티팩트에서 APK 다운로드
|
|
22
22
|
#
|
|
23
|
-
#
|
|
23
|
+
# GitHub Secrets (모두 선택 — 하나도 없어도 테스트 APK는 빌드됩니다)
|
|
24
24
|
#
|
|
25
|
-
# Release Keystore (Play Store 배포와
|
|
26
|
-
# RELEASE_KEYSTORE_BASE64: 릴리즈 키스토어 파일 (base64)
|
|
27
|
-
# RELEASE_KEYSTORE_PASSWORD: 키스토어 비밀번호
|
|
28
|
-
# RELEASE_KEY_ALIAS: 키 별칭
|
|
29
|
-
# RELEASE_KEY_PASSWORD: 키 비밀번호
|
|
25
|
+
# Release Keystore (Play Store 배포와 동일, 없으면 경고 후 debug 키로 서명):
|
|
26
|
+
# RELEASE_KEYSTORE_BASE64 (선택): 릴리즈 키스토어 파일 (base64)
|
|
27
|
+
# RELEASE_KEYSTORE_PASSWORD (선택): 키스토어 비밀번호
|
|
28
|
+
# RELEASE_KEY_ALIAS (선택): 키 별칭
|
|
29
|
+
# RELEASE_KEY_PASSWORD (선택): 키 비밀번호
|
|
30
30
|
#
|
|
31
31
|
# 환경 설정 (선택):
|
|
32
32
|
# ENV_FILE (선택): .env 파일 내용
|
|
33
33
|
# GOOGLE_SERVICES_JSON (선택): google-services.json 내용 (Firebase 사용 시)
|
|
34
34
|
#
|
|
35
35
|
# Firebase App Distribution (선택):
|
|
36
|
-
# FIREBASE_SERVICE_ACCOUNT_JSON_BASE64: Firebase 서비스 계정 JSON (base64)
|
|
36
|
+
# FIREBASE_SERVICE_ACCOUNT_JSON_BASE64 (선택): Firebase 서비스 계정 JSON (base64)
|
|
37
37
|
#
|
|
38
38
|
# ※ 위 Secrets는 저장소 Settings → Secrets and variables → Actions에 등록하세요.
|
|
39
39
|
# Firebase 업로드를 쓰려면 아래 env의 FIREBASE_APP_ID·FIREBASE_TESTER_GROUP도 수정합니다.
|
|
@@ -49,9 +49,17 @@ on:
|
|
|
49
49
|
repository_dispatch:
|
|
50
50
|
types: [build-android-app]
|
|
51
51
|
|
|
52
|
+
# 레포 기본 권한이 read여도 빌드 진행 댓글을 달 수 있게 필요한 권한을 직접 선언한다
|
|
53
|
+
permissions:
|
|
54
|
+
contents: read
|
|
55
|
+
issues: write
|
|
56
|
+
pull-requests: write
|
|
57
|
+
|
|
52
58
|
# 프로젝트별 설정 (아래 값들을 수정하세요)
|
|
53
59
|
env:
|
|
54
|
-
|
|
60
|
+
# Flutter SDK 버전 — 비워 두면 stable 채널 최신을 쓴다(최신 flutter create 프로젝트의 SDK 제약과 맞음).
|
|
61
|
+
# 특정 버전에 고정하려면 "3.38.9"처럼 적는다. pubspec.yaml의 environment.sdk 범위를 만족해야 한다.
|
|
62
|
+
FLUTTER_VERSION: ""
|
|
55
63
|
JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:17
|
|
56
64
|
# Flutter 루트 경로(레포 루트 기준). 단일레포면 ".", 모노레포면 "app" 등 — 설치 시 자동 설정
|
|
57
65
|
FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
|
|
@@ -321,15 +329,39 @@ jobs:
|
|
|
321
329
|
fi
|
|
322
330
|
|
|
323
331
|
- name: Setup Release Keystore
|
|
332
|
+
id: signing
|
|
324
333
|
env:
|
|
325
|
-
|
|
334
|
+
RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }}
|
|
335
|
+
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
|
|
336
|
+
RELEASE_KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
|
|
337
|
+
RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
|
|
326
338
|
run: |
|
|
339
|
+
# 테스트 빌드는 설정 없이도 돌아야 한다 — Secret이 비면 keystore·key.properties를 만들지 않고
|
|
340
|
+
# 프로젝트 기본 서명(flutter create 기본값은 release도 debug 키)으로 빌드한다. 어떤 키인지는 로그에 남긴다.
|
|
341
|
+
MISSING=""
|
|
342
|
+
for name in RELEASE_KEYSTORE_BASE64 RELEASE_KEYSTORE_PASSWORD RELEASE_KEY_ALIAS RELEASE_KEY_PASSWORD; do
|
|
343
|
+
[ -n "${!name}" ] || MISSING="$MISSING $name"
|
|
344
|
+
done
|
|
345
|
+
if [ -n "$MISSING" ]; then
|
|
346
|
+
echo "::warning::서명 Secret이 비어 있어 debug 키로 서명합니다:$MISSING (스토어 업로드용 키가 아닙니다)"
|
|
347
|
+
echo "mode=debug" >> "$GITHUB_OUTPUT"
|
|
348
|
+
exit 0
|
|
349
|
+
fi
|
|
350
|
+
# 값이 있는데 깨진 keystore는 조용히 넘기지 않고 여기서 실패한다
|
|
327
351
|
mkdir -p android/keystore
|
|
328
|
-
|
|
329
|
-
|
|
352
|
+
printf '%s' "$RELEASE_KEYSTORE_BASE64" | base64 -d > android/keystore/key.jks
|
|
353
|
+
if [ ! -s android/keystore/key.jks ]; then
|
|
354
|
+
echo "::error::RELEASE_KEYSTORE_BASE64를 디코딩한 keystore가 비어 있습니다"
|
|
355
|
+
exit 1
|
|
356
|
+
fi
|
|
357
|
+
# 비밀번호·alias가 keystore와 맞지 않으면 여기서 멈춘다 (gradle 서명 단계의 긴 오류 대신)
|
|
358
|
+
keytool -list -keystore android/keystore/key.jks -storepass "$RELEASE_KEYSTORE_PASSWORD" -alias "$RELEASE_KEY_ALIAS" > /dev/null
|
|
359
|
+
echo "mode=release" >> "$GITHUB_OUTPUT"
|
|
360
|
+
echo "✅ Release Keystore 생성 완료 (release 키로 서명)"
|
|
330
361
|
ls -la android/keystore/
|
|
331
362
|
|
|
332
363
|
- name: Create key.properties
|
|
364
|
+
if: steps.signing.outputs.mode == 'release'
|
|
333
365
|
env:
|
|
334
366
|
STORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
|
|
335
367
|
KEY_ALIAS: ${{ secrets.RELEASE_KEY_ALIAS }}
|
|
@@ -360,6 +392,7 @@ jobs:
|
|
|
360
392
|
- name: Set up Flutter
|
|
361
393
|
uses: subosito/flutter-action@v2
|
|
362
394
|
with:
|
|
395
|
+
channel: stable
|
|
363
396
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
364
397
|
cache: true
|
|
365
398
|
|
|
@@ -396,8 +429,13 @@ jobs:
|
|
|
396
429
|
- name: Setup Gradle
|
|
397
430
|
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/android
|
|
398
431
|
run: |
|
|
399
|
-
|
|
400
|
-
|
|
432
|
+
# flutter create 기본 .gitignore는 gradlew를 커밋하지 않는다 — 없으면 flutter build가 만든다
|
|
433
|
+
if [ -f gradlew ]; then
|
|
434
|
+
chmod +x gradlew
|
|
435
|
+
echo "✅ Gradle wrapper permissions set"
|
|
436
|
+
else
|
|
437
|
+
echo "ℹ️ gradlew가 저장소에 없습니다 — flutter build가 생성합니다"
|
|
438
|
+
fi
|
|
401
439
|
|
|
402
440
|
- name: Set up Java
|
|
403
441
|
uses: actions/setup-java@v5
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
# Flutter CI (코드 분석 및 빌드 검증) 워크플로우
|
|
3
3
|
#
|
|
4
4
|
# 이 워크플로우는 PR 생성 또는 {{MAIN_BRANCH}} 브랜치 푸시 시
|
|
5
|
-
# 코드 정적 분석(flutter analyze)
|
|
5
|
+
# 코드 정적 분석(flutter analyze), 테스트(flutter test)와 Android/iOS 앱 빌드를 검증합니다.
|
|
6
6
|
#
|
|
7
7
|
# 주요 특징:
|
|
8
8
|
# - PR 생성/업데이트 시 자동 실행
|
|
9
9
|
# - {{MAIN_BRANCH}} 브랜치 푸시 시 자동 실행
|
|
10
10
|
# - flutter analyze로 코드 품질 검사
|
|
11
|
+
# - test/ 폴더에 테스트(*_test.dart)가 있으면 flutter test 실행 (없으면 건너뜀)
|
|
11
12
|
# - Android/iOS 빌드 개별 활성화/비활성화 가능
|
|
12
13
|
# - Analyze Only 모드: 빌드 없이 코드 분석만 실행 가능
|
|
13
14
|
# - PR인 경우 진행 상황을 댓글로 실시간 업데이트
|
|
@@ -22,10 +23,10 @@
|
|
|
22
23
|
#
|
|
23
24
|
# 프로젝트별 설정
|
|
24
25
|
#
|
|
25
|
-
# ANALYZE_ONLY : true면 analyze만 실행, 빌드 스킵 (기본: false)
|
|
26
|
+
# ANALYZE_ONLY : true면 analyze·test만 실행, 빌드 스킵 (기본: false)
|
|
26
27
|
# ENABLE_ANDROID : Android 빌드 활성화 여부 (ANALYZE_ONLY=false일 때)
|
|
27
28
|
# ENABLE_IOS : iOS 빌드 활성화 여부 (ANALYZE_ONLY=false일 때)
|
|
28
|
-
# FLUTTER_VERSION : Flutter SDK 버전
|
|
29
|
+
# FLUTTER_VERSION : Flutter SDK 버전 (빈 값 = stable 최신)
|
|
29
30
|
# JAVA_VERSION : Java 버전 (Android 빌드용)
|
|
30
31
|
# XCODE_VERSION : Xcode 버전 (iOS 빌드용)
|
|
31
32
|
# ENV_FILE_PATH : dotenv 모드에서 .env를 만들 경로 (Flutter 루트 기준, 기본값: .env)
|
|
@@ -77,14 +78,16 @@ env:
|
|
|
77
78
|
ENV_MODE: "dart-define" # @wizard auto:flutter-env-mode
|
|
78
79
|
|
|
79
80
|
# CI 모드 설정
|
|
80
|
-
ANALYZE_ONLY: "false" # true: analyze만 실행 (빌드 스킵), false: analyze + 빌드
|
|
81
|
+
ANALYZE_ONLY: "false" # true: analyze·test만 실행 (빌드 스킵), false: analyze·test + 빌드
|
|
81
82
|
|
|
82
83
|
# 빌드 대상 플랫폼 설정 (ANALYZE_ONLY=false일 때만 적용)
|
|
83
84
|
ENABLE_ANDROID: "true" # Android 빌드 활성화 (true/false)
|
|
84
85
|
ENABLE_IOS: "true" # iOS 빌드 활성화 (true/false)
|
|
85
86
|
|
|
86
87
|
# 프로젝트별 설정
|
|
87
|
-
|
|
88
|
+
# Flutter SDK 버전 — 비워 두면 stable 채널 최신을 쓴다(최신 flutter create 프로젝트의 SDK 제약과 맞음).
|
|
89
|
+
# 특정 버전에 고정하려면 "3.38.9"처럼 적는다. pubspec.yaml의 environment.sdk 범위를 만족해야 한다.
|
|
90
|
+
FLUTTER_VERSION: ""
|
|
88
91
|
JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:17
|
|
89
92
|
XCODE_VERSION: "26.0"
|
|
90
93
|
ENV_FILE_PATH: ".env" # dotenv 모드에서 .env를 만들 경로 (Flutter 루트 기준, 커스터마이징 가능)
|
|
@@ -112,6 +115,10 @@ jobs:
|
|
|
112
115
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
|
113
116
|
uses: dorny/paths-filter@v4
|
|
114
117
|
with:
|
|
118
|
+
# push는 이번 push로 들어온 커밋만 비교한다(직전 커밋 github.event.before 기준).
|
|
119
|
+
# 비워 두면 기본 브랜치와의 누적 diff로 판정해 skip이 사실상 동작하지 않는다.
|
|
120
|
+
# 새 브랜치 첫 push(before가 0으로만 채워짐)나 PR은 빈 값 → 기본 동작(기본 브랜치·PR base 비교).
|
|
121
|
+
base: ${{ github.event_name == 'push' && github.event.before != '0000000000000000000000000000000000000000' && github.event.before || '' }}
|
|
115
122
|
filters: |
|
|
116
123
|
project:
|
|
117
124
|
- '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
|
|
@@ -212,6 +219,7 @@ jobs:
|
|
|
212
219
|
'| 검사 항목 | 상태 | 소요 시간 |',
|
|
213
220
|
'|----------|------|----------|',
|
|
214
221
|
'| 🔍 Analyze | ⏳ 진행 중... | - |',
|
|
222
|
+
'| 🧪 Test | ⏳ 진행 중... | - |',
|
|
215
223
|
`| 🤖 Android 빌드 | ${androidStatus} | - |`,
|
|
216
224
|
`| 🍎 iOS 빌드 | ${iosStatus} | - |`,
|
|
217
225
|
'',
|
|
@@ -257,6 +265,7 @@ jobs:
|
|
|
257
265
|
|
|
258
266
|
outputs:
|
|
259
267
|
status: ${{ steps.result.outputs.status }}
|
|
268
|
+
test_status: ${{ steps.result.outputs.test_status }}
|
|
260
269
|
duration: ${{ steps.result.outputs.duration }}
|
|
261
270
|
|
|
262
271
|
steps:
|
|
@@ -288,6 +297,7 @@ jobs:
|
|
|
288
297
|
- name: Set up Flutter
|
|
289
298
|
uses: subosito/flutter-action@v2
|
|
290
299
|
with:
|
|
300
|
+
channel: stable
|
|
291
301
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
292
302
|
cache: true
|
|
293
303
|
|
|
@@ -304,6 +314,7 @@ jobs:
|
|
|
304
314
|
restore-keys: ${{ runner.os }}-flutter-pub-
|
|
305
315
|
|
|
306
316
|
- name: Install dependencies
|
|
317
|
+
id: deps
|
|
307
318
|
run: |
|
|
308
319
|
flutter pub get
|
|
309
320
|
if grep -q "build_runner" pubspec.yaml; then
|
|
@@ -318,6 +329,22 @@ jobs:
|
|
|
318
329
|
flutter analyze --no-fatal-infos
|
|
319
330
|
echo "✅ 코드 분석 완료"
|
|
320
331
|
|
|
332
|
+
# analyze가 실패해도 테스트 결과는 따로 보여 준다. 실패하면 job이 실패해 CI Gate도 실패한다.
|
|
333
|
+
- name: Run Flutter Test
|
|
334
|
+
id: test
|
|
335
|
+
if: ${{ !cancelled() && steps.deps.outcome == 'success' }}
|
|
336
|
+
run: |
|
|
337
|
+
# test/에 *_test.dart가 없으면 flutter test 자체가 실패하므로 건너뛴다
|
|
338
|
+
if [ -d test ] && [ -n "$(find test -name '*_test.dart' -print -quit)" ]; then
|
|
339
|
+
echo "🧪 Flutter 테스트 시작..."
|
|
340
|
+
flutter test ${DART_DEFINE_FILE:+--dart-define-from-file="$DART_DEFINE_FILE"}
|
|
341
|
+
echo "ran=true" >> "$GITHUB_OUTPUT"
|
|
342
|
+
echo "✅ 테스트 통과"
|
|
343
|
+
else
|
|
344
|
+
echo "::notice title=Flutter 테스트 건너뜀::test/ 폴더에 *_test.dart 파일이 없어 flutter test를 실행하지 않았습니다"
|
|
345
|
+
echo "ran=false" >> "$GITHUB_OUTPUT"
|
|
346
|
+
fi
|
|
347
|
+
|
|
321
348
|
- name: 분석 결과 기록
|
|
322
349
|
id: result
|
|
323
350
|
if: always()
|
|
@@ -332,10 +359,16 @@ jobs:
|
|
|
332
359
|
const duration = minutes > 0 ? `${minutes}분 ${seconds}초` : `${seconds}초`;
|
|
333
360
|
|
|
334
361
|
const status = '${{ steps.analyze.outcome }}' === 'success' ? 'success' : 'failure';
|
|
362
|
+
// none: 테스트 파일 없음, skipped: 의존성 설치 실패로 실행 못 함
|
|
363
|
+
const testOutcome = '${{ steps.test.outcome }}';
|
|
364
|
+
const testStatus = testOutcome === 'success'
|
|
365
|
+
? ('${{ steps.test.outputs.ran }}' === 'true' ? 'success' : 'none')
|
|
366
|
+
: (testOutcome === 'failure' ? 'failure' : 'skipped');
|
|
335
367
|
|
|
336
368
|
core.setOutput('status', status);
|
|
369
|
+
core.setOutput('test_status', testStatus);
|
|
337
370
|
core.setOutput('duration', duration);
|
|
338
|
-
console.log(`📋 코드 분석 결과: ${status} (${duration})`);
|
|
371
|
+
console.log(`📋 코드 분석 결과: ${status}, 테스트: ${testStatus} (${duration})`);
|
|
339
372
|
|
|
340
373
|
# Job 3: Android 빌드 (조건부, 병렬)
|
|
341
374
|
build-android:
|
|
@@ -383,6 +416,7 @@ jobs:
|
|
|
383
416
|
- name: Set up Flutter
|
|
384
417
|
uses: subosito/flutter-action@v2
|
|
385
418
|
with:
|
|
419
|
+
channel: stable
|
|
386
420
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
387
421
|
cache: true
|
|
388
422
|
|
|
@@ -430,8 +464,13 @@ jobs:
|
|
|
430
464
|
- name: Setup Gradle
|
|
431
465
|
working-directory: ${{ env.FLUTTER_PROJECT_DIR }}/android
|
|
432
466
|
run: |
|
|
433
|
-
|
|
434
|
-
|
|
467
|
+
# flutter create 기본 .gitignore는 gradlew를 커밋하지 않는다 — 없으면 flutter build가 만든다
|
|
468
|
+
if [ -f gradlew ]; then
|
|
469
|
+
chmod +x gradlew
|
|
470
|
+
echo "✅ Gradle wrapper permissions set"
|
|
471
|
+
else
|
|
472
|
+
echo "ℹ️ gradlew가 저장소에 없습니다 — flutter build가 생성합니다"
|
|
473
|
+
fi
|
|
435
474
|
|
|
436
475
|
# APK 빌드 (CI는 빌드 검증 목적이므로 서명 불필요한 debug 빌드 사용)
|
|
437
476
|
- name: Build APK
|
|
@@ -527,6 +566,7 @@ jobs:
|
|
|
527
566
|
- name: Set up Flutter
|
|
528
567
|
uses: subosito/flutter-action@v2
|
|
529
568
|
with:
|
|
569
|
+
channel: stable
|
|
530
570
|
flutter-version: ${{ env.FLUTTER_VERSION }}
|
|
531
571
|
cache: true
|
|
532
572
|
|
|
@@ -558,9 +598,14 @@ jobs:
|
|
|
558
598
|
|
|
559
599
|
- name: Install CocoaPods
|
|
560
600
|
run: |
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
601
|
+
# 플러그인이 없는 기본 프로젝트는 Podfile이 없다 — 플러그인이 생기면 flutter build가 Podfile을 만든다
|
|
602
|
+
if [ -f ios/Podfile ]; then
|
|
603
|
+
gem install cocoapods
|
|
604
|
+
(cd ios && (pod install --repo-update || pod install))
|
|
605
|
+
echo "✅ CocoaPods installed"
|
|
606
|
+
else
|
|
607
|
+
echo "ℹ️ ios/Podfile이 없어 pod install을 건너뜁니다 (CocoaPods 플러그인 없음)"
|
|
608
|
+
fi
|
|
564
609
|
|
|
565
610
|
- name: Build iOS
|
|
566
611
|
id: build
|
|
@@ -614,6 +659,7 @@ jobs:
|
|
|
614
659
|
// 결과 수집
|
|
615
660
|
const analyzeStatus = '${{ needs.analyze.outputs.status }}';
|
|
616
661
|
const analyzeDuration = '${{ needs.analyze.outputs.duration }}' || '-';
|
|
662
|
+
const testStatus = '${{ needs.analyze.outputs.test_status }}';
|
|
617
663
|
const androidStatus = '${{ needs.build-android.outputs.status }}';
|
|
618
664
|
const androidDuration = '${{ needs.build-android.outputs.duration }}' || '-';
|
|
619
665
|
const iosStatus = '${{ needs.build-ios.outputs.status }}';
|
|
@@ -638,6 +684,17 @@ jobs:
|
|
|
638
684
|
overallSuccess = false;
|
|
639
685
|
}
|
|
640
686
|
|
|
687
|
+
// Test 상태 (테스트 파일이 없으면 실패로 보지 않는다)
|
|
688
|
+
let testDisplay;
|
|
689
|
+
if (testStatus === 'success') {
|
|
690
|
+
testDisplay = '✅ 성공';
|
|
691
|
+
} else if (testStatus === 'none') {
|
|
692
|
+
testDisplay = '⏸️ 테스트 없음';
|
|
693
|
+
} else {
|
|
694
|
+
testDisplay = testStatus === 'failure' ? '❌ 실패' : '⏸️ 실행 안 됨';
|
|
695
|
+
overallSuccess = false;
|
|
696
|
+
}
|
|
697
|
+
|
|
641
698
|
// Android 상태
|
|
642
699
|
if (analyzeOnly) {
|
|
643
700
|
androidDisplay = '⏸️ 스킵';
|
|
@@ -681,6 +738,7 @@ jobs:
|
|
|
681
738
|
'| 검사 항목 | 상태 | 소요 시간 |',
|
|
682
739
|
'|----------|------|----------|',
|
|
683
740
|
`| 🔍 Analyze | ${analyzeDisplay} | ${analyzeDuration} |`,
|
|
741
|
+
`| 🧪 Test | ${testDisplay} | - |`,
|
|
684
742
|
`| 🤖 Android 빌드 | ${androidDisplay} | ${analyzeOnly || !enableAndroid ? '-' : androidDuration} |`,
|
|
685
743
|
`| 🍎 iOS 빌드 | ${iosDisplay} | ${analyzeOnly || !enableIos ? '-' : iosDuration} |`,
|
|
686
744
|
'',
|
|
@@ -712,6 +770,12 @@ jobs:
|
|
|
712
770
|
bodyParts.push('');
|
|
713
771
|
}
|
|
714
772
|
|
|
773
|
+
if (testStatus === 'failure') {
|
|
774
|
+
bodyParts.push('**Test 실패 시:**');
|
|
775
|
+
bodyParts.push('- `flutter test` 로컬에서 실행하여 실패한 테스트 확인');
|
|
776
|
+
bodyParts.push('');
|
|
777
|
+
}
|
|
778
|
+
|
|
715
779
|
if (!analyzeOnly && enableAndroid && androidStatus !== 'success') {
|
|
716
780
|
bodyParts.push('**Android 빌드 실패 시:**');
|
|
717
781
|
bodyParts.push('- Gradle 빌드 로그에서 에러 확인');
|