project-auto-wizard 0.1.5
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/LICENSE +21 -0
- package/README.md +139 -0
- package/bin/project-auto-wizard.js +19 -0
- package/package.json +36 -0
- package/payload/coderabbit.yaml +19 -0
- package/payload/config/breaking-changes.json +1 -0
- package/payload/config/wizard-prompts.yml +62 -0
- package/payload/scripts/changelog_manager.py +726 -0
- package/payload/scripts/version_manager.py +617 -0
- package/payload/version.yml.template +48 -0
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +303 -0
- package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +293 -0
- package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +289 -0
- package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +192 -0
- package/payload/workflows/common/secret-backup/PROJECT-COMMON-SECRET-FILE-UPLOAD.yaml +209 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +591 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +700 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +308 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +992 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +689 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +987 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +471 -0
- package/payload/workflows/flutter/PROJECT-FLUTTER-SUH-LAB-APP-BUILD-TRIGGER.yaml +500 -0
- package/payload/workflows/next/PROJECT-NEXT-CI.yaml +185 -0
- package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +271 -0
- package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +81 -0
- package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +2191 -0
- package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +386 -0
- package/payload/workflows/react/PROJECT-REACT-CI.yaml +194 -0
- package/payload/workflows/react/PROJECT-REACT-CICD.yaml +255 -0
- package/payload/workflows/spring/PROJECT-SPRING-GITHUB-PACKAGES-PUBLISH.yml +84 -0
- package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-CI.yml +316 -0
- package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-PUBLISH.yml +58 -0
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +535 -0
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +437 -0
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +2277 -0
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +425 -0
- package/src/cli/args.js +95 -0
- package/src/cli/help.js +27 -0
- package/src/commands/full.js +53 -0
- package/src/commands/interactive.js +276 -0
- package/src/commands/revert.js +60 -0
- package/src/commands/version.js +31 -0
- package/src/commands/workflows.js +49 -0
- package/src/context.js +26 -0
- package/src/core/assets.js +47 -0
- package/src/core/branches.js +60 -0
- package/src/core/branding.js +15 -0
- package/src/core/breaking-check.js +65 -0
- package/src/core/breaking.js +26 -0
- package/src/core/copy/coderabbit.js +26 -0
- package/src/core/copy/gitignore.js +55 -0
- package/src/core/copy/readme.js +30 -0
- package/src/core/copy/simple.js +23 -0
- package/src/core/copy/workflows.js +233 -0
- package/src/core/detect-fs.js +106 -0
- package/src/core/detect.js +62 -0
- package/src/core/fsutil.js +46 -0
- package/src/core/options-ask.js +99 -0
- package/src/core/paths-resolve.js +261 -0
- package/src/core/paths.js +16 -0
- package/src/core/version-yml.js +190 -0
- package/src/core/wizard-env.js +101 -0
- package/src/core/wizard-labels.js +107 -0
- package/src/index.js +162 -0
- package/src/ui/ansi.js +26 -0
- package/src/ui/banner.js +29 -0
- package/src/ui/env-plan.js +207 -0
- package/src/ui/prompts.js +99 -0
- package/src/ui/readline-engine.js +257 -0
- package/src/ui/status-cards.js +56 -0
- package/src/ui/summary.js +127 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# ===================================================================
|
|
2
|
+
# Python FastAPI CI/CD 배포 (SSH + Docker)
|
|
3
|
+
# ===================================================================
|
|
4
|
+
#
|
|
5
|
+
# {{MAIN_BRANCH}} 브랜치 푸시 시 Docker 이미지를 빌드하고
|
|
6
|
+
# SSH로 접속 가능한 서버(Synology·AWS EC2·VPS 등)에 자동 배포합니다.
|
|
7
|
+
#
|
|
8
|
+
# ===================================================================
|
|
9
|
+
# 🔑 필수 GitHub Secrets
|
|
10
|
+
# ===================================================================
|
|
11
|
+
# ENV_FILE: .env 파일 내용
|
|
12
|
+
# DOCKERHUB_USERNAME: DockerHub 사용자명
|
|
13
|
+
# DOCKERHUB_TOKEN: DockerHub 액세스 토큰
|
|
14
|
+
# SERVER_HOST: 배포 서버 주소
|
|
15
|
+
# SERVER_USER: SSH 사용자명
|
|
16
|
+
# SERVER_PASSWORD: SSH 비밀번호 (SSH_AUTH_METHOD=password일 때)
|
|
17
|
+
# SSH_KEY: SSH 개인키 .pem 내용 (SSH_AUTH_METHOD=key일 때, AWS EC2 등)
|
|
18
|
+
# ===================================================================
|
|
19
|
+
#
|
|
20
|
+
# 🔧 환경변수 설정 (env 섹션에서 설정)
|
|
21
|
+
# ===================================================================
|
|
22
|
+
#
|
|
23
|
+
# 🌐 포트 및 컨테이너:
|
|
24
|
+
# CONTAINER_INTERNAL_PORT: FastAPI 내부 포트 (기본: 8000)
|
|
25
|
+
# DEPLOY_PORT: 배포 포트
|
|
26
|
+
# CONTAINER_NAME: 컨테이너 이름 (기본: PROJECT_NAME)
|
|
27
|
+
#
|
|
28
|
+
# 📦 볼륨 마운트:
|
|
29
|
+
# ENABLE_VOLUME_MOUNT: 볼륨 마운트 활성화 (true/false)
|
|
30
|
+
# VOLUME_HOST_PATH: 호스트 볼륨 경로
|
|
31
|
+
# VOLUME_CONTAINER_PATH: 컨테이너 내부 마운트 경로
|
|
32
|
+
#
|
|
33
|
+
# 🔌 SSH 연결:
|
|
34
|
+
# SSH_PORT: SSH 포트 (기본: 2022)
|
|
35
|
+
# SSH_COMMAND_TIMEOUT: SSH 명령 타임아웃 (기본: 600s)
|
|
36
|
+
# SSH_CONNECTION_TIMEOUT: SSH 연결 타임아웃 (기본: 60s)
|
|
37
|
+
#
|
|
38
|
+
# ⏱️ 헬스체크:
|
|
39
|
+
# HEALTHCHECK_WAIT_SECONDS: 초기화 대기 시간 (기본: 10초)
|
|
40
|
+
# HEALTHCHECK_PATH: HTTP 엔드포인트 경로 (비어있으면 로그만 사용)
|
|
41
|
+
# HEALTHCHECK_MAX_RETRIES: HTTP 재시도 횟수 (기본: 5회)
|
|
42
|
+
# HEALTHCHECK_RETRY_INTERVAL: 재시도 간격 (기본: 3초)
|
|
43
|
+
# HEALTHCHECK_LOG_PATTERN: Fallback 로그 검색 패턴
|
|
44
|
+
#
|
|
45
|
+
# 💡 HEALTHCHECK_PATH 예시:
|
|
46
|
+
# FastAPI: "/docs"
|
|
47
|
+
# 커스텀: "/api/v1/health"
|
|
48
|
+
# ===================================================================
|
|
49
|
+
|
|
50
|
+
name: PROJECT-PYTHON-SIMPLE-CICD
|
|
51
|
+
|
|
52
|
+
# ===================================================================
|
|
53
|
+
# 트리거 설정
|
|
54
|
+
# ===================================================================
|
|
55
|
+
on:
|
|
56
|
+
push:
|
|
57
|
+
branches:
|
|
58
|
+
- "{{MAIN_BRANCH}}" # 프로덕션 배포
|
|
59
|
+
# @wizard paths-anchor (모노레포일 때 integrator가 paths 필터를 여기 주입)
|
|
60
|
+
workflow_dispatch: # 수동 실행 허용
|
|
61
|
+
|
|
62
|
+
# ===================================================================
|
|
63
|
+
# 환경 변수 설정
|
|
64
|
+
# ===================================================================
|
|
65
|
+
env:
|
|
66
|
+
# 프로젝트 고유 식별자
|
|
67
|
+
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
68
|
+
|
|
69
|
+
# 🌐 포트 설정
|
|
70
|
+
DEPLOY_PORT: "__DEPLOY_PORT__" # @wizard ask:8000
|
|
71
|
+
|
|
72
|
+
# 🐍 Python 설정
|
|
73
|
+
PYTHON_VERSION: "3.13"
|
|
74
|
+
|
|
75
|
+
# 🐳 컨테이너 이름 설정 (비워두면 PROJECT_NAME 사용)
|
|
76
|
+
CONTAINER_NAME: ""
|
|
77
|
+
|
|
78
|
+
# 🗂️ 볼륨 마운트 설정 (빈값이면 마운트 안함)
|
|
79
|
+
ENABLE_VOLUME_MOUNT: "false"
|
|
80
|
+
VOLUME_HOST_PATH: "/volume1/projects/__PROJECT_NAME__" # @wizard ask:/volume1/projects/__PROJECT_NAME__
|
|
81
|
+
VOLUME_CONTAINER_PATH: "/mnt/__PROJECT_NAME__" # @wizard ask:/mnt/__PROJECT_NAME__
|
|
82
|
+
|
|
83
|
+
# 🔐 SSH 인증 방식: "password"(Synology·일반 서버) | "key"(AWS EC2·.pem)
|
|
84
|
+
# password → SERVER_PASSWORD secret 사용
|
|
85
|
+
# key → SSH_KEY secret 사용 (.pem 파일 전체 내용을 secret 값으로)
|
|
86
|
+
SSH_AUTH_METHOD: "password" # @wizard ask:password
|
|
87
|
+
|
|
88
|
+
# 🔌 SSH 연결 설정
|
|
89
|
+
SSH_PORT: "2022" # SSH 포트 (Synology 기본: 2022)
|
|
90
|
+
SSH_COMMAND_TIMEOUT: "600s" # SSH 명령 실행 타임아웃
|
|
91
|
+
SSH_CONNECTION_TIMEOUT: "60s" # SSH 연결 타임아웃
|
|
92
|
+
|
|
93
|
+
# 🌐 컨테이너 포트 설정
|
|
94
|
+
CONTAINER_INTERNAL_PORT: "8000" # FastAPI 내부 포트
|
|
95
|
+
|
|
96
|
+
# ⏱️ 헬스체크 설정
|
|
97
|
+
HEALTHCHECK_WAIT_SECONDS: "10" # 컨테이너 초기화 대기 시간 (초)
|
|
98
|
+
HEALTHCHECK_PATH: "/docs" # HTTP 엔드포인트 경로 (비어있으면 로그 패턴만 사용)
|
|
99
|
+
HEALTHCHECK_MAX_RETRIES: "5" # HTTP 재시도 횟수
|
|
100
|
+
HEALTHCHECK_RETRY_INTERVAL: "3" # 재시도 간격 (초)
|
|
101
|
+
HEALTHCHECK_LOG_PATTERN: "Uvicorn running on" # Fallback 로그 검색 패턴
|
|
102
|
+
|
|
103
|
+
jobs:
|
|
104
|
+
# ===================================================================
|
|
105
|
+
# 빌드 작업
|
|
106
|
+
# ===================================================================
|
|
107
|
+
build:
|
|
108
|
+
name: Python FastAPI 애플리케이션 빌드
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
|
|
111
|
+
steps:
|
|
112
|
+
# 1. 소스코드 체크아웃
|
|
113
|
+
- name: 코드 체크아웃
|
|
114
|
+
uses: actions/checkout@v5
|
|
115
|
+
|
|
116
|
+
# 2. 디스크 공간 확보
|
|
117
|
+
- name: 디스크 공간 확보
|
|
118
|
+
run: |
|
|
119
|
+
echo "📊 빌드 전 디스크 사용량:"
|
|
120
|
+
df -h
|
|
121
|
+
|
|
122
|
+
echo "🧹 불필요한 파일 정리 중..."
|
|
123
|
+
sudo rm -rf /usr/share/dotnet || true
|
|
124
|
+
sudo rm -rf /opt/ghc || true
|
|
125
|
+
sudo rm -rf /usr/local/share/boost || true
|
|
126
|
+
sudo rm -rf "$AGENT_TOOLSDIRECTORY" || true
|
|
127
|
+
|
|
128
|
+
echo "🐳 Docker 시스템 정리 중..."
|
|
129
|
+
docker system prune -af --volumes || true
|
|
130
|
+
|
|
131
|
+
echo "📊 정리 후 디스크 사용량:"
|
|
132
|
+
df -h
|
|
133
|
+
|
|
134
|
+
# 3. .env 파일 생성
|
|
135
|
+
- name: .env 파일 생성
|
|
136
|
+
run: |
|
|
137
|
+
cat > .env << 'EOF'
|
|
138
|
+
${{ secrets.ENV_FILE }}
|
|
139
|
+
EOF
|
|
140
|
+
echo "✅ .env 파일이 생성되었습니다"
|
|
141
|
+
|
|
142
|
+
# 4. Docker 빌드 환경 설정
|
|
143
|
+
- name: Docker 빌드환경 설정
|
|
144
|
+
uses: docker/setup-buildx-action@v3
|
|
145
|
+
|
|
146
|
+
# 5. DockerHub 로그인
|
|
147
|
+
- name: DockerHub 로그인
|
|
148
|
+
uses: docker/login-action@v3
|
|
149
|
+
with:
|
|
150
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
151
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
152
|
+
|
|
153
|
+
# 6. Docker 이미지 빌드 및 푸시
|
|
154
|
+
- name: Docker 이미지 빌드 및 푸시
|
|
155
|
+
uses: docker/build-push-action@v5
|
|
156
|
+
with:
|
|
157
|
+
context: .
|
|
158
|
+
file: ./Dockerfile
|
|
159
|
+
push: true
|
|
160
|
+
tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.ref_name }}
|
|
161
|
+
# GitHub Actions 캐시 사용 (로컬 디스크 공간 절약)
|
|
162
|
+
cache-from: type=gha
|
|
163
|
+
cache-to: type=gha,mode=max
|
|
164
|
+
|
|
165
|
+
# 7. 빌드 후 정리
|
|
166
|
+
- name: 빌드 후 정리
|
|
167
|
+
if: always()
|
|
168
|
+
run: |
|
|
169
|
+
echo "🧹 빌드 후 정리 중..."
|
|
170
|
+
docker system prune -f || true
|
|
171
|
+
echo "📊 최종 디스크 사용량:"
|
|
172
|
+
df -h
|
|
173
|
+
|
|
174
|
+
# ===================================================================
|
|
175
|
+
# 배포 작업
|
|
176
|
+
# ===================================================================
|
|
177
|
+
deploy:
|
|
178
|
+
name: 원격 서버 배포
|
|
179
|
+
needs: build
|
|
180
|
+
runs-on: ubuntu-latest
|
|
181
|
+
|
|
182
|
+
steps:
|
|
183
|
+
# SSH 통한 원격 서버 배포 실행
|
|
184
|
+
- name: Deploy
|
|
185
|
+
uses: appleboy/ssh-action@v1.0.3
|
|
186
|
+
env:
|
|
187
|
+
SSH_AUTH_METHOD: ${{ env.SSH_AUTH_METHOD }}
|
|
188
|
+
with:
|
|
189
|
+
host: ${{ secrets.SERVER_HOST }}
|
|
190
|
+
username: ${{ secrets.SERVER_USER }}
|
|
191
|
+
password: ${{ secrets.SERVER_PASSWORD }}
|
|
192
|
+
key: ${{ secrets.SSH_KEY }}
|
|
193
|
+
port: ${{ env.SSH_PORT }}
|
|
194
|
+
command_timeout: ${{ env.SSH_COMMAND_TIMEOUT }}
|
|
195
|
+
timeout: ${{ env.SSH_CONNECTION_TIMEOUT }}
|
|
196
|
+
envs: SSH_AUTH_METHOD
|
|
197
|
+
script: |
|
|
198
|
+
set -e
|
|
199
|
+
|
|
200
|
+
# ============================================================
|
|
201
|
+
# 배포 환경 변수 설정
|
|
202
|
+
# ============================================================
|
|
203
|
+
echo "🔧 환경변수 설정.."
|
|
204
|
+
export PATH=$PATH:/usr/local/bin
|
|
205
|
+
export PW=${{ secrets.SERVER_PASSWORD }}
|
|
206
|
+
|
|
207
|
+
# 🔐 SSH 인증 방식에 따른 sudo 추상화
|
|
208
|
+
# password 모드: echo $PW | sudo -S (Synology 등 sudo 비밀번호 필요 서버)
|
|
209
|
+
# key 모드: sudo 직접 (AWS EC2 등 passwordless sudo 서버)
|
|
210
|
+
SSH_AUTH_METHOD="${SSH_AUTH_METHOD:-password}"
|
|
211
|
+
if [ "${SSH_AUTH_METHOD}" = "key" ]; then
|
|
212
|
+
SUDO() { sudo "$@"; }
|
|
213
|
+
else
|
|
214
|
+
SUDO() { echo "$PW" | sudo -S "$@"; }
|
|
215
|
+
fi
|
|
216
|
+
echo "🔐 SSH 인증 방식: ${SSH_AUTH_METHOD}"
|
|
217
|
+
|
|
218
|
+
# GitHub에서 전달받은 브랜치명
|
|
219
|
+
BRANCH=${{ github.ref_name }}
|
|
220
|
+
|
|
221
|
+
# 프로젝트 설정
|
|
222
|
+
PROJECT_NAME="${{ env.PROJECT_NAME }}"
|
|
223
|
+
|
|
224
|
+
# ============================================================
|
|
225
|
+
# 포트 및 컨테이너명 설정
|
|
226
|
+
# ============================================================
|
|
227
|
+
PORT=${{ env.DEPLOY_PORT }}
|
|
228
|
+
|
|
229
|
+
# 컨테이너 이름 설정
|
|
230
|
+
if [ -z "${{ env.CONTAINER_NAME }}" ]; then
|
|
231
|
+
CONTAINER_NAME="${PROJECT_NAME}"
|
|
232
|
+
echo "ℹ️ CONTAINER_NAME이 설정되지 않아 PROJECT_NAME을 사용합니다: $CONTAINER_NAME"
|
|
233
|
+
else
|
|
234
|
+
CONTAINER_NAME="${{ env.CONTAINER_NAME }}"
|
|
235
|
+
echo "ℹ️ 사용자 지정 CONTAINER_NAME 사용: $CONTAINER_NAME"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
echo "🚀 배포 환경으로 배포합니다 (브랜치: $BRANCH)"
|
|
239
|
+
|
|
240
|
+
# 설정 정보 출력
|
|
241
|
+
echo "📋 배포 설정 정보:"
|
|
242
|
+
echo " - 브랜치: $BRANCH"
|
|
243
|
+
echo " - 프로젝트: $PROJECT_NAME"
|
|
244
|
+
echo " - 컨테이너 이름: $CONTAINER_NAME"
|
|
245
|
+
echo " - 포트: $PORT"
|
|
246
|
+
echo " - Docker 이미지: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
|
|
247
|
+
|
|
248
|
+
# ============================================================
|
|
249
|
+
# Docker 이미지 풀 (Pull)
|
|
250
|
+
# ============================================================
|
|
251
|
+
echo "⬇️ Docker 이미지 풀: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
|
|
252
|
+
SUDO docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
|
|
253
|
+
|
|
254
|
+
# ============================================================
|
|
255
|
+
# 기존 컨테이너 정리
|
|
256
|
+
# ============================================================
|
|
257
|
+
echo "🧹 컨테이너 $CONTAINER_NAME 존재 여부 확인 중..."
|
|
258
|
+
|
|
259
|
+
# 동일한 이름의 컨테이너가 존재하는지 확인
|
|
260
|
+
if SUDO docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
|
|
261
|
+
echo "⚠️ 컨테이너 $CONTAINER_NAME 이(가) 존재합니다. 중지 및 삭제 중..."
|
|
262
|
+
SUDO docker rm -f $CONTAINER_NAME
|
|
263
|
+
echo "✅ 컨테이너 $CONTAINER_NAME 이(가) 삭제되었습니다."
|
|
264
|
+
else
|
|
265
|
+
echo "ℹ️ 존재하는 컨테이너 $CONTAINER_NAME 이(가) 없습니다."
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
# ============================================================
|
|
269
|
+
# 새 컨테이너 실행
|
|
270
|
+
# ============================================================
|
|
271
|
+
echo "🚀 새로운 컨테이너 $CONTAINER_NAME 실행 중..."
|
|
272
|
+
|
|
273
|
+
# Docker 컨테이너 실행
|
|
274
|
+
# - 포트 매핑: ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }} (외부:내부)
|
|
275
|
+
# - 시간대: Asia/Seoul
|
|
276
|
+
# - 볼륨 마운트: 로컬 시간 동기화 및 프로젝트 데이터
|
|
277
|
+
# ⚠️ .env 파일은 이미 Docker 이미지에 포함되어 있습니다
|
|
278
|
+
|
|
279
|
+
# 볼륨 마운트 옵션 구성
|
|
280
|
+
VOLUME_OPTS="-v /etc/localtime:/etc/localtime:ro"
|
|
281
|
+
if [ "${{ env.ENABLE_VOLUME_MOUNT }}" == "true" ]; then
|
|
282
|
+
# 디렉토리가 없으면 생성
|
|
283
|
+
if [ ! -d "${{ env.VOLUME_HOST_PATH }}" ]; then
|
|
284
|
+
echo "📁 볼륨 마운트 디렉토리 생성: ${{ env.VOLUME_HOST_PATH }}"
|
|
285
|
+
SUDO mkdir -p "${{ env.VOLUME_HOST_PATH }}"
|
|
286
|
+
fi
|
|
287
|
+
VOLUME_OPTS="$VOLUME_OPTS -v ${{ env.VOLUME_HOST_PATH }}:${{ env.VOLUME_CONTAINER_PATH }}"
|
|
288
|
+
echo "📦 볼륨 마운트 활성화: $VOLUME_OPTS"
|
|
289
|
+
else
|
|
290
|
+
echo "ℹ️ 볼륨 마운트 비활성화됨"
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
SUDO docker run -d \
|
|
294
|
+
-p ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }} \
|
|
295
|
+
--name $CONTAINER_NAME \
|
|
296
|
+
-e TZ=Asia/Seoul \
|
|
297
|
+
$VOLUME_OPTS \
|
|
298
|
+
${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
|
|
299
|
+
|
|
300
|
+
# ============================================================
|
|
301
|
+
# 컨테이너 헬스체크
|
|
302
|
+
# ============================================================
|
|
303
|
+
echo "⏳ 컨테이너 초기화 대기 중... (${{ env.HEALTHCHECK_WAIT_SECONDS }}초)"
|
|
304
|
+
sleep ${{ env.HEALTHCHECK_WAIT_SECONDS }}
|
|
305
|
+
|
|
306
|
+
# 1단계: 컨테이너 프로세스 확인
|
|
307
|
+
echo "🏥 컨테이너 헬스체크 중..."
|
|
308
|
+
if ! SUDO docker ps --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
|
|
309
|
+
echo "❌ 컨테이너 $CONTAINER_NAME 프로세스가 실행되지 않았습니다"
|
|
310
|
+
SUDO docker logs --tail 50 $CONTAINER_NAME || true
|
|
311
|
+
exit 1
|
|
312
|
+
fi
|
|
313
|
+
echo "✅ 컨테이너 프로세스 실행 확인 완료"
|
|
314
|
+
|
|
315
|
+
# 2단계: HTTP 엔드포인트 체크 (설정된 경우)
|
|
316
|
+
HEALTHCHECK_SUCCESS=false
|
|
317
|
+
if [ -n "${{ env.HEALTHCHECK_PATH }}" ]; then
|
|
318
|
+
echo "🌐 HTTP 헬스체크 시작: http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }}"
|
|
319
|
+
|
|
320
|
+
for i in $(seq 1 ${{ env.HEALTHCHECK_MAX_RETRIES }}); do
|
|
321
|
+
echo " 시도 $i/${{ env.HEALTHCHECK_MAX_RETRIES }}..."
|
|
322
|
+
|
|
323
|
+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }} || echo "000")
|
|
324
|
+
|
|
325
|
+
if [ "$HTTP_CODE" = "200" ]; then
|
|
326
|
+
echo "✅ HTTP 헬스체크 성공 (응답 코드: $HTTP_CODE)"
|
|
327
|
+
HEALTHCHECK_SUCCESS=true
|
|
328
|
+
break
|
|
329
|
+
else
|
|
330
|
+
echo "⚠️ HTTP 헬스체크 실패 (응답 코드: $HTTP_CODE)"
|
|
331
|
+
if [ $i -lt ${{ env.HEALTHCHECK_MAX_RETRIES }} ]; then
|
|
332
|
+
echo " ${{ env.HEALTHCHECK_RETRY_INTERVAL }}초 후 재시도..."
|
|
333
|
+
sleep ${{ env.HEALTHCHECK_RETRY_INTERVAL }}
|
|
334
|
+
fi
|
|
335
|
+
fi
|
|
336
|
+
done
|
|
337
|
+
|
|
338
|
+
# HTTP 체크 실패 시 fallback으로 로그 패턴 매칭
|
|
339
|
+
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
340
|
+
echo "⚠️ HTTP 헬스체크 최종 실패, 로그 패턴 매칭으로 fallback..."
|
|
341
|
+
fi
|
|
342
|
+
else
|
|
343
|
+
echo "ℹ️ HEALTHCHECK_PATH가 설정되지 않아 로그 패턴 매칭만 사용합니다"
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
# 3단계: 로그 패턴 매칭 (HTTP 실패 시 또는 HTTP 체크 비활성화 시)
|
|
347
|
+
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
348
|
+
echo "📋 로그 패턴 매칭 시작: '${{ env.HEALTHCHECK_LOG_PATTERN }}'"
|
|
349
|
+
|
|
350
|
+
if SUDO docker logs $CONTAINER_NAME 2>&1 | grep -q "${{ env.HEALTHCHECK_LOG_PATTERN }}"; then
|
|
351
|
+
echo "✅ 로그 패턴 매칭 성공: 애플리케이션이 정상적으로 시작되었습니다"
|
|
352
|
+
HEALTHCHECK_SUCCESS=true
|
|
353
|
+
else
|
|
354
|
+
echo "❌ 로그 패턴 매칭 실패: 애플리케이션 시작 로그를 찾을 수 없습니다"
|
|
355
|
+
echo ""
|
|
356
|
+
echo "📋 디버깅 정보:"
|
|
357
|
+
echo " - 검색 패턴: ${{ env.HEALTHCHECK_LOG_PATTERN }}"
|
|
358
|
+
echo " - 컨테이너: $CONTAINER_NAME"
|
|
359
|
+
echo " - 이미지: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
|
|
360
|
+
echo " - 포트: ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }}"
|
|
361
|
+
echo " - 볼륨: $VOLUME_OPTS"
|
|
362
|
+
echo ""
|
|
363
|
+
echo "📋 컨테이너 로그 (최근 50줄):"
|
|
364
|
+
SUDO docker logs --tail 50 $CONTAINER_NAME || true
|
|
365
|
+
exit 1
|
|
366
|
+
fi
|
|
367
|
+
fi
|
|
368
|
+
|
|
369
|
+
# 최종 성공 메시지
|
|
370
|
+
if [ "$HEALTHCHECK_SUCCESS" = "true" ]; then
|
|
371
|
+
echo "✅ 헬스체크 완료: 컨테이너 $CONTAINER_NAME 이(가) 정상적으로 실행 중입니다"
|
|
372
|
+
fi
|
|
373
|
+
|
|
374
|
+
# ============================================================
|
|
375
|
+
# 배포 완료 확인
|
|
376
|
+
# ============================================================
|
|
377
|
+
echo "✅ 배포가 성공적으로 완료되었습니다!"
|
|
378
|
+
echo ""
|
|
379
|
+
echo "📋 배포 결과 요약:"
|
|
380
|
+
echo " 🎯 프로젝트: $PROJECT_NAME"
|
|
381
|
+
echo " 🌿 브랜치: $BRANCH"
|
|
382
|
+
echo " 🐳 컨테이너: $CONTAINER_NAME"
|
|
383
|
+
echo " 🌐 포트: $PORT"
|
|
384
|
+
echo " ⏰ 배포 시간: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
385
|
+
echo ""
|
|
386
|
+
echo "🔗 접속 URL: http://${{ secrets.SERVER_HOST }}:${PORT}/docs"
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# ===================================================================
|
|
2
|
+
# React CI (코드 분석 및 빌드 검증)
|
|
3
|
+
# ===================================================================
|
|
4
|
+
#
|
|
5
|
+
# {{MAIN_BRANCH}} 브랜치 푸시 시 React 애플리케이션의 빌드를 검증합니다.
|
|
6
|
+
#
|
|
7
|
+
# 주요 특징:
|
|
8
|
+
# - npm ci로 의존성 설치
|
|
9
|
+
# - npm run build로 빌드 검증
|
|
10
|
+
# - node_modules 및 빌드 캐싱 지원
|
|
11
|
+
#
|
|
12
|
+
# ===================================================================
|
|
13
|
+
# 🔑 필수 GitHub Secrets
|
|
14
|
+
# ===================================================================
|
|
15
|
+
# ENV_FILE (선택): .env 파일 내용
|
|
16
|
+
#
|
|
17
|
+
# ※ CI는 빌드 검증 목적이므로 배포 관련 Secrets 불필요
|
|
18
|
+
# ===================================================================
|
|
19
|
+
#
|
|
20
|
+
# 🔧 프로젝트별 설정 (env 섹션)
|
|
21
|
+
# ===================================================================
|
|
22
|
+
# PROJECT_NAME : 프로젝트 이름
|
|
23
|
+
# NODE_VERSION : Node.js 버전 (기본: 20.15.0)
|
|
24
|
+
# ENV_FILE : 환경변수 파일명 (기본: .env)
|
|
25
|
+
# ===================================================================
|
|
26
|
+
|
|
27
|
+
name: PROJECT-REACT-CI
|
|
28
|
+
|
|
29
|
+
on:
|
|
30
|
+
push:
|
|
31
|
+
branches: ["{{DEVELOP_BRANCH}}"]
|
|
32
|
+
workflow_dispatch:
|
|
33
|
+
|
|
34
|
+
env:
|
|
35
|
+
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
36
|
+
NODE_VERSION: "20.15.0"
|
|
37
|
+
ENV_FILE: ".env"
|
|
38
|
+
|
|
39
|
+
permissions:
|
|
40
|
+
contents: read
|
|
41
|
+
pull-requests: write
|
|
42
|
+
issues: write
|
|
43
|
+
|
|
44
|
+
jobs:
|
|
45
|
+
build:
|
|
46
|
+
name: React/Next.js 애플리케이션 빌드
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
|
|
49
|
+
steps:
|
|
50
|
+
# 1. 소스코드 체크아웃
|
|
51
|
+
- name: 코드 체크아웃
|
|
52
|
+
uses: actions/checkout@v5
|
|
53
|
+
|
|
54
|
+
# 2. 환경변수 파일 생성
|
|
55
|
+
- name: 환경변수 파일 생성 (${{ env.ENV_FILE }})
|
|
56
|
+
run: |
|
|
57
|
+
echo -e "${{ secrets.ENV_FILE || secrets.ENV }}" > ${{ env.ENV_FILE }}
|
|
58
|
+
echo "✅ ${{ env.ENV_FILE }} 파일 생성 완료"
|
|
59
|
+
|
|
60
|
+
# 3. Node.js 설정
|
|
61
|
+
- name: Node.js 설정
|
|
62
|
+
uses: actions/setup-node@v4
|
|
63
|
+
with:
|
|
64
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
65
|
+
cache: "npm"
|
|
66
|
+
|
|
67
|
+
# 4. npm 의존성 캐싱
|
|
68
|
+
- name: npm 의존성 캐싱
|
|
69
|
+
uses: actions/cache@v4
|
|
70
|
+
with:
|
|
71
|
+
path: ~/.npm
|
|
72
|
+
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
|
73
|
+
restore-keys: |
|
|
74
|
+
${{ runner.os }}-npm-
|
|
75
|
+
|
|
76
|
+
# 5. node_modules 캐싱
|
|
77
|
+
# package-lock.json이 변경되지 않으면 npm ci 건너뛰기
|
|
78
|
+
- name: node_modules 캐싱
|
|
79
|
+
id: node-modules-cache
|
|
80
|
+
uses: actions/cache@v4
|
|
81
|
+
with:
|
|
82
|
+
path: "**/node_modules"
|
|
83
|
+
key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }}
|
|
84
|
+
restore-keys: |
|
|
85
|
+
${{ runner.os }}-node-modules-
|
|
86
|
+
|
|
87
|
+
# 6. 의존성 설치
|
|
88
|
+
# 캐시가 없을 때만 실행 (npm ci는 package-lock.json 기준 정확한 버전 설치)
|
|
89
|
+
- name: 의존성 설치
|
|
90
|
+
if: steps.node-modules-cache.outputs.cache-hit != 'true'
|
|
91
|
+
run: |
|
|
92
|
+
echo "📦 의존성 설치 중..."
|
|
93
|
+
npm ci
|
|
94
|
+
echo "✅ 의존성 설치 완료"
|
|
95
|
+
|
|
96
|
+
# 7. Next.js 빌드 캐싱 (Next.js 프로젝트인 경우)
|
|
97
|
+
# .next/cache 폴더를 캐싱하여 증분 빌드 속도 향상
|
|
98
|
+
- name: Next.js 빌드 캐싱
|
|
99
|
+
uses: actions/cache@v4
|
|
100
|
+
with:
|
|
101
|
+
path: .next/cache
|
|
102
|
+
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
|
|
103
|
+
restore-keys: |
|
|
104
|
+
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
|
|
105
|
+
${{ runner.os }}-nextjs-
|
|
106
|
+
|
|
107
|
+
# 8. 프로젝트 빌드
|
|
108
|
+
# 빌드 실패 시에도 워크플로우를 중단하지 않고 에러 로그 수집
|
|
109
|
+
- name: 프로젝트 빌드
|
|
110
|
+
id: build
|
|
111
|
+
run: |
|
|
112
|
+
echo "🔨 빌드 시작..."
|
|
113
|
+
echo "프로젝트: ${{ env.PROJECT_NAME }}"
|
|
114
|
+
echo "Node.js: ${{ env.NODE_VERSION }}"
|
|
115
|
+
echo ""
|
|
116
|
+
|
|
117
|
+
# 빌드 실행 및 로그 저장
|
|
118
|
+
# 빌드 실패 시 exit 0으로 워크플로우 계속 진행 (에러 로그 수집 목적)
|
|
119
|
+
npm run build > build_log.txt 2>&1 || {
|
|
120
|
+
echo "build_failed=true" >> $GITHUB_OUTPUT
|
|
121
|
+
echo "status=failed" >> $GITHUB_OUTPUT
|
|
122
|
+
echo ""
|
|
123
|
+
echo "❌ 빌드 실패"
|
|
124
|
+
exit 0
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
echo "build_failed=false" >> $GITHUB_OUTPUT
|
|
128
|
+
echo "status=success" >> $GITHUB_OUTPUT
|
|
129
|
+
echo "✅ 빌드 완료"
|
|
130
|
+
|
|
131
|
+
# 9. 빌드 실패 시 에러 로그 출력
|
|
132
|
+
# 빌드가 실패한 경우에만 실행
|
|
133
|
+
- name: 빌드 에러 로그 출력
|
|
134
|
+
if: steps.build.outputs.build_failed == 'true'
|
|
135
|
+
run: |
|
|
136
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
137
|
+
echo "❌ 빌드 실패 - 에러 로그"
|
|
138
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
139
|
+
echo ""
|
|
140
|
+
|
|
141
|
+
# 로그 파일의 마지막 50줄만 출력 (전체 로그는 아티팩트에서 확인 가능)
|
|
142
|
+
if [ -f "build_log.txt" ]; then
|
|
143
|
+
tail -n 50 build_log.txt
|
|
144
|
+
else
|
|
145
|
+
echo "에러 로그 파일을 찾을 수 없습니다."
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
echo ""
|
|
149
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
150
|
+
|
|
151
|
+
# 빌드 실패로 워크플로우 종료
|
|
152
|
+
exit 1
|
|
153
|
+
|
|
154
|
+
# 10. 빌드 로그 아티팩트 업로드
|
|
155
|
+
# 빌드 성공/실패 여부와 관계없이 로그 저장
|
|
156
|
+
- name: 빌드 로그 업로드
|
|
157
|
+
if: always()
|
|
158
|
+
uses: actions/upload-artifact@v4
|
|
159
|
+
with:
|
|
160
|
+
name: build-log
|
|
161
|
+
path: build_log.txt
|
|
162
|
+
retention-days: 7 # 7일간 보관
|
|
163
|
+
|
|
164
|
+
# 11. 빌드 결과 요약
|
|
165
|
+
# 워크플로우 실행 결과를 보기 쉽게 요약
|
|
166
|
+
- name: 빌드 결과 요약
|
|
167
|
+
if: always()
|
|
168
|
+
run: |
|
|
169
|
+
echo ""
|
|
170
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
171
|
+
if [ "${{ steps.build.outputs.status }}" = "success" ]; then
|
|
172
|
+
echo "🎉 빌드 워크플로우 완료!"
|
|
173
|
+
else
|
|
174
|
+
echo "❌ 빌드 워크플로우 실패"
|
|
175
|
+
fi
|
|
176
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
177
|
+
echo ""
|
|
178
|
+
echo "📊 빌드 결과:"
|
|
179
|
+
echo " 📦 프로젝트: ${{ env.PROJECT_NAME }}"
|
|
180
|
+
echo " 📌 Node.js 버전: ${{ env.NODE_VERSION }}"
|
|
181
|
+
echo " 🆙 빌드 상태: ${{ steps.build.outputs.status }}"
|
|
182
|
+
echo " 🌿 브랜치: ${{ github.ref_name }}"
|
|
183
|
+
echo " 📝 커밋: ${{ github.sha }}"
|
|
184
|
+
echo " 🕐 시간: $(date '+%Y-%m-%d %H:%M:%S UTC')"
|
|
185
|
+
echo ""
|
|
186
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
187
|
+
echo ""
|
|
188
|
+
|
|
189
|
+
if [ "${{ steps.build.outputs.status }}" = "success" ]; then
|
|
190
|
+
echo "✅ 모든 단계가 성공적으로 완료되었습니다."
|
|
191
|
+
else
|
|
192
|
+
echo "❌ 빌드 중 오류가 발생했습니다."
|
|
193
|
+
echo "💡 Actions 탭에서 'build-log' 아티팩트를 다운로드하여 전체 로그를 확인하세요."
|
|
194
|
+
fi
|