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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +139 -0
  3. package/bin/project-auto-wizard.js +19 -0
  4. package/package.json +36 -0
  5. package/payload/coderabbit.yaml +19 -0
  6. package/payload/config/breaking-changes.json +1 -0
  7. package/payload/config/wizard-prompts.yml +62 -0
  8. package/payload/scripts/changelog_manager.py +726 -0
  9. package/payload/scripts/version_manager.py +617 -0
  10. package/payload/version.yml.template +48 -0
  11. package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +303 -0
  12. package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +293 -0
  13. package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +289 -0
  14. package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +192 -0
  15. package/payload/workflows/common/secret-backup/PROJECT-COMMON-SECRET-FILE-UPLOAD.yaml +209 -0
  16. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +591 -0
  17. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +700 -0
  18. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +308 -0
  19. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +992 -0
  20. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +689 -0
  21. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +987 -0
  22. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +471 -0
  23. package/payload/workflows/flutter/PROJECT-FLUTTER-SUH-LAB-APP-BUILD-TRIGGER.yaml +500 -0
  24. package/payload/workflows/next/PROJECT-NEXT-CI.yaml +185 -0
  25. package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +271 -0
  26. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +81 -0
  27. package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +2191 -0
  28. package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +386 -0
  29. package/payload/workflows/react/PROJECT-REACT-CI.yaml +194 -0
  30. package/payload/workflows/react/PROJECT-REACT-CICD.yaml +255 -0
  31. package/payload/workflows/spring/PROJECT-SPRING-GITHUB-PACKAGES-PUBLISH.yml +84 -0
  32. package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-CI.yml +316 -0
  33. package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-PUBLISH.yml +58 -0
  34. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +535 -0
  35. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +437 -0
  36. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +2277 -0
  37. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +425 -0
  38. package/src/cli/args.js +95 -0
  39. package/src/cli/help.js +27 -0
  40. package/src/commands/full.js +53 -0
  41. package/src/commands/interactive.js +276 -0
  42. package/src/commands/revert.js +60 -0
  43. package/src/commands/version.js +31 -0
  44. package/src/commands/workflows.js +49 -0
  45. package/src/context.js +26 -0
  46. package/src/core/assets.js +47 -0
  47. package/src/core/branches.js +60 -0
  48. package/src/core/branding.js +15 -0
  49. package/src/core/breaking-check.js +65 -0
  50. package/src/core/breaking.js +26 -0
  51. package/src/core/copy/coderabbit.js +26 -0
  52. package/src/core/copy/gitignore.js +55 -0
  53. package/src/core/copy/readme.js +30 -0
  54. package/src/core/copy/simple.js +23 -0
  55. package/src/core/copy/workflows.js +233 -0
  56. package/src/core/detect-fs.js +106 -0
  57. package/src/core/detect.js +62 -0
  58. package/src/core/fsutil.js +46 -0
  59. package/src/core/options-ask.js +99 -0
  60. package/src/core/paths-resolve.js +261 -0
  61. package/src/core/paths.js +16 -0
  62. package/src/core/version-yml.js +190 -0
  63. package/src/core/wizard-env.js +101 -0
  64. package/src/core/wizard-labels.js +107 -0
  65. package/src/index.js +162 -0
  66. package/src/ui/ansi.js +26 -0
  67. package/src/ui/banner.js +29 -0
  68. package/src/ui/env-plan.js +207 -0
  69. package/src/ui/prompts.js +99 -0
  70. package/src/ui/readline-engine.js +257 -0
  71. package/src/ui/status-cards.js +56 -0
  72. 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