project-auto-wizard 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) 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 +7 -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 +397 -97
  12. package/payload/version.yml.template +4 -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 +17 -10
  43. package/src/commands/dry-run.js +54 -1
  44. package/src/commands/full.js +127 -50
  45. package/src/commands/interactive-flutter.js +1 -1
  46. package/src/commands/interactive.js +124 -56
  47. package/src/commands/purge.js +59 -17
  48. package/src/commands/status.js +16 -1
  49. package/src/commands/uninstall.js +58 -13
  50. package/src/context.js +5 -5
  51. package/src/core/baseline.js +20 -5
  52. package/src/core/branches.js +19 -1
  53. package/src/core/branding.js +1 -1
  54. package/src/core/breaking-check.js +14 -19
  55. package/src/core/breaking.js +7 -3
  56. package/src/core/copy/gitignore.js +20 -12
  57. package/src/core/copy/readme.js +24 -5
  58. package/src/core/copy/simple.js +52 -13
  59. package/src/core/copy/workflows.js +90 -47
  60. package/src/core/deploy-style.js +83 -11
  61. package/src/core/detect-fs.js +127 -24
  62. package/src/core/detect.js +131 -47
  63. package/src/core/errors.js +3 -0
  64. package/src/core/flutter-options.js +4 -3
  65. package/src/core/fsutil.js +23 -6
  66. package/src/core/logger.js +55 -19
  67. package/src/core/paths-resolve.js +50 -47
  68. package/src/core/paths.js +26 -1
  69. package/src/core/release-options.js +23 -0
  70. package/src/core/removal-plan.js +75 -19
  71. package/src/core/types.js +113 -0
  72. package/src/core/verify.js +85 -19
  73. package/src/core/version-yml.js +42 -8
  74. package/src/core/wizard-env.js +21 -14
  75. package/src/core/wizard-labels.js +34 -25
  76. package/src/index.js +122 -49
  77. package/src/ui/ansi.js +3 -1
  78. package/src/ui/banner.js +12 -8
  79. package/src/ui/env-plan.js +68 -44
  80. package/src/ui/prompts.js +22 -12
  81. package/src/ui/readline-engine.js +109 -22
  82. package/src/ui/status-cards.js +10 -5
  83. package/src/ui/summary.js +71 -33
  84. package/payload/scripts/__pycache__/changelog_manager.cpython-314.pyc +0 -0
  85. package/payload/scripts/__pycache__/issue_helper.cpython-314.pyc +0 -0
  86. package/payload/scripts/__pycache__/version_manager.cpython-314.pyc +0 -0
@@ -48,9 +48,17 @@ on:
48
48
  repository_dispatch:
49
49
  types: [build-ios-app]
50
50
 
51
+ # 레포 기본 권한이 read여도 빌드 진행 댓글을 달 수 있게 필요한 권한을 직접 선언한다
52
+ permissions:
53
+ contents: read
54
+ issues: write
55
+ pull-requests: write
56
+
51
57
  # 프로젝트별 설정 (아래 값들을 수정하세요)
52
58
  env:
53
- FLUTTER_VERSION: "3.35.5"
59
+ # Flutter SDK 버전 — 비워 두면 stable 채널 최신을 쓴다(최신 flutter create 프로젝트의 SDK 제약과 맞음).
60
+ # 특정 버전에 고정하려면 "3.38.9"처럼 적는다. pubspec.yaml의 environment.sdk 범위를 만족해야 한다.
61
+ FLUTTER_VERSION: ""
54
62
  XCODE_VERSION: "26.0"
55
63
  PROJECT_TYPE: "flutter"
56
64
  # Flutter 루트 경로(레포 루트 기준). 단일레포면 ".", 모노레포면 "app" 등 — 설치 시 자동 설정
@@ -205,6 +213,41 @@ jobs:
205
213
  - name: Pull latest changes
206
214
  run: git pull origin ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch_name || github.ref_name }}
207
215
 
