project-auto-wizard 0.12.2 → 0.13.1

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 (91) hide show
  1. package/README.ja.md +165 -0
  2. package/README.ko.md +165 -0
  3. package/README.md +107 -286
  4. package/README.zh-CN.md +165 -0
  5. package/bin/project-auto-wizard.js +1 -2
  6. package/package.json +9 -3
  7. package/payload/config/breaking-changes.json +24 -0
  8. package/payload/config/wizard-prompts.yml +45 -0
  9. package/payload/scripts/changelog_manager.py +141 -46
  10. package/payload/scripts/issue_helper.py +34 -5
  11. package/payload/scripts/version_manager.py +482 -107
  12. package/payload/version.yml.template +5 -2
  13. package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +39 -9
  14. package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +165 -38
  15. package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +14 -5
  16. package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +220 -6
  17. package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +89 -3
  18. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +78 -16
  19. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +72 -16
  20. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +62 -20
  21. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +51 -13
  22. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +75 -11
  23. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +52 -3
  24. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +60 -11
  25. package/payload/workflows/go/PROJECT-GO-CI.yaml +10 -1
  26. package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +148 -28
  27. package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +45 -4
  28. package/payload/workflows/next/PROJECT-NEXT-CI.yaml +25 -3
  29. package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +83 -9
  30. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +70 -6
  31. package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +148 -28
  32. package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +45 -4
  33. package/payload/workflows/react/PROJECT-REACT-CI.yaml +25 -3
  34. package/payload/workflows/react/PROJECT-REACT-CICD.yaml +84 -10
  35. package/payload/workflows/spring/PROJECT-SPRING-CI.yml +13 -3
  36. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +47 -5
  37. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +47 -5
  38. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +153 -30
  39. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +47 -5
  40. package/src/cli/args.js +33 -24
  41. package/src/cli/help.js +16 -4
  42. package/src/commands/doctor.js +26 -60
  43. package/src/commands/dry-run.js +59 -5
  44. package/src/commands/full.js +135 -55
  45. package/src/commands/install-settings.js +51 -0
  46. package/src/commands/interactive-flutter.js +1 -1
  47. package/src/commands/interactive.js +135 -69
  48. package/src/commands/purge.js +50 -21
  49. package/src/commands/status.js +18 -12
  50. package/src/commands/uninstall.js +22 -22
  51. package/src/context.js +5 -5
  52. package/src/core/baseline.js +20 -5
  53. package/src/core/branches.js +19 -1
  54. package/src/core/branding.js +1 -1
  55. package/src/core/breaking-check.js +14 -19
  56. package/src/core/breaking.js +7 -3
  57. package/src/core/copy/app-files.js +28 -0
  58. package/src/core/copy/gitignore.js +20 -12
  59. package/src/core/copy/readme.js +24 -5
  60. package/src/core/copy/simple.js +52 -13
  61. package/src/core/copy/workflows.js +133 -111
  62. package/src/core/deploy-style.js +83 -11
  63. package/src/core/detect-fs.js +127 -24
  64. package/src/core/detect.js +177 -47
  65. package/src/core/errors.js +3 -0
  66. package/src/core/flutter-doctor.js +49 -0
  67. package/src/core/flutter-hooks.js +29 -0
  68. package/src/core/flutter-options.js +4 -3
  69. package/src/core/fsutil.js +23 -6
  70. package/src/core/logger.js +55 -19
  71. package/src/core/paths-resolve.js +50 -47
  72. package/src/core/paths.js +26 -1
  73. package/src/core/release-options.js +23 -0
  74. package/src/core/removal-exec.js +67 -0
  75. package/src/core/removal-plan.js +75 -19
  76. package/src/core/types.js +136 -0
  77. package/src/core/verify.js +85 -19
  78. package/src/core/version-yml.js +42 -8
  79. package/src/core/wizard-env.js +21 -14
  80. package/src/core/wizard-labels.js +34 -25
  81. package/src/index.js +128 -60
  82. package/src/ui/ansi.js +3 -1
  83. package/src/ui/banner.js +12 -8
  84. package/src/ui/env-plan.js +68 -44
  85. package/src/ui/prompts.js +22 -12
  86. package/src/ui/readline-engine.js +109 -22
  87. package/src/ui/status-cards.js +10 -5
  88. package/src/ui/summary.js +71 -33
  89. package/payload/scripts/__pycache__/changelog_manager.cpython-314.pyc +0 -0
  90. package/payload/scripts/__pycache__/issue_helper.cpython-314.pyc +0 -0
  91. package/payload/scripts/__pycache__/version_manager.cpython-314.pyc +0 -0
