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
|
@@ -32,6 +32,10 @@ on:
|
|
|
32
32
|
env:
|
|
33
33
|
# 프로젝트 고유 식별자
|
|
34
34
|
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
35
|
+
# 모노레포(--paths)에서 이 프로젝트가 있는 하위 경로. 빌드·Docker context의 기준 ('.'이면 레포 루트)
|
|
36
|
+
PROJECT_PATH: "." # @wizard auto:project-path
|
|
37
|
+
# 모노레포(PROJECT_PATH가 '.'이 아님)에서는 이미지·컨테이너·도메인 이름에 '-go'를 붙여
|
|
38
|
+
# 같은 레포의 다른 타입 서비스와 이름이 겹치지 않게 한다 (예: my-app-go)
|
|
35
39
|
|
|
36
40
|
DEPLOY_PORT: "__DEPLOY_PORT__" # @wizard ask:8080
|
|
37
41
|
|
|
@@ -71,6 +75,42 @@ jobs:
|
|
|
71
75
|
- name: 코드 체크아웃
|
|
72
76
|
uses: actions/checkout@v7
|
|
73
77
|
|
|
78
|
+
# 필수 Secret·Dockerfile이 없으면 docker 로그인 같은 서드파티 오류 대신 빠진 항목을 알려 주고 멈춘다
|
|
79
|
+
- name: 배포 사전 점검 (Secret·Dockerfile)
|
|
80
|
+
working-directory: ${{ env.PROJECT_PATH }}
|
|
81
|
+
env:
|
|
82
|
+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
83
|
+
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
84
|
+
SERVER_HOST: ${{ secrets.SERVER_HOST }}
|
|
85
|
+
SERVER_USER: ${{ secrets.SERVER_USER }}
|
|
86
|
+
SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
|
|
87
|
+
SSH_KEY: ${{ secrets.SSH_KEY }}
|
|
88
|
+
DOCKERFILE: Dockerfile
|
|
89
|
+
run: |
|
|
90
|
+
MISSING=""
|
|
91
|
+
for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
|
|
92
|
+
[ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
|
|
93
|
+
done
|
|
94
|
+
# SSH_AUTH_METHOD=key면 SSH_KEY, 아니면 SERVER_PASSWORD가 필요하다
|
|
95
|
+
if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
|
|
96
|
+
[ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
|
|
97
|
+
else
|
|
98
|
+
[ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
|
|
99
|
+
fi
|
|
100
|
+
FAILED=false
|
|
101
|
+
if [ -n "$MISSING" ]; then
|
|
102
|
+
echo "::error title=필수 Secret 누락::등록되지 않은 GitHub Secret:$MISSING — Settings → Secrets and variables → Actions에서 등록하세요"
|
|
103
|
+
FAILED=true
|
|
104
|
+
fi
|
|
105
|
+
if [ ! -f "$DOCKERFILE" ]; then
|
|
106
|
+
echo "::error title=Dockerfile 없음::${PROJECT_PATH}/${DOCKERFILE#./} 파일이 없습니다. 이 워크플로우는 Docker 이미지로 배포하므로 Dockerfile이 필요합니다 (서버 배포를 하지 않는다면 이 워크플로우 파일을 삭제하세요)"
|
|
107
|
+
FAILED=true
|
|
108
|
+
fi
|
|
109
|
+
if [ "$FAILED" = "true" ]; then
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
echo "✅ 사전 점검 통과: 필수 Secret과 Dockerfile이 모두 있습니다"
|
|
113
|
+
|
|
74
114
|
- name: 디스크 공간 확보
|
|
75
115
|
run: |
|
|
76
116
|
echo "📊 빌드 전 디스크 사용량:"
|
|
@@ -89,6 +129,7 @@ jobs:
|
|
|
89
129
|
df -h
|
|
90
130
|
|
|
91
131
|
- name: .env 파일 생성
|
|
132
|
+
working-directory: ${{ env.PROJECT_PATH }}
|
|
92
133
|
run: |
|
|
93
134
|
cat > .env << 'EOF'
|
|
94
135
|
${{ secrets.ENV_FILE }}
|
|
@@ -107,10 +148,10 @@ jobs:
|
|
|
107
148
|
- name: Docker 이미지 빌드 및 푸시
|
|
108
149
|
uses: docker/build-push-action@v5
|
|
109
150
|
with:
|
|
110
|
-
context: .
|
|
111
|
-
file:
|
|
151
|
+
context: ${{ env.PROJECT_PATH }}
|
|
152
|
+
file: ${{ env.PROJECT_PATH }}/Dockerfile
|
|
112
153
|
push: true
|
|
113
|
-
tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.ref_name }}
|
|
154
|
+
tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}:${{ github.ref_name }}
|
|
114
155
|
# GitHub Actions 캐시 사용 (로컬 디스크 공간 절약)
|
|
115
156
|
cache-from: type=gha
|
|
116
157
|
cache-to: type=gha,mode=max
|
|
@@ -164,7 +205,7 @@ jobs:
|
|
|
164
205
|
|
|
165
206
|
BRANCH=${{ github.ref_name }}
|
|
166
207
|
|
|
167
|
-
PROJECT_NAME="${{ env.PROJECT_NAME }}"
|
|
208
|
+
PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}"
|
|
168
209
|
|
|
169
210
|
# 포트 및 컨테이너명 설정
|
|
170
211
|
PORT=${{ env.DEPLOY_PORT }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# project-auto-wizard:managed-workflow
|
|
2
2
|
# Next.js CI (빌드 검증)
|
|
3
|
-
# {{
|
|
3
|
+
# {{DEVELOP_BRANCH}} 브랜치 push·PR 시 npm ci, npm test(test 스크립트가 있을 때), npm run build로 검증합니다. node_modules와 .next/cache를 캐싱합니다.
|
|
4
4
|
#
|
|
5
5
|
# GitHub Secrets
|
|
6
6
|
# ENV_FILE (선택): .env 파일 내용. CI는 빌드 검증 목적이므로 배포 관련 Secrets는 불필요
|
|
@@ -15,6 +15,8 @@ name: PROJECT-NEXT-CI
|
|
|
15
15
|
on:
|
|
16
16
|
push:
|
|
17
17
|
branches: ["{{DEVELOP_BRANCH}}"]
|
|
18
|
+
pull_request:
|
|
19
|
+
branches: ["{{DEVELOP_BRANCH}}"]
|
|
18
20
|
workflow_dispatch:
|
|
19
21
|
|
|
20
22
|
env:
|
|
@@ -48,6 +50,9 @@ jobs:
|
|
|
48
50
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
|
49
51
|
uses: dorny/paths-filter@v4
|
|
50
52
|
with:
|
|
53
|
+
# push는 이번 push 범위(직전 커밋 before 대비)만 본다. 비워 두면 기본 브랜치 대비 누적 diff가 된다.
|
|
54
|
+
# 새 브랜치 첫 push(before가 0)와 PR은 빈 값 → 기본 동작(기본 브랜치·PR base 대비)
|
|
55
|
+
base: ${{ github.event_name == 'push' && github.event.before != '0000000000000000000000000000000000000000' && github.event.before || '' }}
|
|
51
56
|
filters: |
|
|
52
57
|
project:
|
|
53
58
|
- '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
|
|
@@ -57,6 +62,10 @@ jobs:
|
|
|
57
62
|
runs-on: ubuntu-latest
|
|
58
63
|
needs: changes
|
|
59
64
|
if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true' }}
|
|
65
|
+
# 모노레포(--paths)면 그 하위 폴더에서 빌드한다
|
|
66
|
+
defaults:
|
|
67
|
+
run:
|
|
68
|
+
working-directory: ${{ env.PROJECT_PATH }}
|
|
60
69
|
|
|
61
70
|
steps:
|
|
62
71
|
- name: 코드 체크아웃
|
|
@@ -72,6 +81,7 @@ jobs:
|
|
|
72
81
|
with:
|
|
73
82
|
node-version: ${{ env.NODE_VERSION }}
|
|
74
83
|
cache: "npm"
|
|
84
|
+
cache-dependency-path: ${{ env.PROJECT_PATH }}/package-lock.json
|
|
75
85
|
|
|
76
86
|
- name: npm 의존성 캐싱
|
|
77
87
|
uses: actions/cache@v6
|
|
@@ -102,12 +112,24 @@ jobs:
|
|
|
102
112
|
- name: Next.js 빌드 캐싱
|
|
103
113
|
uses: actions/cache@v6
|
|
104
114
|
with:
|
|
105
|
-
path: .next/cache
|
|
115
|
+
path: ${{ env.PROJECT_PATH }}/.next/cache
|
|
106
116
|
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
|
|
107
117
|
restore-keys: |
|
|
108
118
|
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
|
|
109
119
|
${{ runner.os }}-nextjs-
|
|
110
120
|
|
|
121
|
+
# package.json에 test 스크립트가 있을 때만 실행한다 (npm init 기본값 "no test specified"는 없는 것으로 본다)
|
|
122
|
+
- name: 테스트 실행
|
|
123
|
+
run: |
|
|
124
|
+
TEST_SCRIPT=$(node -p "(require('./package.json').scripts || {}).test || ''")
|
|
125
|
+
if [ -z "$TEST_SCRIPT" ] || echo "$TEST_SCRIPT" | grep -q "no test specified"; then
|
|
126
|
+
echo "::notice::package.json에 test 스크립트가 없어 테스트를 건너뜁니다"
|
|
127
|
+
exit 0
|
|
128
|
+
fi
|
|
129
|
+
echo "🧪 npm test 실행: $TEST_SCRIPT"
|
|
130
|
+
# CI=true: watch 모드로 대기하는 테스트 러너(react-scripts 등)를 한 번만 실행
|
|
131
|
+
CI=true npm test
|
|
132
|
+
|
|
111
133
|
- name: 프로젝트 빌드
|
|
112
134
|
id: build
|
|
113
135
|
run: |
|
|
@@ -160,7 +182,7 @@ jobs:
|
|
|
160
182
|
uses: actions/upload-artifact@v7
|
|
161
183
|
with:
|
|
162
184
|
name: build-log
|
|
163
|
-
path: build_log.txt
|
|
185
|
+
path: ${{ env.PROJECT_PATH }}/build_log.txt
|
|
164
186
|
retention-days: 7
|
|
165
187
|
|
|
166
188
|
- name: 빌드 결과 요약
|
|
@@ -8,8 +8,13 @@
|
|
|
8
8
|
# DOCKERHUB_TOKEN: DockerHub 액세스 토큰
|
|
9
9
|
# SERVER_HOST: 배포 서버 주소
|
|
10
10
|
# SERVER_USER: SSH 사용자명
|
|
11
|
-
# SERVER_PASSWORD: SSH 비밀번호
|
|
11
|
+
# SERVER_PASSWORD: SSH 비밀번호 (SSH_AUTH_METHOD=password일 때)
|
|
12
|
+
# SSH_KEY: SSH 개인키 .pem 내용 (SSH_AUTH_METHOD=key일 때, AWS EC2 등)
|
|
12
13
|
# PROJECT_DEPLOY_PORT (선택): 배포 포트 (기본: 3000)
|
|
14
|
+
#
|
|
15
|
+
# 환경변수 (env 섹션에서 설정)
|
|
16
|
+
# SSH_AUTH_METHOD: SSH 인증 방식 password | key (기본: password)
|
|
17
|
+
# SSH_PORT (기본: 2022), SSH_COMMAND_TIMEOUT (기본: 600s), SSH_CONNECTION_TIMEOUT (기본: 60s)
|
|
13
18
|
|
|
14
19
|
name: PROJECT-NEXT-CICD
|
|
15
20
|
|
|
@@ -22,24 +27,76 @@ on:
|
|
|
22
27
|
|
|
23
28
|
env:
|
|
24
29
|
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
30
|
+
# 모노레포(--paths)에서 이 프로젝트가 있는 하위 경로. 빌드·Docker context의 기준 ('.'이면 레포 루트)
|
|
31
|
+
PROJECT_PATH: "." # @wizard auto:project-path
|
|
25
32
|
DOCKER_IMAGE_PREFIX: "nextjs-container"
|
|
26
33
|
NODE_VERSION: "20.15.0"
|
|
27
34
|
ENV_FILE_NAME: ".env"
|
|
28
35
|
|
|
36
|
+
# SSH 인증 방식: "password"(Synology·일반 서버) | "key"(AWS EC2·.pem)
|
|
37
|
+
# password → SERVER_PASSWORD secret 사용
|
|
38
|
+
# key → SSH_KEY secret 사용 (.pem 파일 전체 내용을 secret 값으로)
|
|
39
|
+
SSH_AUTH_METHOD: "password" # @wizard ask:password
|
|
40
|
+
|
|
41
|
+
SSH_PORT: "__SSH_PORT__" # @wizard ask:2022
|
|
42
|
+
SSH_COMMAND_TIMEOUT: "600s" # SSH 명령 실행 타임아웃
|
|
43
|
+
SSH_CONNECTION_TIMEOUT: "60s" # SSH 연결 타임아웃
|
|
44
|
+
|
|
29
45
|
jobs:
|
|
30
46
|
build:
|
|
31
47
|
name: Next.js 애플리케이션 빌드
|
|
32
48
|
runs-on: ubuntu-latest
|
|
49
|
+
# 모노레포(--paths)면 그 하위 폴더에서 빌드한다
|
|
50
|
+
defaults:
|
|
51
|
+
run:
|
|
52
|
+
working-directory: ${{ env.PROJECT_PATH }}
|
|
33
53
|
|
|
34
54
|
steps:
|
|
35
55
|
- name: 코드 체크아웃
|
|
36
56
|
uses: actions/checkout@v7
|
|
37
57
|
|
|
58
|
+
# 필수 Secret·Dockerfile이 없으면 docker 로그인 같은 서드파티 오류 대신 빠진 항목을 알려 주고 멈춘다
|
|
59
|
+
- name: 배포 사전 점검 (Secret·Dockerfile)
|
|
60
|
+
working-directory: ${{ env.PROJECT_PATH }}
|
|
61
|
+
env:
|
|
62
|
+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
63
|
+
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
64
|
+
SERVER_HOST: ${{ secrets.SERVER_HOST }}
|
|
65
|
+
SERVER_USER: ${{ secrets.SERVER_USER }}
|
|
66
|
+
SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
|
|
67
|
+
SSH_KEY: ${{ secrets.SSH_KEY }}
|
|
68
|
+
DOCKERFILE: Dockerfile
|
|
69
|
+
run: |
|
|
70
|
+
MISSING=""
|
|
71
|
+
for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
|
|
72
|
+
[ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
|
|
73
|
+
done
|
|
74
|
+
# SSH_AUTH_METHOD=key면 SSH_KEY, 아니면 SERVER_PASSWORD가 필요하다
|
|
75
|
+
if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
|
|
76
|
+
[ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
|
|
77
|
+
else
|
|
78
|
+
[ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
|
|
79
|
+
fi
|
|
80
|
+
FAILED=false
|
|
81
|
+
if [ -n "$MISSING" ]; then
|
|
82
|
+
echo "::error title=필수 Secret 누락::등록되지 않은 GitHub Secret:$MISSING — Settings → Secrets and variables → Actions에서 등록하세요"
|
|
83
|
+
FAILED=true
|
|
84
|
+
fi
|
|
85
|
+
if [ ! -f "$DOCKERFILE" ]; then
|
|
86
|
+
echo "::error title=Dockerfile 없음::${PROJECT_PATH}/${DOCKERFILE#./} 파일이 없습니다. 이 워크플로우는 Docker 이미지로 배포하므로 Dockerfile이 필요합니다 (서버 배포를 하지 않는다면 이 워크플로우 파일을 삭제하세요)"
|
|
87
|
+
FAILED=true
|
|
88
|
+
fi
|
|
89
|
+
if [ "$FAILED" = "true" ]; then
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
echo "✅ 사전 점검 통과: 필수 Secret과 Dockerfile이 모두 있습니다"
|
|
93
|
+
|
|
38
94
|
- name: Node.js 설정
|
|
39
95
|
uses: actions/setup-node@v7
|
|
40
96
|
with:
|
|
41
97
|
node-version: ${{ env.NODE_VERSION }}
|
|
42
98
|
cache: 'npm'
|
|
99
|
+
cache-dependency-path: ${{ env.PROJECT_PATH }}/package-lock.json
|
|
43
100
|
|
|
44
101
|
- name: 환경변수 파일 생성 (${{ env.ENV_FILE_NAME }})
|
|
45
102
|
run: |
|
|
@@ -72,7 +129,7 @@ jobs:
|
|
|
72
129
|
- name: Next.js 빌드 캐싱
|
|
73
130
|
uses: actions/cache@v6
|
|
74
131
|
with:
|
|
75
|
-
path: .next/cache
|
|
132
|
+
path: ${{ env.PROJECT_PATH }}/.next/cache
|
|
76
133
|
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
|
|
77
134
|
restore-keys: |
|
|
78
135
|
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
|
|
@@ -101,8 +158,8 @@ jobs:
|
|
|
101
158
|
- name: Docker 이미지 빌드 및 푸시
|
|
102
159
|
uses: docker/build-push-action@v5
|
|
103
160
|
with:
|
|
104
|
-
context: .
|
|
105
|
-
file:
|
|
161
|
+
context: ${{ env.PROJECT_PATH }}
|
|
162
|
+
file: ${{ env.PROJECT_PATH }}/Dockerfile
|
|
106
163
|
push: true
|
|
107
164
|
tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_NAME }}-${{ env.DOCKER_IMAGE_PREFIX }}:${{ github.ref_name }}
|
|
108
165
|
cache-from: type=local,src=/tmp/.buildx-cache
|
|
@@ -121,11 +178,17 @@ jobs:
|
|
|
121
178
|
steps:
|
|
122
179
|
- name: Deploy
|
|
123
180
|
uses: appleboy/ssh-action@v1.0.3
|
|
181
|
+
env:
|
|
182
|
+
SSH_AUTH_METHOD: ${{ env.SSH_AUTH_METHOD }}
|
|
124
183
|
with:
|
|
125
184
|
host: ${{ secrets.SERVER_HOST }}
|
|
126
185
|
username: ${{ secrets.SERVER_USER }}
|
|
127
186
|
password: ${{ secrets.SERVER_PASSWORD }}
|
|
128
|
-
|
|
187
|
+
key: ${{ secrets.SSH_KEY }}
|
|
188
|
+
port: ${{ env.SSH_PORT }}
|
|
189
|
+
command_timeout: ${{ env.SSH_COMMAND_TIMEOUT }}
|
|
190
|
+
timeout: ${{ env.SSH_CONNECTION_TIMEOUT }}
|
|
191
|
+
envs: SSH_AUTH_METHOD
|
|
129
192
|
script: |
|
|
130
193
|
set -e
|
|
131
194
|
|
|
@@ -134,6 +197,17 @@ jobs:
|
|
|
134
197
|
export PATH=$PATH:/usr/local/bin
|
|
135
198
|
export PW=${{ secrets.SERVER_PASSWORD }}
|
|
136
199
|
|
|
200
|
+
# SSH 인증 방식에 따른 sudo 추상화
|
|
201
|
+
# password 모드: echo $PW | sudo -S (Synology 등 sudo 비밀번호 필요 서버)
|
|
202
|
+
# key 모드: sudo 직접 (AWS EC2 등 passwordless sudo 서버)
|
|
203
|
+
SSH_AUTH_METHOD="${SSH_AUTH_METHOD:-password}"
|
|
204
|
+
if [ "${SSH_AUTH_METHOD}" = "key" ]; then
|
|
205
|
+
SUDO() { sudo "$@"; }
|
|
206
|
+
else
|
|
207
|
+
SUDO() { echo "$PW" | sudo -S "$@"; }
|
|
208
|
+
fi
|
|
209
|
+
echo "🔐 SSH 인증 방식: ${SSH_AUTH_METHOD}"
|
|
210
|
+
|
|
137
211
|
BRANCH=${{ github.ref_name }}
|
|
138
212
|
PROJECT_NAME="${{ env.PROJECT_NAME }}"
|
|
139
213
|
|
|
@@ -159,14 +233,14 @@ jobs:
|
|
|
159
233
|
|
|
160
234
|
# Docker 이미지 풀 (Pull)
|
|
161
235
|
echo "⬇️ Docker 이미지 풀..."
|
|
162
|
-
|
|
236
|
+
SUDO docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}-${{ env.DOCKER_IMAGE_PREFIX }}:${BRANCH}
|
|
163
237
|
|
|
164
238
|
# 기존 컨테이너 정리
|
|
165
239
|
echo "🧹 컨테이너 $CONTAINER_NAME 존재 여부 확인 중..."
|
|
166
240
|
|
|
167
|
-
if
|
|
241
|
+
if SUDO docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
|
|
168
242
|
echo "⚠️ 컨테이너 $CONTAINER_NAME 이(가) 존재합니다. 중지 및 삭제 중..."
|
|
169
|
-
|
|
243
|
+
SUDO docker rm -f $CONTAINER_NAME
|
|
170
244
|
echo "✅ 컨테이너 $CONTAINER_NAME 이(가) 삭제되었습니다."
|
|
171
245
|
else
|
|
172
246
|
echo "ℹ️ 존재하는 컨테이너 $CONTAINER_NAME 이(가) 없습니다."
|
|
@@ -177,7 +251,7 @@ jobs:
|
|
|
177
251
|
|
|
178
252
|
# Next.js는 SSR을 위해 Node.js 서버가 필요합니다
|
|
179
253
|
# standalone 모드 사용 시 node server.js로 실행
|
|
180
|
-
|
|
254
|
+
SUDO docker run -d \
|
|
181
255
|
-p ${PORT}:3000 \
|
|
182
256
|
--name $CONTAINER_NAME \
|
|
183
257
|
-e TZ=Asia/Seoul \
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# project-auto-wizard:managed-workflow
|
|
2
|
-
# Python
|
|
3
|
-
# - {{
|
|
2
|
+
# Python 빌드 검증 워크플로우 (CI Only)
|
|
3
|
+
# - {{DEVELOP_BRANCH}} 브랜치 push/PR 시 의존성 설치 → pytest → (Dockerfile이 있으면) Docker 빌드로 검증
|
|
4
|
+
# - 의존성: requirements.txt가 있으면 그것을, 없으면 pyproject.toml/setup.py로 `pip install .`
|
|
5
|
+
# - 테스트: tests/·test/ 폴더나 test_*.py·*_test.py가 있을 때만 pytest 실행 (없으면 건너뜀)
|
|
6
|
+
# - Dockerfile이 없어도 실패하지 않는다 (Docker 빌드 검증만 건너뜀)
|
|
4
7
|
# - 배포는 수행하지 않음 ({{MAIN_BRANCH}} 브랜치에서만 배포)
|
|
8
|
+
#
|
|
9
|
+
# GitHub Secrets
|
|
10
|
+
# - ENV_FILE (선택): .env 파일 내용 — 없으면 빈 .env로 검증합니다
|
|
5
11
|
|
|
6
12
|
name: PROJECT-PYTHON-CI
|
|
7
13
|
|
|
@@ -19,6 +25,8 @@ env:
|
|
|
19
25
|
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
20
26
|
# 모노레포에서 이 프로젝트가 있는 하위 경로 ('.'이면 항상 검증)
|
|
21
27
|
PROJECT_PATH: "." # @wizard auto:project-path
|
|
28
|
+
# 테스트·의존성 설치에 쓸 Python 버전
|
|
29
|
+
PYTHON_VERSION: "3.12"
|
|
22
30
|
|
|
23
31
|
jobs:
|
|
24
32
|
changes:
|
|
@@ -38,15 +46,22 @@ jobs:
|
|
|
38
46
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
|
39
47
|
uses: dorny/paths-filter@v4
|
|
40
48
|
with:
|
|
49
|
+
# push는 이번 push 범위(직전 커밋 before 대비)만 본다. 비워 두면 기본 브랜치 대비 누적 diff가 된다.
|
|
50
|
+
# 새 브랜치 첫 push(before가 0)와 PR은 빈 값 → 기본 동작(기본 브랜치·PR base 대비)
|
|
51
|
+
base: ${{ github.event_name == 'push' && github.event.before != '0000000000000000000000000000000000000000' && github.event.before || '' }}
|
|
41
52
|
filters: |
|
|
42
53
|
project:
|
|
43
54
|
- '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
|
|
44
55
|
|
|
45
56
|
build-check:
|
|
46
|
-
name: Python
|
|
57
|
+
name: Python 빌드 검증
|
|
47
58
|
runs-on: ubuntu-latest
|
|
48
59
|
needs: changes
|
|
49
60
|
if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true' }}
|
|
61
|
+
# 모노레포(--paths)면 그 하위 폴더에서 빌드한다
|
|
62
|
+
defaults:
|
|
63
|
+
run:
|
|
64
|
+
working-directory: ${{ env.PROJECT_PATH }}
|
|
50
65
|
|
|
51
66
|
steps:
|
|
52
67
|
- name: 코드 체크아웃
|
|
@@ -59,14 +74,63 @@ jobs:
|
|
|
59
74
|
EOF
|
|
60
75
|
echo "✅ .env 파일이 생성되었습니다"
|
|
61
76
|
|
|
77
|
+
- name: Python 설정
|
|
78
|
+
uses: actions/setup-python@v7
|
|
79
|
+
with:
|
|
80
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
81
|
+
|
|
82
|
+
- name: 의존성 설치
|
|
83
|
+
run: |
|
|
84
|
+
python -m pip install --upgrade pip
|
|
85
|
+
if [ -f requirements.txt ]; then
|
|
86
|
+
pip install -r requirements.txt
|
|
87
|
+
elif [ -f pyproject.toml ] || [ -f setup.py ]; then
|
|
88
|
+
pip install .
|
|
89
|
+
else
|
|
90
|
+
echo "::notice::requirements.txt·pyproject.toml·setup.py가 없어 의존성 설치를 건너뜁니다"
|
|
91
|
+
fi
|
|
92
|
+
# 개발용 의존성은 있으면 함께 설치 (pytest 등)
|
|
93
|
+
if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
|
|
94
|
+
|
|
95
|
+
# 테스트가 없는 프로젝트는 실패가 아니라 건너뜀으로 처리한다
|
|
96
|
+
- name: 테스트 실행 (pytest)
|
|
97
|
+
run: |
|
|
98
|
+
if [ -d tests ] || [ -d test ] || [ -n "$(find . \( -path ./.venv -o -path ./venv -o -path ./node_modules -o -path ./.git \) -prune -o \( -name 'test_*.py' -o -name '*_test.py' \) -type f -print -quit)" ]; then
|
|
99
|
+
python -m pytest --version > /dev/null 2>&1 || pip install pytest
|
|
100
|
+
set +e
|
|
101
|
+
python -m pytest
|
|
102
|
+
code=$?
|
|
103
|
+
set -e
|
|
104
|
+
# 5 = 수집된 테스트 없음 → 실패로 보지 않는다
|
|
105
|
+
if [ "$code" -eq 5 ]; then
|
|
106
|
+
echo "::notice::수집된 테스트가 없어 pytest를 건너뜁니다"
|
|
107
|
+
exit 0
|
|
108
|
+
fi
|
|
109
|
+
exit "$code"
|
|
110
|
+
else
|
|
111
|
+
echo "::notice::테스트 파일(tests/, test_*.py 등)이 없어 pytest를 건너뜁니다"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
- name: Dockerfile 확인
|
|
115
|
+
id: dockerfile
|
|
116
|
+
run: |
|
|
117
|
+
if [ -f Dockerfile ]; then
|
|
118
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
119
|
+
else
|
|
120
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
121
|
+
echo "::notice::${{ env.PROJECT_PATH }}/Dockerfile이 없어 Docker 빌드 검증을 건너뜁니다"
|
|
122
|
+
fi
|
|
123
|
+
|
|
62
124
|
- name: Docker 빌드환경 설정
|
|
125
|
+
if: steps.dockerfile.outputs.exists == 'true'
|
|
63
126
|
uses: docker/setup-buildx-action@v3
|
|
64
127
|
|
|
65
128
|
- name: Docker 이미지 빌드 검증
|
|
129
|
+
if: steps.dockerfile.outputs.exists == 'true'
|
|
66
130
|
uses: docker/build-push-action@v5
|
|
67
131
|
with:
|
|
68
|
-
context: .
|
|
69
|
-
file:
|
|
132
|
+
context: ${{ env.PROJECT_PATH }}
|
|
133
|
+
file: ${{ env.PROJECT_PATH }}/Dockerfile
|
|
70
134
|
push: false # 푸시하지 않음 (빌드 검증만)
|
|
71
135
|
tags: ${{ env.PROJECT_NAME }}:build-check
|
|
72
136
|
cache-from: type=gha
|
|
@@ -74,7 +138,7 @@ jobs:
|
|
|
74
138
|
|
|
75
139
|
- name: 빌드 검증 완료
|
|
76
140
|
run: |
|
|
77
|
-
echo "✅
|
|
141
|
+
echo "✅ Python 빌드 검증이 성공적으로 완료되었습니다!"
|
|
78
142
|
echo ""
|
|
79
143
|
echo "📋 빌드 정보:"
|
|
80
144
|
echo " 🎯 프로젝트: ${{ env.PROJECT_NAME }}"
|