216
+ # 인증서 import·빌드 전에 설정 누락을 한 번에 알려 준다 — 없으면 macOS 러너에서 한참 빌드한 뒤에야 엉뚱한 단계에서 실패한다
217
+ - name: 배포 설정 사전 검증
218
+ env:
219
+ APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
220
+ APPLE_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}
221
+ IOS_PROVISIONING_PROFILE_NAME: ${{ secrets.IOS_PROVISIONING_PROFILE_NAME }}
222
+ APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
223
+ APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
224
+ APP_STORE_CONNECT_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}
225
+ run: |
226
+ FAILED=0
227
+ MISSING=""
228
+ for name in APPLE_CERTIFICATE_BASE64 APPLE_PROVISIONING_PROFILE_BASE64 IOS_PROVISIONING_PROFILE_NAME \
229
+ APP_STORE_CONNECT_API_KEY_ID APP_STORE_CONNECT_ISSUER_ID APP_STORE_CONNECT_API_KEY_BASE64; do
230
+ [ -n "${!name}" ] || MISSING="$MISSING $name"
231
+ done
232
+ if [ -n "$MISSING" ]; then
233
+ echo "::error::필수 GitHub Secret이 비어 있습니다:$MISSING (워크플로우 상단 주석 참고)"
234
+ FAILED=1
235
+ fi
236
+ if [ ! -f ios/ExportOptions.plist ]; then
237
+ echo "::error::ios/ExportOptions.plist가 없습니다 — 저장소에 추가(커밋)하세요."
238
+ FAILED=1
239
+ elif grep -Eq '__[A-Z][A-Z0-9_]*__' ios/ExportOptions.plist; then
240
+ echo "::error::ios/ExportOptions.plist에 채워지지 않은 플레이스홀더가 남아 있습니다 — 팀 ID·번들 ID·프로비저닝 프로파일 이름으로 바꿔 커밋하세요."
241
+ grep -En '__[A-Z][A-Z0-9_]*__' ios/ExportOptions.plist
242
+ FAILED=1
243
+ fi
244
+ if [ ! -f ios/fastlane/Fastfile ]; then
245
+ echo "::error::ios/fastlane/Fastfile이 없습니다 — 저장소에 추가(커밋)하세요."
246
+ FAILED=1
247
+ fi
248
+ [ "$FAILED" -eq 0 ] || exit 1
249
+ echo "✅ 배포 설정 확인 완료"
250
+
208
251
  - name: Select Xcode version
209
252
  run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
210
253
 
@@ -521,6 +564,7 @@ jobs:
521
564
  - name: Set up Flutter
522
565
  uses: subosito/flutter-action@v2
523
566
  with:
567
+ channel: stable
524
568
  flutter-version: ${{ env.FLUTTER_VERSION }}
525
569
  cache: true
526
570
 
@@ -546,8 +590,13 @@ jobs:
546
590
 
547
591
  - name: Install CocoaPods
548
592
  run: |
549
- gem install cocoapods
550
- cd ios && pod install
593
+ # 플러그인이 없는 기본 프로젝트는 Podfile이 없다 — 플러그인이 생기면 flutter build가 Podfile을 만든다
594
+ if [ -f ios/Podfile ]; then
595
+ gem install cocoapods
596
+ (cd ios && pod install)
597
+ else
598
+ echo "ℹ️ ios/Podfile이 없어 pod install을 건너뜁니다 (CocoaPods 플러그인 없음)"
599
+ fi
551
600
 
552
601
  - name: Import Code-Signing Certificates
553
602
  uses: Apple-Actions/import-codesign-certs@v2
@@ -52,7 +52,7 @@ on:
52
52
  deploy_mode:
53
53
  description: "배포 모드 (store_only=TestFlight까지 / store_prepare=ASC 제출직전 / store_submit=심사 자동제출)"
54
54
  required: false
55
- default: "store_only"
55
+ default: "store_only" # @wizard auto:ios-deploy-mode
56
56
  type: choice
57
57
  options:
58
58
  - store_only
@@ -64,7 +64,9 @@ env:
64
64
  FLUTTER_PROJECT_DIR: "." # @wizard auto:flutter-root
65
65
  # 환경변수 전달 방식 (dart-define | dotenv) — 설치 시 선택값으로 자동 설정
66
66
  ENV_MODE: "dart-define" # @wizard auto:flutter-env-mode
67
- FLUTTER_VERSION: "3.35.5"
67
+ # Flutter SDK 버전 — 비워 두면 stable 채널 최신을 쓴다(최신 flutter create 프로젝트의 SDK 제약과 맞음).
68
+ # 특정 버전에 고정하려면 "3.38.9"처럼 적는다. pubspec.yaml의 environment.sdk 범위를 만족해야 한다.
69
+ FLUTTER_VERSION: ""
68
70
  XCODE_VERSION: "26.0"
69
71
  PROJECT_TYPE: "flutter"
70
72
 
@@ -96,11 +98,46 @@ jobs:
96
98
  - name: Checkout repository
97
99
  uses: actions/checkout@v7
98
100
  with:
99
- ref: main
101
+ ref: "{{MAIN_BRANCH}}"
100
102
  fetch-depth: 0
101
103
 
102
104
  - name: Pull latest changes