@@ -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-REACT-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: "front-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: React/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: ./Dockerfile
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
- port: 2022
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
  # GitHub에서 전달받은 브랜치명
138
212
  BRANCH=${{ github.ref_name }}
139
213
 
@@ -172,15 +246,15 @@ jobs:
172
246
 
173
247
  # Docker 이미지 풀 (Pull)
174
248
  echo "⬇️ Docker 이미지 풀: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}-${{ env.DOCKER_IMAGE_PREFIX }}:${BRANCH}"
175
- echo $PW | sudo -S docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}-${{ env.DOCKER_IMAGE_PREFIX }}:${BRANCH}
249
+ SUDO docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}-${{ env.DOCKER_IMAGE_PREFIX }}:${BRANCH}
176
250
 
177
251
  # 기존 컨테이너 정리
178
252
  echo "🧹 컨테이너 $CONTAINER_NAME 존재 여부 확인 중..."
179
253
 
180
254
  # 동일한 이름의 컨테이너가 존재하는지 확인
181
- if sudo docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
255
+ if SUDO docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
182
256
  echo "⚠️ 컨테이너 $CONTAINER_NAME 이(가) 존재합니다. 중지 및 삭제 중..."
183
- echo $PW | sudo -S docker rm -f $CONTAINER_NAME
257
+ SUDO docker rm -f $CONTAINER_NAME
184
258
  echo "✅ 컨테이너 $CONTAINER_NAME 이(가) 삭제되었습니다."
185
259
  else
186
260
  echo "ℹ️ 존재하는 컨테이너 $CONTAINER_NAME 이(가) 없습니다."
@@ -189,7 +263,7 @@ jobs:
189
263
  # 새 컨테이너 실행
190
264
  echo "🚀 새로운 컨테이너 $CONTAINER_NAME 실행 중..."
191
265
 
192
- echo $PW | sudo -S docker run -d \
266
+ SUDO docker run -d \
193
267
  -p ${PORT}:3000 \
194
268
  --name $CONTAINER_NAME \
195
269
  -e TZ=Asia/Seoul \
@@ -213,7 +287,7 @@ jobs:
213
287
  # 현재 설정: {{MAIN_BRANCH}} 브랜치에 push할 때 자동 배포
214
288
  #
215
289
  # 브랜치별 포트:
216
- # - main: 3000 (PROJECT_DEPLOY_PORT secret으로 변경 가능)
290
+ # - {{MAIN_BRANCH}}: 3000 (PROJECT_DEPLOY_PORT secret으로 변경 가능)
217
291
  #
218
292
  # 필요한 Secrets:
219
293
  # - REACT_ENV_FILE, DOCKERHUB_*, SERVER_* (필수)
@@ -41,6 +41,9 @@ jobs:
41
41
  if: ${{ github.event_name != 'workflow_dispatch' }}
42
42
  uses: dorny/paths-filter@v4
43
43
  with:
44
+ # push는 이번 push 범위(직전 커밋 before 대비)만 본다. 비워 두면 기본 브랜치 대비 누적 diff가 된다.
45
+ # 새 브랜치 첫 push(before가 0)와 PR은 빈 값 → 기본 동작(기본 브랜치·PR base 대비)
46
+ base: ${{ github.event_name == 'push' && github.event.before != '0000000000000000000000000000000000000000' && github.event.before || '' }}
44
47
  filters: |
45
48
  project:
46
49
  - '${{ env.PROJECT_PATH == '.' && '**' || format('{0}/**', env.PROJECT_PATH) }}'
@@ -50,6 +53,10 @@ jobs:
50
53
  runs-on: ubuntu-latest
51
54
  needs: changes
52
55
  if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.project == 'true' }}
56
+ # 모노레포(--paths)면 그 하위 폴더에서 빌드한다
57
+ defaults:
58
+ run:
59
+ working-directory: ${{ env.PROJECT_PATH }}
53
60
 
54
61
  steps:
55
62
  - name: Checkout Repository
@@ -80,7 +87,8 @@ jobs:
80
87
  run: |
81
88
  set +e
82
89
  ./gradlew compileJava compileTestJava 2>&1 | tee compile_output.txt
83
- echo "compile_exit_code=$?" >> $GITHUB_OUTPUT
90
+ # $?는 tee의 종료 코드라 gradle 결과는 PIPESTATUS로 읽는다
91
+ echo "compile_exit_code=${PIPESTATUS[0]}" >> $GITHUB_OUTPUT
84
92
  set -e
85
93
  continue-on-error: true
86
94
 
@@ -89,7 +97,8 @@ jobs:
89
97
  run: |
90
98
  set +e
91
99
  ./gradlew test 2>&1 | tee test_output.txt
92
- echo "test_exit_code=$?" >> $GITHUB_OUTPUT
100
+ # $?는 tee의 종료 코드라 gradle 결과는 PIPESTATUS로 읽는다
101
+ echo "test_exit_code=${PIPESTATUS[0]}" >> $GITHUB_OUTPUT
93
102
  set -e
94
103
  continue-on-error: true
95
104
 
@@ -98,7 +107,8 @@ jobs:
98
107
  run: |
99
108
  set +e
100
109
  ./gradlew build -x test 2>&1 | tee build_output.txt
101
- echo "build_exit_code=$?" >> $GITHUB_OUTPUT
110
+ # $?는 tee의 종료 코드라 gradle 결과는 PIPESTATUS로 읽는다
111
+ echo "build_exit_code=${PIPESTATUS[0]}" >> $GITHUB_OUTPUT
102
112
  set -e
103
113
  continue-on-error: true
104
114
 
@@ -70,6 +70,10 @@ on:
70
70
  env:
71
71
  # 프로젝트 설정 - 실제 프로젝트명으로 변경하세요
72
72
  PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
73
+ # 모노레포(--paths)에서 이 프로젝트가 있는 하위 경로. 빌드·Docker context의 기준 ('.'이면 레포 루트)
74
+ PROJECT_PATH: "." # @wizard auto:project-path
75
+ # 모노레포(PROJECT_PATH가 '.'이 아님)에서는 이미지·컨테이너·도메인 이름에 '-spring'를 붙여
76
+ # 같은 레포의 다른 타입 서비스와 이름이 겹치지 않게 한다 (예: my-app-spring)
73
77
  SERVICE_DOMAIN: "__SERVICE_DOMAIN__" # @wizard ask:example.com
74
78
 
75
79
  # Spring Boot 설정
@@ -79,7 +83,7 @@ env:
79
83
 
80
84
  # 경로 설정
81
85
  APPLICATION_YML_DIR: "__APPLICATION_YML_DIR__" # @wizard auto:spring-app-yml-dir
82
- DOCKERFILE_PATH: "./Dockerfile"
86
+ DOCKERFILE_PATH: "./Dockerfile" # PROJECT_PATH 기준 상대 경로
83
87
 
84
88
  # 포트 및 컨테이너 설정
85
89
  # BLUE/GREEN은 서버에서 번갈아 뜨는 두 컨테이너의 호스트 포트다 — 다른 서비스와 겹치면 배포가 실패한다.
@@ -126,6 +130,42 @@ jobs:
126
130
  - name: 코드 체크아웃
127
131
  uses: actions/checkout@v7
128
132
 
133
+ # 필수 Secret·Dockerfile이 없으면 docker 로그인 같은 서드파티 오류 대신 빠진 항목을 알려 주고 멈춘다
134
+ - name: 배포 사전 점검 (Secret·Dockerfile)
135
+ working-directory: ${{ env.PROJECT_PATH }}
136
+ env:
137
+ DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
138
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
139
+ SERVER_HOST: ${{ secrets.SERVER_HOST }}
140
+ SERVER_USER: ${{ secrets.SERVER_USER }}
141
+ SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
142
+ SSH_KEY: ${{ secrets.SSH_KEY }}
143
+ DOCKERFILE: ${{ env.DOCKERFILE_PATH }}
144
+ run: |
145
+ MISSING=""
146
+ for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
147
+ [ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
148
+ done
149
+ # SSH_AUTH_METHOD=key면 SSH_KEY, 아니면 SERVER_PASSWORD가 필요하다
150
+ if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
151
+ [ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
152
+ else
153
+ [ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
154
+ fi
155
+ FAILED=false
156
+ if [ -n "$MISSING" ]; then
157
+ echo "::error title=필수 Secret 누락::등록되지 않은 GitHub Secret:$MISSING — Settings → Secrets and variables → Actions에서 등록하세요"
158
+ FAILED=true
159
+ fi
160
+ if [ ! -f "$DOCKERFILE" ]; then
161
+ echo "::error title=Dockerfile 없음::${PROJECT_PATH}/${DOCKERFILE#./} 파일이 없습니다. 이 워크플로우는 Docker 이미지로 배포하므로 Dockerfile이 필요합니다 (서버 배포를 하지 않는다면 이 워크플로우 파일을 삭제하세요)"
162
+ FAILED=true
163
+ fi
164
+ if [ "$FAILED" = "true" ]; then
165
+ exit 1
166
+ fi
167
+ echo "✅ 사전 점검 통과: 필수 Secret과 Dockerfile이 모두 있습니다"
168
+
129
169
  - name: Java 설정
130
170
  uses: actions/setup-java@v5
131
171
  with:
@@ -134,6 +174,7 @@ jobs:
134
174
  cache: 'gradle'
135
175
 
136
176
  - name: Gradle Wrapper 실행권한 부여
177
+ working-directory: ${{ env.PROJECT_PATH }}
137
178
  run: chmod +x gradlew
138
179
 
139
180
  - name: application-prod.yml 생성
@@ -145,6 +186,7 @@ jobs:
145
186
  echo "✅ application-prod.yml 파일 생성 완료 (${{ env.APPLICATION_YML_DIR }})"
146
187
 
147
188
  - name: Build with Gradle
189
+ working-directory: ${{ env.PROJECT_PATH }}
148
190
  run: ./gradlew clean build -x test ${{ env.GRADLE_OPTS }}
149
191
 
150
192
  - name: Docker 빌드환경 설정
@@ -167,10 +209,10 @@ jobs:
167
209
  - name: Docker 이미지 빌드 및 푸시
168
210
  uses: docker/build-push-action@v5
169
211
  with:
170
- context: .
171
- file: ${{ env.DOCKERFILE_PATH }}
212
+ context: ${{ env.PROJECT_PATH }}
213
+ file: ${{ env.PROJECT_PATH }}/${{ env.DOCKERFILE_PATH }}
172
214
  push: true
173
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.ref_name }}
215
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_PATH != '.' && format('{0}-spring', env.PROJECT_NAME) || env.PROJECT_NAME }}:${{ github.ref_name }}
174
216
  cache-from: type=local,src=/tmp/.buildx-cache
175
217
  cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
176
218
 
@@ -189,7 +231,7 @@ jobs:
189
231
  - name: Deploy
190
232
  uses: appleboy/ssh-action@v1.0.3
191
233
  env:
192
- PROJECT_NAME: ${{ env.PROJECT_NAME }}
234
+ PROJECT_NAME: ${{ env.PROJECT_PATH != '.' && format('{0}-spring', env.PROJECT_NAME) || env.PROJECT_NAME }}
193
235
  SERVICE_DOMAIN: ${{ env.SERVICE_DOMAIN }}
194
236
  SPRING_PROFILE: ${{ env.SPRING_PROFILE }}
195
237
  CONTAINER_INTERNAL_PORT: ${{ env.CONTAINER_INTERNAL_PORT }}
@@ -66,6 +66,10 @@ on:
66
66
  env:
67
67
  # 프로젝트 설정 - 실제 프로젝트명으로 변경하세요
68
68
  PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
69
+ # 모노레포(--paths)에서 이 프로젝트가 있는 하위 경로. 빌드·Docker context의 기준 ('.'이면 레포 루트)
70
+ PROJECT_PATH: "." # @wizard auto:project-path
71
+ # 모노레포(PROJECT_PATH가 '.'이 아님)에서는 이미지·컨테이너·도메인 이름에 '-spring'를 붙여
72
+ # 같은 레포의 다른 타입 서비스와 이름이 겹치지 않게 한다 (예: my-app-spring)
69
73
  SERVICE_DOMAIN: "__SERVICE_DOMAIN__" # @wizard ask:example.com
70
74
 
71
75
  # Spring Boot 설정
@@ -75,7 +79,7 @@ env:
75
79
 
76
80
  # 경로 설정
77
81
  APPLICATION_YML_DIR: "__APPLICATION_YML_DIR__" # @wizard auto:spring-app-yml-dir
78
- DOCKERFILE_PATH: "./Dockerfile"
82
+ DOCKERFILE_PATH: "./Dockerfile" # PROJECT_PATH 기준 상대 경로
79
83
 
80
84
  # 포트 및 컨테이너 설정
81
85
  CONTAINER_INTERNAL_PORT: "8080"
@@ -118,6 +122,42 @@ jobs:
118
122
  - name: 코드 체크아웃
119
123
  uses: actions/checkout@v7
120
124
 
125
+ # 필수 Secret·Dockerfile이 없으면 docker 로그인 같은 서드파티 오류 대신 빠진 항목을 알려 주고 멈춘다
126
+ - name: 배포 사전 점검 (Secret·Dockerfile)
127
+ working-directory: ${{ env.PROJECT_PATH }}
128
+ env:
129
+ DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
130
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
131
+ SERVER_HOST: ${{ secrets.SERVER_HOST }}
132
+ SERVER_USER: ${{ secrets.SERVER_USER }}
133
+ SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
134
+ SSH_KEY: ${{ secrets.SSH_KEY }}
135
+ DOCKERFILE: ${{ env.DOCKERFILE_PATH }}
136
+ run: |
137
+ MISSING=""
138
+ for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
139
+ [ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
140
+ done
141
+ # SSH_AUTH_METHOD=key면 SSH_KEY, 아니면 SERVER_PASSWORD가 필요하다
142
+ if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
143
+ [ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
144
+ else
145
+ [ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
146
+ fi
147
+ FAILED=false
148
+ if [ -n "$MISSING" ]; then
149
+ echo "::error title=필수 Secret 누락::등록되지 않은 GitHub Secret:$MISSING — Settings → Secrets and variables → Actions에서 등록하세요"
150
+ FAILED=true
151
+ fi
152
+ if [ ! -f "$DOCKERFILE" ]; then
153
+ echo "::error title=Dockerfile 없음::${PROJECT_PATH}/${DOCKERFILE#./} 파일이 없습니다. 이 워크플로우는 Docker 이미지로 배포하므로 Dockerfile이 필요합니다 (서버 배포를 하지 않는다면 이 워크플로우 파일을 삭제하세요)"
154
+ FAILED=true
155
+ fi
156
+ if [ "$FAILED" = "true" ]; then
157
+ exit 1
158
+ fi
159
+ echo "✅ 사전 점검 통과: 필수 Secret과 Dockerfile이 모두 있습니다"
160
+
121
161
  - name: Java 설정
122
162
  uses: actions/setup-java@v5
123
163
  with:
@@ -126,6 +166,7 @@ jobs:
126
166
  cache: 'gradle'
127
167
 
128
168
  - name: Gradle Wrapper 실행권한 부여
169
+ working-directory: ${{ env.PROJECT_PATH }}
129
170
  run: chmod +x gradlew
130
171
 
131
172
  - name: application-prod.yml 생성
@@ -137,6 +178,7 @@ jobs:
137
178
  echo "✅ application-prod.yml 파일 생성 완료 (${{ env.APPLICATION_YML_DIR }})"
138
179
 
139
180
  - name: Build with Gradle
181
+ working-directory: ${{ env.PROJECT_PATH }}
140
182
  run: ./gradlew clean build -x test ${{ env.GRADLE_OPTS }}
141
183
 
142
184
  - name: Docker 빌드환경 설정
@@ -159,10 +201,10 @@ jobs:
159
201
  - name: Docker 이미지 빌드 및 푸시
160
202
  uses: docker/build-push-action@v5
161
203
  with:
162
- context: .
163
- file: ${{ env.DOCKERFILE_PATH }}
204
+ context: ${{ env.PROJECT_PATH }}
205
+ file: ${{ env.PROJECT_PATH }}/${{ env.DOCKERFILE_PATH }}
164
206
  push: true
165
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.ref_name }}
207
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_PATH != '.' && format('{0}-spring', env.PROJECT_NAME) || env.PROJECT_NAME }}:${{ github.ref_name }}
166
208
  cache-from: type=local,src=/tmp/.buildx-cache
167
209
  cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
168
210
 
@@ -181,7 +223,7 @@ jobs:
181
223
  - name: Deploy
182
224
  uses: appleboy/ssh-action@v1.0.3
183
225
  env:
184
- PROJECT_NAME: ${{ env.PROJECT_NAME }}
226
+ PROJECT_NAME: ${{ env.PROJECT_PATH != '.' && format('{0}-spring', env.PROJECT_NAME) || env.PROJECT_NAME }}
185
227
  SERVICE_DOMAIN: ${{ env.SERVICE_DOMAIN }}
186
228
  CONTAINER_INTERNAL_PORT: ${{ env.CONTAINER_INTERNAL_PORT }}
187
229
  SPRING_PROFILE: ${{ env.SPRING_PROFILE }}