project-auto-wizard 0.5.0 → 0.6.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.md +12 -17
- package/package.json +1 -1
- 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
- package/payload/scripts/changelog_manager.py +37 -0
- package/payload/scripts/issue_helper.py +110 -6
- package/payload/scripts/version_manager.py +2 -0
- package/payload/version.yml.template +2 -1
- package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +20 -0
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +35 -1
- package/payload/workflows/go/PROJECT-GO-CI.yaml +82 -0
- package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +2193 -0
- package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +384 -0
- package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-CI.yml +5 -2
- package/payload/workflows/spring/nexus/PROJECT-SPRING-NEXUS-PUBLISH.yml +12 -2
- package/src/cli/help.js +1 -1
- package/src/commands/doctor.js +8 -11
- package/src/context.js +1 -1
- package/src/core/detect.js +2 -1
- package/src/core/paths-resolve.js +2 -1
- package/src/ui/prompts.js +1 -1
- package/src/ui/summary.js +3 -2
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# project-auto-wizard:managed-workflow
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# Go CI/CD 배포 (SSH + Docker)
|
|
4
|
+
# ===================================================================
|
|
5
|
+
#
|
|
6
|
+
# {{MAIN_BRANCH}} 브랜치 푸시 시 Docker 이미지를 빌드하고
|
|
7
|
+
# SSH로 접속 가능한 서버(Synology·AWS EC2·VPS 등)에 자동 배포합니다.
|
|
8
|
+
#
|
|
9
|
+
# ===================================================================
|
|
10
|
+
# 🔑 필수 GitHub Secrets
|
|
11
|
+
# ===================================================================
|
|
12
|
+
# ENV_FILE: .env 파일 내용
|
|
13
|
+
# DOCKERHUB_USERNAME: DockerHub 사용자명
|
|
14
|
+
# DOCKERHUB_TOKEN: DockerHub 액세스 토큰
|
|
15
|
+
# SERVER_HOST: 배포 서버 주소
|
|
16
|
+
# SERVER_USER: SSH 사용자명
|
|
17
|
+
# SERVER_PASSWORD: SSH 비밀번호 (SSH_AUTH_METHOD=password일 때)
|
|
18
|
+
# SSH_KEY: SSH 개인키 .pem 내용 (SSH_AUTH_METHOD=key일 때, AWS EC2 등)
|
|
19
|
+
# ===================================================================
|
|
20
|
+
#
|
|
21
|
+
# 🔧 환경변수 설정 (env 섹션에서 설정)
|
|
22
|
+
# ===================================================================
|
|
23
|
+
#
|
|
24
|
+
# 🌐 포트 및 컨테이너:
|
|
25
|
+
# CONTAINER_INTERNAL_PORT: Go 앱 내부 포트 (기본: 8080)
|
|
26
|
+
# DEPLOY_PORT: 배포 포트
|
|
27
|
+
# CONTAINER_NAME: 컨테이너 이름 (기본: PROJECT_NAME)
|
|
28
|
+
#
|
|
29
|
+
# 📦 볼륨 마운트:
|
|
30
|
+
# ENABLE_VOLUME_MOUNT: 볼륨 마운트 활성화 (true/false)
|
|
31
|
+
# VOLUME_HOST_PATH: 호스트 볼륨 경로
|
|
32
|
+
# VOLUME_CONTAINER_PATH: 컨테이너 내부 마운트 경로
|
|
33
|
+
#
|
|
34
|
+
# 🔌 SSH 연결:
|
|
35
|
+
# SSH_PORT: SSH 포트 (기본: 2022)
|
|
36
|
+
# SSH_COMMAND_TIMEOUT: SSH 명령 타임아웃 (기본: 600s)
|
|
37
|
+
# SSH_CONNECTION_TIMEOUT: SSH 연결 타임아웃 (기본: 60s)
|
|
38
|
+
#
|
|
39
|
+
# ⏱️ 헬스체크:
|
|
40
|
+
# HEALTHCHECK_WAIT_SECONDS: 초기화 대기 시간 (기본: 10초)
|
|
41
|
+
# HEALTHCHECK_PATH: HTTP 엔드포인트 경로 (비어있으면 로그만 사용)
|
|
42
|
+
# HEALTHCHECK_MAX_RETRIES: HTTP 재시도 횟수 (기본: 5회)
|
|
43
|
+
# HEALTHCHECK_RETRY_INTERVAL: 재시도 간격 (기본: 3초)
|
|
44
|
+
# HEALTHCHECK_LOG_PATTERN: Fallback 로그 검색 패턴
|
|
45
|
+
#
|
|
46
|
+
# 💡 HEALTHCHECK_PATH 예시:
|
|
47
|
+
# Go (net/http 등): "/health"
|
|
48
|
+
# 커스텀: "/api/v1/health"
|
|
49
|
+
# ===================================================================
|
|
50
|
+
|
|
51
|
+
name: PROJECT-GO-SIMPLE-CICD
|
|
52
|
+
|
|
53
|
+
# ===================================================================
|
|
54
|
+
# 트리거 설정
|
|
55
|
+
# ===================================================================
|
|
56
|
+
on:
|
|
57
|
+
push:
|
|
58
|
+
branches:
|
|
59
|
+
- "{{MAIN_BRANCH}}" # 프로덕션 배포
|
|
60
|
+
# @wizard paths-anchor (모노레포일 때 integrator가 paths 필터를 여기 주입)
|
|
61
|
+
workflow_dispatch: # 수동 실행 허용
|
|
62
|
+
|
|
63
|
+
# ===================================================================
|
|
64
|
+
# 환경 변수 설정
|
|
65
|
+
# ===================================================================
|
|
66
|
+
env:
|
|
67
|
+
# 프로젝트 고유 식별자
|
|
68
|
+
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
69
|
+
|
|
70
|
+
# 🌐 포트 설정
|
|
71
|
+
DEPLOY_PORT: "__DEPLOY_PORT__" # @wizard ask:8080
|
|
72
|
+
|
|
73
|
+
# 🐳 컨테이너 이름 설정 (비워두면 PROJECT_NAME 사용)
|
|
74
|
+
CONTAINER_NAME: ""
|
|
75
|
+
|
|
76
|
+
# 🗂️ 볼륨 마운트 설정 (빈값이면 마운트 안함)
|
|
77
|
+
ENABLE_VOLUME_MOUNT: "false"
|
|
78
|
+
VOLUME_HOST_PATH: "/volume1/projects/__PROJECT_NAME__" # @wizard ask:/volume1/projects/__PROJECT_NAME__
|
|
79
|
+
VOLUME_CONTAINER_PATH: "/mnt/__PROJECT_NAME__" # @wizard ask:/mnt/__PROJECT_NAME__
|
|
80
|
+
|
|
81
|
+
# 🔐 SSH 인증 방식: "password"(Synology·일반 서버) | "key"(AWS EC2·.pem)
|
|
82
|
+
# password → SERVER_PASSWORD secret 사용
|
|
83
|
+
# key → SSH_KEY secret 사용 (.pem 파일 전체 내용을 secret 값으로)
|
|
84
|
+
SSH_AUTH_METHOD: "password" # @wizard ask:password
|
|
85
|
+
|
|
86
|
+
# 🔌 SSH 연결 설정
|
|
87
|
+
SSH_PORT: "__SSH_PORT__" # @wizard ask:2022
|
|
88
|
+
SSH_COMMAND_TIMEOUT: "600s" # SSH 명령 실행 타임아웃
|
|
89
|
+
SSH_CONNECTION_TIMEOUT: "60s" # SSH 연결 타임아웃
|
|
90
|
+
|
|
91
|
+
# 🌐 컨테이너 포트 설정
|
|
92
|
+
CONTAINER_INTERNAL_PORT: "8080" # Go 앱 내부 포트
|
|
93
|
+
|
|
94
|
+
# ⏱️ 헬스체크 설정
|
|
95
|
+
HEALTHCHECK_WAIT_SECONDS: "10" # 컨테이너 초기화 대기 시간 (초)
|
|
96
|
+
HEALTHCHECK_PATH: "/health" # HTTP 엔드포인트 경로 (비어있으면 로그 패턴만 사용)
|
|
97
|
+
HEALTHCHECK_MAX_RETRIES: "5" # HTTP 재시도 횟수
|
|
98
|
+
HEALTHCHECK_RETRY_INTERVAL: "3" # 재시도 간격 (초)
|
|
99
|
+
HEALTHCHECK_LOG_PATTERN: "listening on" # Fallback 로그 검색 패턴 (프로젝트 로그 문구에 맞게 조정)
|
|
100
|
+
|
|
101
|
+
jobs:
|
|
102
|
+
# ===================================================================
|
|
103
|
+
# 빌드 작업
|
|
104
|
+
# ===================================================================
|
|
105
|
+
build:
|
|
106
|
+
name: Go 애플리케이션 빌드
|
|
107
|
+
runs-on: ubuntu-latest
|
|
108
|
+
|
|
109
|
+
steps:
|
|
110
|
+
# 1. 소스코드 체크아웃
|
|
111
|
+
- name: 코드 체크아웃
|
|
112
|
+
uses: actions/checkout@v7
|
|
113
|
+
|
|
114
|
+
# 2. 디스크 공간 확보
|
|
115
|
+
- name: 디스크 공간 확보
|
|
116
|
+
run: |
|
|
117
|
+
echo "📊 빌드 전 디스크 사용량:"
|
|
118
|
+
df -h
|
|
119
|
+
|
|
120
|
+
echo "🧹 불필요한 파일 정리 중..."
|
|
121
|
+
sudo rm -rf /usr/share/dotnet || true
|
|
122
|
+
sudo rm -rf /opt/ghc || true
|
|
123
|
+
sudo rm -rf /usr/local/share/boost || true
|
|
124
|
+
sudo rm -rf "$AGENT_TOOLSDIRECTORY" || true
|
|
125
|
+
|
|
126
|
+
echo "🐳 Docker 시스템 정리 중..."
|
|
127
|
+
docker system prune -af --volumes || true
|
|
128
|
+
|
|
129
|
+
echo "📊 정리 후 디스크 사용량:"
|
|
130
|
+
df -h
|
|
131
|
+
|
|
132
|
+
# 3. .env 파일 생성
|
|
133
|
+
- name: .env 파일 생성
|
|
134
|
+
run: |
|
|
135
|
+
cat > .env << 'EOF'
|
|
136
|
+
${{ secrets.ENV_FILE }}
|
|
137
|
+
EOF
|
|
138
|
+
echo "✅ .env 파일이 생성되었습니다"
|
|
139
|
+
|
|
140
|
+
# 4. Docker 빌드 환경 설정
|
|
141
|
+
- name: Docker 빌드환경 설정
|
|
142
|
+
uses: docker/setup-buildx-action@v3
|
|
143
|
+
|
|
144
|
+
# 5. DockerHub 로그인
|
|
145
|
+
- name: DockerHub 로그인
|
|
146
|
+
uses: docker/login-action@v3
|
|
147
|
+
with:
|
|
148
|
+
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
149
|
+
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
150
|
+
|
|
151
|
+
# 6. Docker 이미지 빌드 및 푸시
|
|
152
|
+
- name: Docker 이미지 빌드 및 푸시
|
|
153
|
+
uses: docker/build-push-action@v5
|
|
154
|
+
with:
|
|
155
|
+
context: .
|
|
156
|
+
file: ./Dockerfile
|
|
157
|
+
push: true
|
|
158
|
+
tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.ref_name }}
|
|
159
|
+
# GitHub Actions 캐시 사용 (로컬 디스크 공간 절약)
|
|
160
|
+
cache-from: type=gha
|
|
161
|
+
cache-to: type=gha,mode=max
|
|
162
|
+
|
|
163
|
+
# 7. 빌드 후 정리
|
|
164
|
+
- name: 빌드 후 정리
|
|
165
|
+
if: always()
|
|
166
|
+
run: |
|
|
167
|
+
echo "🧹 빌드 후 정리 중..."
|
|
168
|
+
docker system prune -f || true
|
|
169
|
+
echo "📊 최종 디스크 사용량:"
|
|
170
|
+
df -h
|
|
171
|
+
|
|
172
|
+
# ===================================================================
|
|
173
|
+
# 배포 작업
|
|
174
|
+
# ===================================================================
|
|
175
|
+
deploy:
|
|
176
|
+
name: 원격 서버 배포
|
|
177
|
+
needs: build
|
|
178
|
+
runs-on: ubuntu-latest
|
|
179
|
+
|
|
180
|
+
steps:
|
|
181
|
+
# SSH 통한 원격 서버 배포 실행
|
|
182
|
+
- name: Deploy
|
|
183
|
+
uses: appleboy/ssh-action@v1.0.3
|
|
184
|
+
env:
|
|
185
|
+
SSH_AUTH_METHOD: ${{ env.SSH_AUTH_METHOD }}
|
|
186
|
+
with:
|
|
187
|
+
host: ${{ secrets.SERVER_HOST }}
|
|
188
|
+
username: ${{ secrets.SERVER_USER }}
|
|
189
|
+
password: ${{ secrets.SERVER_PASSWORD }}
|
|
190
|
+
key: ${{ secrets.SSH_KEY }}
|
|
191
|
+
port: ${{ env.SSH_PORT }}
|
|
192
|
+
command_timeout: ${{ env.SSH_COMMAND_TIMEOUT }}
|
|
193
|
+
timeout: ${{ env.SSH_CONNECTION_TIMEOUT }}
|
|
194
|
+
envs: SSH_AUTH_METHOD
|
|
195
|
+
script: |
|
|
196
|
+
set -e
|
|
197
|
+
|
|
198
|
+
# ============================================================
|
|
199
|
+
# 배포 환경 변수 설정
|
|
200
|
+
# ============================================================
|
|
201
|
+
echo "🔧 환경변수 설정.."
|
|
202
|
+
export PATH=$PATH:/usr/local/bin
|
|
203
|
+
export PW=${{ secrets.SERVER_PASSWORD }}
|
|
204
|
+
|
|
205
|
+
# 🔐 SSH 인증 방식에 따른 sudo 추상화
|
|
206
|
+
# password 모드: echo $PW | sudo -S (Synology 등 sudo 비밀번호 필요 서버)
|
|
207
|
+
# key 모드: sudo 직접 (AWS EC2 등 passwordless sudo 서버)
|
|
208
|
+
SSH_AUTH_METHOD="${SSH_AUTH_METHOD:-password}"
|
|
209
|
+
if [ "${SSH_AUTH_METHOD}" = "key" ]; then
|
|
210
|
+
SUDO() { sudo "$@"; }
|
|
211
|
+
else
|
|
212
|
+
SUDO() { echo "$PW" | sudo -S "$@"; }
|
|
213
|
+
fi
|
|
214
|
+
echo "🔐 SSH 인증 방식: ${SSH_AUTH_METHOD}"
|
|
215
|
+
|
|
216
|
+
# GitHub에서 전달받은 브랜치명
|
|
217
|
+
BRANCH=${{ github.ref_name }}
|
|
218
|
+
|
|
219
|
+
# 프로젝트 설정
|
|
220
|
+
PROJECT_NAME="${{ env.PROJECT_NAME }}"
|
|
221
|
+
|
|
222
|
+
# ============================================================
|
|
223
|
+
# 포트 및 컨테이너명 설정
|
|
224
|
+
# ============================================================
|
|
225
|
+
PORT=${{ env.DEPLOY_PORT }}
|
|
226
|
+
|
|
227
|
+
# 컨테이너 이름 설정
|
|
228
|
+
if [ -z "${{ env.CONTAINER_NAME }}" ]; then
|
|
229
|
+
CONTAINER_NAME="${PROJECT_NAME}"
|
|
230
|
+
echo "ℹ️ CONTAINER_NAME이 설정되지 않아 PROJECT_NAME을 사용합니다: $CONTAINER_NAME"
|
|
231
|
+
else
|
|
232
|
+
CONTAINER_NAME="${{ env.CONTAINER_NAME }}"
|
|
233
|
+
echo "ℹ️ 사용자 지정 CONTAINER_NAME 사용: $CONTAINER_NAME"
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
echo "🚀 배포 환경으로 배포합니다 (브랜치: $BRANCH)"
|
|
237
|
+
|
|
238
|
+
# 설정 정보 출력
|
|
239
|
+
echo "📋 배포 설정 정보:"
|
|
240
|
+
echo " - 브랜치: $BRANCH"
|
|
241
|
+
echo " - 프로젝트: $PROJECT_NAME"
|
|
242
|
+
echo " - 컨테이너 이름: $CONTAINER_NAME"
|
|
243
|
+
echo " - 포트: $PORT"
|
|
244
|
+
echo " - Docker 이미지: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
|
|
245
|
+
|
|
246
|
+
# ============================================================
|
|
247
|
+
# Docker 이미지 풀 (Pull)
|
|
248
|
+
# ============================================================
|
|
249
|
+
echo "⬇️ Docker 이미지 풀: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
|
|
250
|
+
SUDO docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
|
|
251
|
+
|
|
252
|
+
# ============================================================
|
|
253
|
+
# 기존 컨테이너 정리
|
|
254
|
+
# ============================================================
|
|
255
|
+
echo "🧹 컨테이너 $CONTAINER_NAME 존재 여부 확인 중..."
|
|
256
|
+
|
|
257
|
+
# 동일한 이름의 컨테이너가 존재하는지 확인
|
|
258
|
+
if SUDO docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
|
|
259
|
+
echo "⚠️ 컨테이너 $CONTAINER_NAME 이(가) 존재합니다. 중지 및 삭제 중..."
|
|
260
|
+
SUDO docker rm -f $CONTAINER_NAME
|
|
261
|
+
echo "✅ 컨테이너 $CONTAINER_NAME 이(가) 삭제되었습니다."
|
|
262
|
+
else
|
|
263
|
+
echo "ℹ️ 존재하는 컨테이너 $CONTAINER_NAME 이(가) 없습니다."
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
# ============================================================
|
|
267
|
+
# 새 컨테이너 실행
|
|
268
|
+
# ============================================================
|
|
269
|
+
echo "🚀 새로운 컨테이너 $CONTAINER_NAME 실행 중..."
|
|
270
|
+
|
|
271
|
+
# Docker 컨테이너 실행
|
|
272
|
+
# - 포트 매핑: ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }} (외부:내부)
|
|
273
|
+
# - 시간대: Asia/Seoul
|
|
274
|
+
# - 볼륨 마운트: 로컬 시간 동기화 및 프로젝트 데이터
|
|
275
|
+
# ⚠️ .env 파일은 이미 Docker 이미지에 포함되어 있습니다
|
|
276
|
+
|
|
277
|
+
# 볼륨 마운트 옵션 구성
|
|
278
|
+
VOLUME_OPTS="-v /etc/localtime:/etc/localtime:ro"
|
|
279
|
+
if [ "${{ env.ENABLE_VOLUME_MOUNT }}" == "true" ]; then
|
|
280
|
+
# 디렉토리가 없으면 생성
|
|
281
|
+
if [ ! -d "${{ env.VOLUME_HOST_PATH }}" ]; then
|
|
282
|
+
echo "📁 볼륨 마운트 디렉토리 생성: ${{ env.VOLUME_HOST_PATH }}"
|
|
283
|
+
SUDO mkdir -p "${{ env.VOLUME_HOST_PATH }}"
|
|
284
|
+
fi
|
|
285
|
+
VOLUME_OPTS="$VOLUME_OPTS -v ${{ env.VOLUME_HOST_PATH }}:${{ env.VOLUME_CONTAINER_PATH }}"
|
|
286
|
+
echo "📦 볼륨 마운트 활성화: $VOLUME_OPTS"
|
|
287
|
+
else
|
|
288
|
+
echo "ℹ️ 볼륨 마운트 비활성화됨"
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
SUDO docker run -d \
|
|
292
|
+
-p ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }} \
|
|
293
|
+
--name $CONTAINER_NAME \
|
|
294
|
+
-e TZ=Asia/Seoul \
|
|
295
|
+
$VOLUME_OPTS \
|
|
296
|
+
${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
|
|
297
|
+
|
|
298
|
+
# ============================================================
|
|
299
|
+
# 컨테이너 헬스체크
|
|
300
|
+
# ============================================================
|
|
301
|
+
echo "⏳ 컨테이너 초기화 대기 중... (${{ env.HEALTHCHECK_WAIT_SECONDS }}초)"
|
|
302
|
+
sleep ${{ env.HEALTHCHECK_WAIT_SECONDS }}
|
|
303
|
+
|
|
304
|
+
# 1단계: 컨테이너 프로세스 확인
|
|
305
|
+
echo "🏥 컨테이너 헬스체크 중..."
|
|
306
|
+
if ! SUDO docker ps --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
|
|
307
|
+
echo "❌ 컨테이너 $CONTAINER_NAME 프로세스가 실행되지 않았습니다"
|
|
308
|
+
SUDO docker logs --tail 50 $CONTAINER_NAME || true
|
|
309
|
+
exit 1
|
|
310
|
+
fi
|
|
311
|
+
echo "✅ 컨테이너 프로세스 실행 확인 완료"
|
|
312
|
+
|
|
313
|
+
# 2단계: HTTP 엔드포인트 체크 (설정된 경우)
|
|
314
|
+
HEALTHCHECK_SUCCESS=false
|
|
315
|
+
if [ -n "${{ env.HEALTHCHECK_PATH }}" ]; then
|
|
316
|
+
echo "🌐 HTTP 헬스체크 시작: http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }}"
|
|
317
|
+
|
|
318
|
+
for i in $(seq 1 ${{ env.HEALTHCHECK_MAX_RETRIES }}); do
|
|
319
|
+
echo " 시도 $i/${{ env.HEALTHCHECK_MAX_RETRIES }}..."
|
|
320
|
+
|
|
321
|
+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }} || echo "000")
|
|
322
|
+
|
|
323
|
+
if [ "$HTTP_CODE" = "200" ]; then
|
|
324
|
+
echo "✅ HTTP 헬스체크 성공 (응답 코드: $HTTP_CODE)"
|
|
325
|
+
HEALTHCHECK_SUCCESS=true
|
|
326
|
+
break
|
|
327
|
+
else
|
|
328
|
+
echo "⚠️ HTTP 헬스체크 실패 (응답 코드: $HTTP_CODE)"
|
|
329
|
+
if [ $i -lt ${{ env.HEALTHCHECK_MAX_RETRIES }} ]; then
|
|
330
|
+
echo " ${{ env.HEALTHCHECK_RETRY_INTERVAL }}초 후 재시도..."
|
|
331
|
+
sleep ${{ env.HEALTHCHECK_RETRY_INTERVAL }}
|
|
332
|
+
fi
|
|
333
|
+
fi
|
|
334
|
+
done
|
|
335
|
+
|
|
336
|
+
# HTTP 체크 실패 시 fallback으로 로그 패턴 매칭
|
|
337
|
+
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
338
|
+
echo "⚠️ HTTP 헬스체크 최종 실패, 로그 패턴 매칭으로 fallback..."
|
|
339
|
+
fi
|
|
340
|
+
else
|
|
341
|
+
echo "ℹ️ HEALTHCHECK_PATH가 설정되지 않아 로그 패턴 매칭만 사용합니다"
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
# 3단계: 로그 패턴 매칭 (HTTP 실패 시 또는 HTTP 체크 비활성화 시)
|
|
345
|
+
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
346
|
+
echo "📋 로그 패턴 매칭 시작: '${{ env.HEALTHCHECK_LOG_PATTERN }}'"
|
|
347
|
+
|
|
348
|
+
if SUDO docker logs $CONTAINER_NAME 2>&1 | grep -q "${{ env.HEALTHCHECK_LOG_PATTERN }}"; then
|
|
349
|
+
echo "✅ 로그 패턴 매칭 성공: 애플리케이션이 정상적으로 시작되었습니다"
|
|
350
|
+
HEALTHCHECK_SUCCESS=true
|
|
351
|
+
else
|
|
352
|
+
echo "❌ 로그 패턴 매칭 실패: 애플리케이션 시작 로그를 찾을 수 없습니다"
|
|
353
|
+
echo ""
|
|
354
|
+
echo "📋 디버깅 정보:"
|
|
355
|
+
echo " - 검색 패턴: ${{ env.HEALTHCHECK_LOG_PATTERN }}"
|
|
356
|
+
echo " - 컨테이너: $CONTAINER_NAME"
|
|
357
|
+
echo " - 이미지: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
|
|
358
|
+
echo " - 포트: ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }}"
|
|
359
|
+
echo " - 볼륨: $VOLUME_OPTS"
|
|
360
|
+
echo ""
|
|
361
|
+
echo "📋 컨테이너 로그 (최근 50줄):"
|
|
362
|
+
SUDO docker logs --tail 50 $CONTAINER_NAME || true
|
|
363
|
+
exit 1
|
|
364
|
+
fi
|
|
365
|
+
fi
|
|
366
|
+
|
|
367
|
+
# 최종 성공 메시지
|
|
368
|
+
if [ "$HEALTHCHECK_SUCCESS" = "true" ]; then
|
|
369
|
+
echo "✅ 헬스체크 완료: 컨테이너 $CONTAINER_NAME 이(가) 정상적으로 실행 중입니다"
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
# ============================================================
|
|
373
|
+
# 배포 완료 확인
|
|
374
|
+
# ============================================================
|
|
375
|
+
echo "✅ 배포가 성공적으로 완료되었습니다!"
|
|
376
|
+
echo ""
|
|
377
|
+
echo "📋 배포 결과 요약:"
|
|
378
|
+
echo " 🎯 프로젝트: $PROJECT_NAME"
|
|
379
|
+
echo " 🌿 브랜치: $BRANCH"
|
|
380
|
+
echo " 🐳 컨테이너: $CONTAINER_NAME"
|
|
381
|
+
echo " 🌐 포트: $PORT"
|
|
382
|
+
echo " ⏰ 배포 시간: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
383
|
+
echo ""
|
|
384
|
+
echo "🔗 접속 URL: http://${{ secrets.SERVER_HOST }}:${PORT}"
|
|
@@ -24,6 +24,9 @@ permissions:
|
|
|
24
24
|
issues: write
|
|
25
25
|
checks: write
|
|
26
26
|
|
|
27
|
+
env:
|
|
28
|
+
JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:@jdk
|
|
29
|
+
|
|
27
30
|
jobs:
|
|
28
31
|
build-check:
|
|
29
32
|
name: Build Verification
|
|
@@ -35,10 +38,10 @@ jobs:
|
|
|
35
38
|
with:
|
|
36
39
|
fetch-depth: 0
|
|
37
40
|
|
|
38
|
-
- name: Setup JDK
|
|
41
|
+
- name: Setup JDK
|
|
39
42
|
uses: actions/setup-java@v5
|
|
40
43
|
with:
|
|
41
|
-
java-version:
|
|
44
|
+
java-version: ${{ env.JAVA_VERSION }}
|
|
42
45
|
distribution: 'temurin'
|
|
43
46
|
|
|
44
47
|
- name: Validate Gradle Wrapper
|
|
@@ -16,16 +16,26 @@ on:
|
|
|
16
16
|
tags:
|
|
17
17
|
- 'v*.*.*'
|
|
18
18
|
|
|
19
|
+
concurrency:
|
|
20
|
+
group: nexus-publish-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: false
|
|
22
|
+
|
|
23
|
+
permissions:
|
|
24
|
+
contents: read
|
|
25
|
+
|
|
26
|
+
env:
|
|
27
|
+
JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:@jdk
|
|
28
|
+
|
|
19
29
|
jobs:
|
|
20
30
|
publish:
|
|
21
31
|
runs-on: ubuntu-latest
|
|
22
32
|
steps:
|
|
23
33
|
- uses: actions/checkout@v7
|
|
24
34
|
|
|
25
|
-
- name: Set up JDK
|
|
35
|
+
- name: Set up JDK
|
|
26
36
|
uses: actions/setup-java@v5
|
|
27
37
|
with:
|
|
28
|
-
java-version:
|
|
38
|
+
java-version: ${{ env.JAVA_VERSION }}
|
|
29
39
|
distribution: 'temurin'
|
|
30
40
|
|
|
31
41
|
- name: Setup Gradle
|
package/src/cli/help.js
CHANGED
|
@@ -11,7 +11,7 @@ export const HELP_TEXT = `project-auto-wizard — One command DevOps: GitHub-nat
|
|
|
11
11
|
status = 설치 상태·드리프트 확인(읽기 전용). doctor = 환경 진단(읽기 전용)
|
|
12
12
|
-t, --type CSV 프로젝트 타입 csv (예: spring,react,python)
|
|
13
13
|
지원: spring flutter next react react-native
|
|
14
|
-
react-native-expo node python basic
|
|
14
|
+
react-native-expo node python basic go
|
|
15
15
|
--project-version V 통합 대상의 초기 버전 (예: 1.0.0). 미지정 시 자동 감지
|
|
16
16
|
--paths "t=p,..." 타입별 프로젝트 경로 (모노레포). 예: flutter=app,react=client
|
|
17
17
|
--main-branch B 릴리스 브랜치 (기본: 감지된 default branch)
|
package/src/commands/doctor.js
CHANGED
|
@@ -116,18 +116,15 @@ export function runDoctor(cwd = process.cwd(), { exec = defaultExec } = {}) {
|
|
|
116
116
|
add(hasPat
|
|
117
117
|
? { name: "WORKFLOW_PAT secret", label: "WORKFLOW_PAT", purpose: "자동 태그·Release 발행", status: "OK", value: "등록됨" }
|
|
118
118
|
: {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
"
|
|
127
|
-
"레포 Settings → Secrets and variables → Actions",
|
|
128
|
-
"New repository secret · 이름은 WORKFLOW_PAT",
|
|
119
|
+
// 조치가 필요 없으므로 WARN이 아니라 INFO다 — 위 Workflow permissions 항목과 동일 이유:
|
|
120
|
+
// 폴백(wait-for-merge-and-trigger-release / Trigger NPM-PUBLISH)이 GITHUB_TOKEN만으로
|
|
121
|
+
// 파이프라인을 끝까지 이어가므로, 없는 장애를 경고로 띄우지 않는다(이슈 #105).
|
|
122
|
+
name: "WORKFLOW_PAT secret", label: "WORKFLOW_PAT", purpose: "자동 태그·Release 발행", status: "INFO",
|
|
123
|
+
note: [
|
|
124
|
+
"secret이 없어도 폴백이 자동으로 이어받아 태그·Release까지 진행됩니다 — 실제 병합 후 최대 ~20초 정도 더 걸릴 뿐입니다.",
|
|
125
|
+
"속도를 더 원한다면 PAT을 등록할 수 있습니다 — 반드시 개인 계정이 아닌 조직 bot/machine 계정으로 발급하세요 (scopes: repo, workflow).",
|
|
126
|
+
"등록: 레포 Settings → Secrets and variables → Actions → New repository secret · 이름은 WORKFLOW_PAT",
|
|
129
127
|
],
|
|
130
|
-
doc: DOC.postInstall,
|
|
131
128
|
});
|
|
132
129
|
|
|
133
130
|
const mergeSettings = exec("gh", ["api", `repos/${owner}/${repo}`, "--jq", ".allow_merge_commit"]);
|
package/src/context.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 마법사 전역 상태를 하나의 객체로 명시화 (bash 전역 변수군 대체)
|
|
2
2
|
export const VALID_TYPES = [
|
|
3
3
|
"spring", "flutter", "next", "react",
|
|
4
|
-
"react-native", "react-native-expo", "node", "python", "basic",
|
|
4
|
+
"react-native", "react-native-expo", "node", "python", "basic", "go",
|
|
5
5
|
];
|
|
6
6
|
|
|
7
7
|
// --mode 화이트리스트 (issue #19) — 알 수 없는 값은 부수효과(브랜치 조회 등) 이전에 즉시 거부해야 한다.
|
package/src/core/detect.js
CHANGED
|
@@ -24,6 +24,7 @@ export function detectTypesFromMarkers({ has, read }) {
|
|
|
24
24
|
if (has("pubspec.yaml")) types.push("flutter");
|
|
25
25
|
if (has("build.gradle") || has("build.gradle.kts") || has("pom.xml")) types.push("spring");
|
|
26
26
|
if (has("pyproject.toml") || has("setup.py") || has("requirements.txt")) types.push("python");
|
|
27
|
+
if (has("go.mod")) types.push("go");
|
|
27
28
|
if (has("package.json")) {
|
|
28
29
|
const cls = classifyPackageText(read ? read("package.json") : "");
|
|
29
30
|
if (cls === "node") { if (types.length === 0) types.push("node"); }
|
|
@@ -73,7 +74,7 @@ export function versionFromPom(content) {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
export function markerForType(type) {
|
|
76
|
-
return { flutter: "pubspec.yaml", "react-native-expo": "app.json", python: "pyproject.toml", spring: "build.gradle" }[type] || "package.json";
|
|
77
|
+
return { flutter: "pubspec.yaml", "react-native-expo": "app.json", python: "pyproject.toml", spring: "build.gradle", go: "go.mod" }[type] || "package.json";
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
export function extraMarkers(type) {
|
|
@@ -18,7 +18,7 @@ const isCancel = (v) => typeof v === "symbol";
|
|
|
18
18
|
// 타입의 대표 마커 파일명 (.sh marker_for_type L1220~1229 등가).
|
|
19
19
|
// detect.js는 미지 타입에 package.json을 기본 반환하지만 .sh는 빈 문자열 — 등가를 위해 래핑.
|
|
20
20
|
const KNOWN_MARKER_TYPES = new Set([
|
|
21
|
-
"flutter", "react", "next", "node", "react-native", "react-native-expo", "python", "spring",
|
|
21
|
+
"flutter", "react", "next", "node", "react-native", "react-native-expo", "python", "spring", "go",
|
|
22
22
|
]);
|
|
23
23
|
export function markerForType(type) {
|
|
24
24
|
return KNOWN_MARKER_TYPES.has(type) ? baseMarkerForType(type) : "";
|
|
@@ -79,6 +79,7 @@ export function findTypePathCandidates(root, type) {
|
|
|
79
79
|
"react-native-expo": ["app.json"],
|
|
80
80
|
python: ["pyproject.toml", "setup.py", "requirements.txt"],
|
|
81
81
|
spring: ["build.gradle", "build.gradle.kts", "pom.xml"],
|
|
82
|
+
go: ["go.mod"],
|
|
82
83
|
};
|
|
83
84
|
const names = namesByType[type];
|
|
84
85
|
if (!names) return [];
|
package/src/ui/prompts.js
CHANGED
|
@@ -48,7 +48,7 @@ export async function editMenu({ showOptional = false } = {}) {
|
|
|
48
48
|
return engine.select({ message: "어떤 항목을 수정할까요?", options });
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const ALL_TYPES = ["spring", "flutter", "next", "react", "react-native", "react-native-expo", "node", "python", "basic"];
|
|
51
|
+
const ALL_TYPES = ["spring", "flutter", "next", "react", "react-native", "react-native-expo", "node", "python", "basic", "go"];
|
|
52
52
|
|
|
53
53
|
// 타입 멀티선택.
|
|
54
54
|
export async function selectTypes(current = []) {
|
package/src/ui/summary.js
CHANGED
|
@@ -156,10 +156,11 @@ export function printSummary(ctx) {
|
|
|
156
156
|
err("");
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
err(` ${num()} 릴리스 automerge용 PAT (선택 — 없으면 GITHUB_TOKEN
|
|
159
|
+
err(` ${num()} 릴리스 automerge용 PAT (선택 — 없으면 GITHUB_TOKEN 폴백으로 자동 진행)`);
|
|
160
160
|
err(" → Repository Settings > Secrets > Actions");
|
|
161
161
|
err(" → Secret Name: WORKFLOW_PAT (Scopes: repo, workflow)");
|
|
162
|
-
err(" →
|
|
162
|
+
err(" → 등록 시 개인 계정이 아닌 조직 bot/machine 계정으로 발급하세요");
|
|
163
|
+
err(" → 없어도 자동 복구되며, 있으면 병합~Release 반영이 조금 더 빠릅니다");
|
|
163
164
|
err("");
|
|
164
165
|
err(` ${num()} GitHub Actions 권한 확인`);
|
|
165
166
|
err(" → Settings > Actions > Workflow permissions: Read and write");
|