103
- run: git pull origin main
105
+ run: git pull origin {{MAIN_BRANCH}}
106
+
107
+ # 인증서 import·빌드 전에 설정 누락을 한 번에 알려 준다 — 없으면 macOS 러너에서 한참 빌드한 뒤에야 엉뚱한 단계에서 실패한다
108
+ - name: 배포 설정 사전 검증
109
+ env:
110
+ APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
111
+ APPLE_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}
112
+ IOS_PROVISIONING_PROFILE_NAME: ${{ secrets.IOS_PROVISIONING_PROFILE_NAME }}
113
+ APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
114
+ APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
115
+ APP_STORE_CONNECT_API_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_BASE64 }}
116
+ run: |
117
+ FAILED=0
118
+ MISSING=""
119
+ for name in APPLE_CERTIFICATE_BASE64 APPLE_PROVISIONING_PROFILE_BASE64 IOS_PROVISIONING_PROFILE_NAME \
120
+ APP_STORE_CONNECT_API_KEY_ID APP_STORE_CONNECT_ISSUER_ID APP_STORE_CONNECT_API_KEY_BASE64; do
121
+ [ -n "${!name}" ] || MISSING="$MISSING $name"
122
+ done
123
+ if [ -n "$MISSING" ]; then
124
+ echo "::error::필수 GitHub Secret이 비어 있습니다:$MISSING (워크플로우 상단 주석 참고)"
125
+ FAILED=1
126
+ fi
127
+ if [ ! -f ios/ExportOptions.plist ]; then
128
+ echo "::error::ios/ExportOptions.plist가 없습니다 — 저장소에 추가(커밋)하세요."
129
+ FAILED=1
130
+ elif grep -Eq '__[A-Z][A-Z0-9_]*__' ios/ExportOptions.plist; then
131
+ echo "::error::ios/ExportOptions.plist에 채워지지 않은 플레이스홀더가 남아 있습니다 — 팀 ID·번들 ID·프로비저닝 프로파일 이름으로 바꿔 커밋하세요."
132
+ grep -En '__[A-Z][A-Z0-9_]*__' ios/ExportOptions.plist
133
+ FAILED=1
134
+ fi
135
+ if [ ! -f ios/fastlane/Fastfile ]; then
136
+ echo "::error::ios/fastlane/Fastfile이 없습니다 — 저장소에 추가(커밋)하세요."
137
+ FAILED=1
138
+ fi
139
+ [ "$FAILED" -eq 0 ] || exit 1
140
+ echo "✅ 배포 설정 확인 완료"
104
141
 
105
142
  - name: Select Xcode version
106
143
  run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
@@ -132,6 +169,7 @@ jobs:
132
169
  - name: Set up Flutter
133
170
  uses: subosito/flutter-action@v2
134
171
  with:
172
+ channel: stable
135
173
  flutter-version: ${{ env.FLUTTER_VERSION }}
136
174
  cache: true
137
175
 
@@ -157,8 +195,13 @@ jobs:
157
195
 
158
196
  - name: Install CocoaPods
159
197
  run: |
160
- gem install cocoapods
161
- cd ios && pod install
198
+ # 플러그인이 없는 기본 프로젝트는 Podfile이 없다 — 플러그인이 생기면 flutter build가 Podfile을 만든다
199
+ if [ -f ios/Podfile ]; then
200
+ gem install cocoapods
201
+ (cd ios && pod install)
202
+ else
203
+ echo "ℹ️ ios/Podfile이 없어 pod install을 건너뜁니다 (CocoaPods 플러그인 없음)"
204
+ fi
162
205
 
163
206
  - name: 현재 버전 정보 가져오기
164
207
  id: current_version
@@ -227,11 +270,11 @@ jobs:
227
270
  - name: Checkout repository
228
271
  uses: actions/checkout@v7
229
272
  with:
230
- ref: main
273
+ ref: "{{MAIN_BRANCH}}"
231
274
  fetch-depth: 0
232
275
 
233
276
  - name: Pull latest changes
234
- run: git pull origin main
277
+ run: git pull origin {{MAIN_BRANCH}}
235
278
 
236
279
  - name: Select Xcode version
237
280
  run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app/Contents/Developer
@@ -276,6 +319,7 @@ jobs:
276
319
  - name: Set up Flutter
277
320
  uses: subosito/flutter-action@v2
278
321
  with:
322
+ channel: stable
279
323
  flutter-version: ${{ env.FLUTTER_VERSION }}
280
324
  cache: true
281
325
 
@@ -301,8 +345,13 @@ jobs:
301
345
 
302
346
  - name: Install CocoaPods
303
347
  run: |
304
- gem install cocoapods
305
- cd ios && pod install
348
+ # 플러그인이 없는 기본 프로젝트는 Podfile이 없다 — 플러그인이 생기면 flutter build가 Podfile을 만든다
349
+ if [ -f ios/Podfile ]; then
350
+ gem install cocoapods
351
+ (cd ios && pod install)
352
+ else
353
+ echo "ℹ️ ios/Podfile이 없어 pod install을 건너뜁니다 (CocoaPods 플러그인 없음)"
354
+ fi
306
355
 
307
356
  - name: Import Code-Signing Certificates
308
357
  uses: Apple-Actions/import-codesign-certs@v2
@@ -387,7 +436,7 @@ jobs:
387
436
  - name: Checkout repository
388
437
  uses: actions/checkout@v7
389
438
  with:
390
- ref: main
439
+ ref: "{{MAIN_BRANCH}}"
391
440
  fetch-depth: 0
392
441
 
393
442
  - name: Setup App Store Connect API Key
@@ -40,6 +40,9 @@ jobs:
40
40
  if: ${{ github.event_name != 'workflow_dispatch' }}
41
41
  uses: dorny/paths-filter@v4
42
42
  with:
43
+ # push는 이번 push 범위(직전 커밋 before 대비)만 본다. 비워 두면 기본 브랜치 대비 누적 diff가 된다.
44
+ # 새 브랜치 첫 push(before가 0)와 PR은 빈 값 → 기본 동작(기본 브랜치·PR base 대비)
45
+ base: ${{ github.event_name == 'push' && github.event.before != '0000000000000000000000000000000000000000' && github.event.before || '' }}
43
46
  filters: |
44
47
  project:
45
48
  - '${{ 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: 코드 체크아웃
@@ -59,7 +66,8 @@ jobs:
59
66
  - name: Go 설정
60
67
  uses: actions/setup-go@v7
61
68
  with:
62
- go-version-file: go.mod
69
+ go-version-file: ${{ env.PROJECT_PATH }}/go.mod
70
+ cache-dependency-path: ${{ env.PROJECT_PATH }}/go.sum
63
71
  cache: true
64
72
 
65
73
  - name: go build 검증
@@ -77,6 +85,7 @@ jobs:
77
85
  uses: golangci/golangci-lint-action@v8
78
86
  with:
79
87
  version: latest
88
+ working-directory: ${{ env.PROJECT_PATH }}
80
89
 
81
90
  - name: 빌드 검증 완료
82
91
  run: |
@@ -28,9 +28,13 @@ name: PROJECT-GO-PR-PREVIEW
28
28
  env:
29
29
  # 프로젝트 고유 식별자 (컨테이너명, 이미지명, 도메인에 사용)
30
30
  PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
31
+ # 모노레포(--paths)에서 이 프로젝트가 있는 하위 경로. 빌드·Docker context의 기준 ('.'이면 레포 루트)
32
+ PROJECT_PATH: "." # @wizard auto:project-path
33
+ # 모노레포(PROJECT_PATH가 '.'이 아님)에서는 이미지·컨테이너·도메인 이름에 '-go'를 붙여
34
+ # 같은 레포의 다른 타입 서비스와 이름이 겹치지 않게 한다 (예: my-app-go)
31
35
 
32
36
  # Docker 설정
33
- DOCKERFILE_PATH: './Dockerfile'
37
+ DOCKERFILE_PATH: './Dockerfile' # PROJECT_PATH 기준 상대 경로
34
38
  INTERNAL_PORT: '8080'
35
39
 
36
40
  # 볼륨 마운트 설정 (프로젝트별 수정 필요, 빈값이면 마운트 안함)
@@ -227,8 +231,45 @@ jobs:
227
231
  with:
228
232
  ref: ${{ steps.pr.outputs.ref }}
229
233
 
234
+ # 필수 Secret·Dockerfile이 없으면 docker 로그인 같은 서드파티 오류 대신 빠진 항목을 알려 주고 멈춘다
235
+ - name: 배포 사전 점검 (Secret·Dockerfile)
236
+ working-directory: ${{ env.PROJECT_PATH }}
237
+ env:
238
+ DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
239
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
240
+ SERVER_HOST: ${{ secrets.SERVER_HOST }}
241
+ SERVER_USER: ${{ secrets.SERVER_USER }}
242
+ SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
243
+ SSH_KEY: ${{ secrets.SSH_KEY }}
244
+ DOCKERFILE: ${{ env.DOCKERFILE_PATH }}
245
+ run: |
246
+ MISSING=""
247
+ for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
248
+ [ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
249
+ done
250
+ # SSH_AUTH_METHOD=key면 SSH_KEY, 아니면 SERVER_PASSWORD가 필요하다
251
+ if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
252
+ [ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
253
+ else
254
+ [ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
255
+ fi
256
+ FAILED=false
257
+ if [ -n "$MISSING" ]; then
258
+ echo "::error title=필수 Secret 누락::등록되지 않은 GitHub Secret:$MISSING — Settings → Secrets and variables → Actions에서 등록하세요"
259
+ FAILED=true
260
+ fi
261
+ if [ ! -f "$DOCKERFILE" ]; then
262
+ echo "::error title=Dockerfile 없음::${PROJECT_PATH}/${DOCKERFILE#./} 파일이 없습니다. 이 워크플로우는 Docker 이미지로 배포하므로 Dockerfile이 필요합니다 (서버 배포를 하지 않는다면 이 워크플로우 파일을 삭제하세요)"
263
+ FAILED=true
264
+ fi
265
+ if [ "$FAILED" = "true" ]; then
266
+ exit 1
267
+ fi
268
+ echo "✅ 사전 점검 통과: 필수 Secret과 Dockerfile이 모두 있습니다"
269
+
230
270
  # [영역 2] 환경변수 파일 생성
231
271
  - name: "[필수] .env 파일 생성"
272
+ working-directory: ${{ env.PROJECT_PATH }}
232
273
  run: |
233
274
  cat << 'EOF' > .env
234
275
  ${{ secrets.ENV_FILE }}
@@ -247,10 +288,10 @@ jobs:
247
288
  - name: Docker 이미지 빌드 & Push
248
289
  uses: docker/build-push-action@v5
249
290
  with:
250
- context: .
251
- file: ${{ env.DOCKERFILE_PATH }}
291
+ context: ${{ env.PROJECT_PATH }}
292
+ file: ${{ env.PROJECT_PATH }}/${{ env.DOCKERFILE_PATH }}
252
293
  push: true
253
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_NAME }}:pr-${{ github.event.issue.number }}
294
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}:pr-${{ github.event.issue.number }}
254
295
  cache-from: type=gha
255
296
  cache-to: type=gha,mode=max
256
297
 
@@ -319,7 +360,7 @@ jobs:
319
360
  echo "🔐 SSH 인증 방식: ${SSH_AUTH_METHOD}"
320
361
 
321
362
  PR_NUMBER=${{ github.event.issue.number }}
322
- PROJECT_NAME="${{ env.PROJECT_NAME }}"
363
+ PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}"
323
364
  CONTAINER_NAME="${PROJECT_NAME}-pr-${PR_NUMBER}"
324
365
  IMAGE="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:pr-${PR_NUMBER}"
325
366
  DOMAIN="${PROJECT_NAME}-pr-${PR_NUMBER}.${{ env.PREVIEW_DOMAIN_SUFFIX }}"
@@ -446,7 +487,7 @@ jobs:
446
487
  const seconds = Math.floor((deployElapsed % 60000) / 1000);
447
488
  const deployDuration = minutes > 0 ? `${minutes}분 ${seconds}초` : `${seconds}초`;
448
489
 
449
- const projectName = '${{ env.PROJECT_NAME }}';
490
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
450
491
  const domainSuffix = '${{ env.PREVIEW_DOMAIN_SUFFIX }}';
451
492
  const previewPort = '${{ env.PREVIEW_PORT }}';
452
493
  const apiDocsPath = '${{ env.API_DOCS_PATH }}';
@@ -514,7 +555,7 @@ jobs:
514
555
  with:
515
556
  script: |
516
557
  const commentId = ${{ steps.progress.outputs.comment_id || 0 }};
517
- const projectName = '${{ env.PROJECT_NAME }}';
558
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
518
559
  const prNumber = context.issue.number;
519
560
  const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
520
561
 
@@ -551,6 +592,7 @@ jobs:
551
592
  `**[📋 빌드/배포 로그 확인](${runUrl})**`,
552
593
  '',
553
594
  '### 🔍 가능한 원인',
595
+ '- 필수 Secret(DOCKERHUB_*, SERVER_*) 미등록 또는 Dockerfile 없음 → 로그의 "배포 사전 점검" 단계 확인',
554
596
  '- Docker 이미지 빌드 실패 (Go 의존성/빌드 문제)',
555
597
  '- 컨테이너 시작 실패 (애플리케이션 기동 오류)',
556
598
  '- Health Check 타임아웃 (120초 내 기동 완료 안됨)',
@@ -793,8 +835,46 @@ jobs:
793
835
  with:
794
836
  ref: ${{ needs.get-branch-from-issue.outputs.branch_name }}
795
837
 
838
+ # 필수 Secret·Dockerfile이 없으면 docker 로그인 같은 서드파티 오류 대신 빠진 항목을 알려 주고 멈춘다
839
+ - name: 배포 사전 점검 (Secret·Dockerfile)
840
+ if: steps.check_branch.outputs.exists == 'true'
841
+ working-directory: ${{ env.PROJECT_PATH }}
842
+ env:
843
+ DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
844
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
845
+ SERVER_HOST: ${{ secrets.SERVER_HOST }}
846
+ SERVER_USER: ${{ secrets.SERVER_USER }}
847
+ SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
848
+ SSH_KEY: ${{ secrets.SSH_KEY }}
849
+ DOCKERFILE: ${{ env.DOCKERFILE_PATH }}
850
+ run: |
851
+ MISSING=""
852
+ for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
853
+ [ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
854
+ done
855
+ # SSH_AUTH_METHOD=key면 SSH_KEY, 아니면 SERVER_PASSWORD가 필요하다
856
+ if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
857
+ [ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
858
+ else
859
+ [ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
860
+ fi
861
+ FAILED=false
862
+ if [ -n "$MISSING" ]; then
863
+ echo "::error title=필수 Secret 누락::등록되지 않은 GitHub Secret:$MISSING — Settings → Secrets and variables → Actions에서 등록하세요"
864
+ FAILED=true
865
+ fi
866
+ if [ ! -f "$DOCKERFILE" ]; then
867
+ echo "::error title=Dockerfile 없음::${PROJECT_PATH}/${DOCKERFILE#./} 파일이 없습니다. 이 워크플로우는 Docker 이미지로 배포하므로 Dockerfile이 필요합니다 (서버 배포를 하지 않는다면 이 워크플로우 파일을 삭제하세요)"
868
+ FAILED=true
869
+ fi
870
+ if [ "$FAILED" = "true" ]; then
871
+ exit 1
872
+ fi
873
+ echo "✅ 사전 점검 통과: 필수 Secret과 Dockerfile이 모두 있습니다"
874
+
796
875
  # [영역 2] 환경변수 파일 생성
797
876
  - name: "[필수] .env 파일 생성"
877
+ working-directory: ${{ env.PROJECT_PATH }}
798
878
  if: steps.check_branch.outputs.exists == 'true'
799
879
  run: |
800
880
  cat << 'EOF' > .env
@@ -817,10 +897,10 @@ jobs:
817
897
  if: steps.check_branch.outputs.exists == 'true'
818
898
  uses: docker/build-push-action@v5
819
899
  with:
820
- context: .
821
- file: ${{ env.DOCKERFILE_PATH }}
900
+ context: ${{ env.PROJECT_PATH }}
901
+ file: ${{ env.PROJECT_PATH }}/${{ env.DOCKERFILE_PATH }}
822
902
  push: true
823
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_NAME }}:pr-${{ needs.get-branch-from-issue.outputs.issue_number }}
903
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}:pr-${{ needs.get-branch-from-issue.outputs.issue_number }}
824
904
  cache-from: type=gha
825
905
  cache-to: type=gha,mode=max
826
906
 
@@ -894,7 +974,7 @@ jobs:
894
974
  echo "🔐 SSH 인증 방식: ${SSH_AUTH_METHOD}"
895
975
 
896
976
  ISSUE_NUMBER=${{ needs.get-branch-from-issue.outputs.issue_number }}
897
- PROJECT_NAME="${{ env.PROJECT_NAME }}"
977
+ PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}"
898
978
  CONTAINER_NAME="${PROJECT_NAME}-pr-${ISSUE_NUMBER}"
899
979
  IMAGE="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:pr-${ISSUE_NUMBER}"
900
980
  DOMAIN="${PROJECT_NAME}-pr-${ISSUE_NUMBER}.${{ env.PREVIEW_DOMAIN_SUFFIX }}"
@@ -1023,7 +1103,7 @@ jobs:
1023
1103
  const seconds = Math.floor((deployElapsed % 60000) / 1000);
1024
1104
  const deployDuration = minutes > 0 ? `${minutes}분 ${seconds}초` : `${seconds}초`;
1025
1105
 
1026
- const projectName = '${{ env.PROJECT_NAME }}';
1106
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
1027
1107
  const domainSuffix = '${{ env.PREVIEW_DOMAIN_SUFFIX }}';
1028
1108
  const previewPort = '${{ env.PREVIEW_PORT }}';
1029
1109
  const apiDocsPath = '${{ env.API_DOCS_PATH }}';
@@ -1092,7 +1172,7 @@ jobs:
1092
1172
  with:
1093
1173
  script: |
1094
1174
  const commentId = ${{ steps.progress.outputs.comment_id || 0 }};
1095
- const projectName = '${{ env.PROJECT_NAME }}';
1175
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
1096
1176
  const issueNumber = ${{ needs.get-branch-from-issue.outputs.issue_number }};
1097
1177
  const branchName = '${{ needs.get-branch-from-issue.outputs.branch_name }}';
1098
1178
  const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
@@ -1127,6 +1207,7 @@ jobs:
1127
1207
  `**[📋 빌드/배포 로그 확인](${runUrl})**`,
1128
1208
  '',
1129
1209
  '### 🔍 가능한 원인',
1210
+ '- 필수 Secret(DOCKERHUB_*, SERVER_*) 미등록 또는 Dockerfile 없음 → 로그의 "배포 사전 점검" 단계 확인',
1130
1211
  '- Docker 이미지 빌드 실패 (Go 의존성/빌드 문제)',
1131
1212
  '- 컨테이너 시작 실패 (애플리케이션 기동 오류)',
1132
1213
  '- Health Check 타임아웃 (120초 내 기동 완료 안됨)',
@@ -1205,7 +1286,7 @@ jobs:
1205
1286
  echo "🔐 SSH 인증 방식: ${SSH_AUTH_METHOD}"
1206
1287
 
1207
1288
  PR_NUMBER=${{ steps.pr_number.outputs.number }}
1208
- PROJECT_NAME="${{ env.PROJECT_NAME }}"
1289
+ PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}"
1209
1290
  CONTAINER_NAME="${PROJECT_NAME}-pr-${PR_NUMBER}"
1210
1291
  IMAGE="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:pr-${PR_NUMBER}"
1211
1292
 
@@ -1233,7 +1314,7 @@ jobs:
1233
1314
  with:
1234
1315
  script: |
1235
1316
  const number = ${{ steps.pr_number.outputs.number }};
1236
- const projectName = '${{ env.PROJECT_NAME }}';
1317
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
1237
1318
 
1238
1319
  const body = [
1239
1320
  '## 🗑️ Preview 환경 삭제 완료!',
@@ -1276,7 +1357,7 @@ jobs:
1276
1357
  export PATH=$PATH:/usr/local/bin
1277
1358
 
1278
1359
  NUMBER=${{ github.event.issue.number }}
1279
- PROJECT_NAME="${{ env.PROJECT_NAME }}"
1360
+ PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}"
1280
1361
  CONTAINER_NAME="${PROJECT_NAME}-pr-${NUMBER}"
1281
1362
 
1282
1363
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -1306,7 +1387,7 @@ jobs:
1306
1387
  export PATH=$PATH:/usr/local/bin
1307
1388
 
1308
1389
  NUMBER=${{ github.event.issue.number }}
1309
- PROJECT_NAME="${{ env.PROJECT_NAME }}"
1390
+ PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}"
1310
1391
  CONTAINER_NAME="${PROJECT_NAME}-pr-${NUMBER}"
1311
1392
  docker ps --filter "name=${CONTAINER_NAME}" --format "{{.Names}}" | grep -q "${CONTAINER_NAME}"
1312
1393
 
@@ -1315,7 +1396,7 @@ jobs:
1315
1396
  with:
1316
1397
  script: |
1317
1398
  const number = context.issue.number;
1318
- const projectName = '${{ env.PROJECT_NAME }}';
1399
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
1319
1400
  const domainSuffix = '${{ env.PREVIEW_DOMAIN_SUFFIX }}';
1320
1401
  const previewPort = '${{ env.PREVIEW_PORT }}';
1321
1402
  const domain = `${projectName}-pr-${number}.${domainSuffix}`;
@@ -1505,7 +1586,45 @@ jobs:
1505
1586
  with:
1506
1587
  ref: ${{ needs.check-command.outputs.custom_branch }}
1507
1588
 
1589
+ # 필수 Secret·Dockerfile이 없으면 docker 로그인 같은 서드파티 오류 대신 빠진 항목을 알려 주고 멈춘다
1590
+ - name: 배포 사전 점검 (Secret·Dockerfile)
1591
+ if: steps.branch_info.outputs.branch_exists == 'true'
1592
+ working-directory: ${{ env.PROJECT_PATH }}
1593
+ env:
1594
+ DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
1595
+ DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
1596
+ SERVER_HOST: ${{ secrets.SERVER_HOST }}
1597
+ SERVER_USER: ${{ secrets.SERVER_USER }}
1598
+ SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
1599
+ SSH_KEY: ${{ secrets.SSH_KEY }}
1600
+ DOCKERFILE: ${{ env.DOCKERFILE_PATH }}
1601
+ run: |
1602
+ MISSING=""
1603
+ for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
1604
+ [ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
1605
+ done
1606
+ # SSH_AUTH_METHOD=key면 SSH_KEY, 아니면 SERVER_PASSWORD가 필요하다
1607
+ if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
1608
+ [ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
1609
+ else
1610
+ [ -n "$SERVER_PASSWORD" ] || MISSING="$MISSING SERVER_PASSWORD"
1611
+ fi
1612
+ FAILED=false
1613
+ if [ -n "$MISSING" ]; then
1614
+ echo "::error title=필수 Secret 누락::등록되지 않은 GitHub Secret:$MISSING — Settings → Secrets and variables → Actions에서 등록하세요"
1615
+ FAILED=true
1616
+ fi
1617
+ if [ ! -f "$DOCKERFILE" ]; then
1618
+ echo "::error title=Dockerfile 없음::${PROJECT_PATH}/${DOCKERFILE#./} 파일이 없습니다. 이 워크플로우는 Docker 이미지로 배포하므로 Dockerfile이 필요합니다 (서버 배포를 하지 않는다면 이 워크플로우 파일을 삭제하세요)"
1619
+ FAILED=true
1620
+ fi
1621
+ if [ "$FAILED" = "true" ]; then
1622
+ exit 1
1623
+ fi
1624
+ echo "✅ 사전 점검 통과: 필수 Secret과 Dockerfile이 모두 있습니다"
1625
+
1508
1626
  - name: "[필수] .env 파일 생성"
1627
+ working-directory: ${{ env.PROJECT_PATH }}
1509
1628
  if: steps.branch_info.outputs.branch_exists == 'true'
1510
1629
  run: |
1511
1630
  cat << 'EOF' > .env
@@ -1533,16 +1652,16 @@ jobs:
1533
1652
  SUFFIX="pr-${{ steps.branch_info.outputs.container_number }}"
1534
1653
  fi
1535
1654
  echo "suffix=$SUFFIX" >> $GITHUB_OUTPUT
1536
- echo "name=${{ env.PROJECT_NAME }}-${SUFFIX}" >> $GITHUB_OUTPUT
1655
+ echo "name=${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}-${SUFFIX}" >> $GITHUB_OUTPUT
1537
1656
 
1538
1657
  - name: Docker 이미지 빌드 & Push
1539
1658
  if: steps.branch_info.outputs.branch_exists == 'true'
1540
1659
  uses: docker/build-push-action@v5
1541
1660
  with:
1542
- context: .
1543
- file: ${{ env.DOCKERFILE_PATH }}
1661
+ context: ${{ env.PROJECT_PATH }}
1662
+ file: ${{ env.PROJECT_PATH }}/${{ env.DOCKERFILE_PATH }}
1544
1663
  push: true
1545
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_NAME }}:${{ steps.container.outputs.suffix }}
1664
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}:${{ steps.container.outputs.suffix }}
1546
1665
  cache-from: type=gha
1547
1666
  cache-to: type=gha,mode=max
1548
1667
 
@@ -1615,7 +1734,7 @@ jobs:
1615
1734
  fi
1616
1735
  echo "🔐 SSH 인증 방식: ${SSH_AUTH_METHOD}"
1617
1736
 
1618
- PROJECT_NAME="${{ env.PROJECT_NAME }}"
1737
+ PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}"
1619
1738
  CONTAINER_NAME="${{ steps.container.outputs.name }}"
1620
1739
  CONTAINER_SUFFIX="${{ steps.container.outputs.suffix }}"
1621
1740
  IMAGE="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${CONTAINER_SUFFIX}"
@@ -1739,7 +1858,7 @@ jobs:
1739
1858
  const seconds = Math.floor((deployElapsed % 60000) / 1000);
1740
1859
  const deployDuration = minutes > 0 ? `${minutes}분 ${seconds}초` : `${seconds}초`;
1741
1860
 
1742
- const projectName = '${{ env.PROJECT_NAME }}';
1861
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
1743
1862
  const domainSuffix = '${{ env.PREVIEW_DOMAIN_SUFFIX }}';
1744
1863
  const previewPort = '${{ env.PREVIEW_PORT }}';
1745
1864
  const apiDocsPath = '${{ env.API_DOCS_PATH }}';
@@ -1841,6 +1960,7 @@ jobs:
1841
1960
  `**[📋 빌드/배포 로그 확인](${runUrl})**`,
1842
1961
  '',
1843
1962
  '### 🔍 가능한 원인',
1963
+ '- 필수 Secret(DOCKERHUB_*, SERVER_*) 미등록 또는 Dockerfile 없음 → 로그의 "배포 사전 점검" 단계 확인',
1844
1964
  '- Docker 이미지 빌드 실패 (Go 의존성/빌드 문제)',
1845
1965
  '- 컨테이너 시작 실패 (애플리케이션 기동 오류)',
1846
1966
  '- Health Check 타임아웃 (120초 내 기동 완료 안됨)',
@@ -1886,7 +2006,7 @@ jobs:
1886
2006
  with:
1887
2007
  script: |
1888
2008
  const branchName = '${{ needs.check-command.outputs.custom_branch }}';
1889
- const projectName = '${{ env.PROJECT_NAME }}';
2009
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
1890
2010
 
1891
2011
  // 브랜치명에서 Issue 번호 추출 (#번호 패턴)
1892
2012
  const issueMatch = branchName.match(/#(\d+)/);
@@ -1936,7 +2056,7 @@ jobs:
1936
2056
  echo "🔐 SSH 인증 방식: ${SSH_AUTH_METHOD}"
1937
2057
 
1938
2058
  CONTAINER_NAME="${{ steps.container_info.outputs.container_name }}"
1939
- PROJECT_NAME="${{ env.PROJECT_NAME }}"
2059
+ PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}"
1940
2060
 
1941
2061
  # 컨테이너명에서 suffix 추출
1942
2062
  SUFFIX="${CONTAINER_NAME#${PROJECT_NAME}-}"
@@ -2002,7 +2122,7 @@ jobs:
2002
2122
  with:
2003
2123
  script: |
2004
2124
  const branchName = '${{ needs.check-command.outputs.custom_branch }}';
2005
- const projectName = '${{ env.PROJECT_NAME }}';
2125
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
2006
2126
 
2007
2127
  // 브랜치명에서 Issue 번호 추출 (#번호 패턴)
2008
2128
  const issueMatch = branchName.match(/#(\d+)/);
@@ -2060,7 +2180,7 @@ jobs:
2060
2180
  script: |
2061
2181
  const branchName = '${{ needs.check-command.outputs.custom_branch }}';
2062
2182
  const containerName = '${{ steps.container_info.outputs.container_name }}';
2063
- const projectName = '${{ env.PROJECT_NAME }}';
2183
+ const projectName = '${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}';
2064
2184
  const domainSuffix = '${{ env.PREVIEW_DOMAIN_SUFFIX }}';
2065
2185
  const previewPort = '${{ env.PREVIEW_PORT }}';
2066
2186
  const domain = `${containerName}.${domainSuffix}`;