project-auto-wizard 0.13.3 โ†’ 0.14.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 (112) hide show
  1. package/README.md +1 -1
  2. package/bin/project-auto-wizard.js +18 -4
  3. package/package.json +2 -2
  4. package/payload/config/breaking-changes.json +28 -14
  5. package/payload/config/wizard-prompts.yml +102 -46
  6. package/payload/flutter-app/android/fastlane/Fastfile.playstore +44 -32
  7. package/payload/flutter-app/ios/ExportOptions.plist +8 -8
  8. package/payload/flutter-app/ios/fastlane/Fastfile +57 -43
  9. package/payload/scripts/changelog_manager.py +296 -257
  10. package/payload/scripts/issue_helper.py +54 -50
  11. package/payload/scripts/messages.py +2279 -0
  12. package/payload/scripts/truncate_release_notes.py +7 -6
  13. package/payload/scripts/version_manager.py +64 -61
  14. package/payload/version.yml.template +2 -1
  15. package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +17 -12
  16. package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +59 -34
  17. package/payload/workflows/common/PROJECT-COMMON-ISSUE-HELPER.yaml +17 -15
  18. package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +17 -10
  19. package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +54 -44
  20. package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +24 -17
  21. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +194 -184
  22. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +226 -213
  23. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +46 -42
  24. package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +334 -241
  25. package/payload/workflows/flutter/PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml +152 -128
  26. package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +246 -188
  27. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +353 -238
  28. package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +96 -85
  29. package/payload/workflows/go/PROJECT-GO-CI.yaml +47 -36
  30. package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +945 -508
  31. package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +155 -133
  32. package/payload/workflows/next/PROJECT-NEXT-CI.yaml +76 -60
  33. package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +97 -77
  34. package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +62 -47
  35. package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +942 -505
  36. package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +148 -126
  37. package/payload/workflows/react/PROJECT-REACT-CI.yaml +86 -70
  38. package/payload/workflows/react/PROJECT-REACT-CICD.yaml +112 -92
  39. package/payload/workflows/spring/PROJECT-SPRING-CI.yml +112 -82
  40. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +180 -150
  41. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +161 -131
  42. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +970 -549
  43. package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +193 -162
  44. package/src/cli/args.js +98 -52
  45. package/src/cli/help.js +8 -41
  46. package/src/commands/doctor.js +85 -89
  47. package/src/commands/dry-run.js +63 -43
  48. package/src/commands/full.js +79 -77
  49. package/src/commands/install-settings.js +16 -15
  50. package/src/commands/interactive-flutter.js +20 -19
  51. package/src/commands/interactive.js +135 -132
  52. package/src/commands/purge.js +47 -42
  53. package/src/commands/status.js +36 -36
  54. package/src/commands/uninstall.js +43 -41
  55. package/src/context.js +12 -11
  56. package/src/core/assets.js +12 -11
  57. package/src/core/baseline.js +27 -27
  58. package/src/core/branches.js +29 -27
  59. package/src/core/branding.js +7 -6
  60. package/src/core/breaking-check.js +22 -21
  61. package/src/core/breaking.js +15 -8
  62. package/src/core/copy/app-files.js +5 -4
  63. package/src/core/copy/flutter-app.js +8 -8
  64. package/src/core/copy/gitignore.js +58 -50
  65. package/src/core/copy/readme.js +45 -35
  66. package/src/core/copy/simple.js +16 -15
  67. package/src/core/copy/workflows.js +131 -124
  68. package/src/core/deploy-style.js +51 -48
  69. package/src/core/detect-fs.js +57 -57
  70. package/src/core/detect.js +67 -65
  71. package/src/core/errors.js +2 -2
  72. package/src/core/flutter-doctor.js +17 -16
  73. package/src/core/flutter-hooks.js +33 -32
  74. package/src/core/flutter-options.js +34 -33
  75. package/src/core/fsutil.js +12 -12
  76. package/src/core/installed-stores.js +4 -4
  77. package/src/core/logger.js +42 -40
  78. package/src/core/paths-resolve.js +88 -86
  79. package/src/core/paths.js +10 -10
  80. package/src/core/release-options.js +8 -8
  81. package/src/core/removal-exec.js +14 -13
  82. package/src/core/removal-plan.js +37 -37
  83. package/src/core/types.js +47 -47
  84. package/src/core/verify.js +44 -38
  85. package/src/core/version-yml.js +76 -68
  86. package/src/core/wizard-env.js +46 -46
  87. package/src/core/wizard-labels.js +63 -41
  88. package/src/i18n/catalog/en/cli.js +50 -0
  89. package/src/i18n/catalog/en/commands.js +187 -0
  90. package/src/i18n/catalog/en/copy.js +19 -0
  91. package/src/i18n/catalog/en/core.js +74 -0
  92. package/src/i18n/catalog/en/core2.js +15 -0
  93. package/src/i18n/catalog/en/ui.js +221 -0
  94. package/src/i18n/catalog/en.js +12 -0
  95. package/src/i18n/catalog/index.js +5 -0
  96. package/src/i18n/catalog/ko/cli.js +50 -0
  97. package/src/i18n/catalog/ko/commands.js +187 -0
  98. package/src/i18n/catalog/ko/copy.js +19 -0
  99. package/src/i18n/catalog/ko/core.js +74 -0
  100. package/src/i18n/catalog/ko/core2.js +15 -0
  101. package/src/i18n/catalog/ko/ui.js +221 -0
  102. package/src/i18n/catalog/ko.js +10 -0
  103. package/src/i18n/index.js +44 -0
  104. package/src/i18n/languages.js +9 -0
  105. package/src/index.js +128 -94
  106. package/src/ui/ansi.js +7 -7
  107. package/src/ui/banner.js +7 -6
  108. package/src/ui/env-plan.js +82 -81
  109. package/src/ui/prompts.js +97 -122
  110. package/src/ui/readline-engine.js +59 -58
  111. package/src/ui/status-cards.js +37 -31
  112. package/src/ui/summary.js +89 -88
@@ -1,82 +1,83 @@
1
1
  # project-auto-wizard:managed-workflow
2
- # Go CI/CD ๋ฐฐํฌ (SSH + Docker)
3
- # {{MAIN_BRANCH}} ๋ธŒ๋žœ์น˜ ํ‘ธ์‹œ ์‹œ Docker ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ณ 
4
- # SSH๋กœ ์ ‘์† ๊ฐ€๋Šฅํ•œ ์„œ๋ฒ„(SynologyยทAWS EC2ยทVPS ๋“ฑ)์— ์ž๋™ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.
2
+ # Go CI/CD deployment (SSH + Docker)
3
+ # Builds a Docker image when the {{MAIN_BRANCH}} branch is pushed and
4
+ # deploys it automatically to any server reachable over SSH (Synology, AWS EC2, VPS, ...).
5
5
  #
6
- # ํ•„์ˆ˜ GitHub Secrets
7
- # ENV_FILE: .env ํŒŒ์ผ ๋‚ด์šฉ
8
- # DOCKERHUB_USERNAME / DOCKERHUB_TOKEN: DockerHub ์‚ฌ์šฉ์ž๋ช… / ์•ก์„ธ์Šค ํ† ํฐ
9
- # SERVER_HOST / SERVER_USER: ๋ฐฐํฌ ์„œ๋ฒ„ ์ฃผ์†Œ / SSH ์‚ฌ์šฉ์ž๋ช…
10
- # SERVER_PASSWORD: SSH ๋น„๋ฐ€๋ฒˆํ˜ธ (SSH_AUTH_METHOD=password์ผ ๋•Œ)
11
- # SSH_KEY: SSH ๊ฐœ์ธํ‚ค .pem ๋‚ด์šฉ (SSH_AUTH_METHOD=key์ผ ๋•Œ, AWS EC2 ๋“ฑ)
6
+ # Required GitHub Secrets
7
+ # ENV_FILE: contents of the .env file
8
+ # DOCKERHUB_USERNAME / DOCKERHUB_TOKEN: DockerHub username / access token
9
+ # SERVER_HOST / SERVER_USER: deploy server address / SSH username
10
+ # SERVER_PASSWORD: SSH password (when SSH_AUTH_METHOD=password)
11
+ # SSH_KEY: contents of the SSH private key .pem (when SSH_AUTH_METHOD=key, e.g. AWS EC2)
12
12
  #
13
- # ํ™˜๊ฒฝ๋ณ€์ˆ˜ (env ์„น์…˜์—์„œ ์„ค์ •)
14
- # CONTAINER_INTERNAL_PORT: Go ์•ฑ ๋‚ด๋ถ€ ํฌํŠธ (๊ธฐ๋ณธ: 8080) / DEPLOY_PORT: ๋ฐฐํฌ ํฌํŠธ
15
- # CONTAINER_NAME: ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„ (๊ธฐ๋ณธ: PROJECT_NAME)
16
- # ENABLE_VOLUME_MOUNT (true/false), VOLUME_HOST_PATH, VOLUME_CONTAINER_PATH: ๋ณผ๋ฅจ ๋งˆ์šดํŠธ
17
- # SSH_PORT (๊ธฐ๋ณธ: 2022), SSH_COMMAND_TIMEOUT (๊ธฐ๋ณธ: 600s), SSH_CONNECTION_TIMEOUT (๊ธฐ๋ณธ: 60s)
18
- # HEALTHCHECK_WAIT_SECONDS: ์ดˆ๊ธฐํ™” ๋Œ€๊ธฐ ์‹œ๊ฐ„ (๊ธฐ๋ณธ: 10์ดˆ)
19
- # HEALTHCHECK_PATH: HTTP ์—”๋“œํฌ์ธํŠธ ๊ฒฝ๋กœ (๋น„์–ด์žˆ์œผ๋ฉด ๋กœ๊ทธ๋งŒ ์‚ฌ์šฉ, ์˜ˆ: "/health", "/api/v1/health")
20
- # HEALTHCHECK_MAX_RETRIES: HTTP ์žฌ์‹œ๋„ ํšŸ์ˆ˜ (๊ธฐ๋ณธ: 5ํšŒ) / HEALTHCHECK_RETRY_INTERVAL: ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ (๊ธฐ๋ณธ: 3์ดˆ)
21
- # HEALTHCHECK_LOG_PATTERN: Fallback ๋กœ๊ทธ ๊ฒ€์ƒ‰ ํŒจํ„ด
13
+ # Environment variables (set in the env section)
14
+ # CONTAINER_INTERNAL_PORT: Go app internal port (default: 8080) / DEPLOY_PORT: deploy port
15
+ # CONTAINER_NAME: container name (default: PROJECT_NAME)
16
+ # ENABLE_VOLUME_MOUNT (true/false), VOLUME_HOST_PATH, VOLUME_CONTAINER_PATH: volume mount
17
+ # SSH_PORT (default: 2022), SSH_COMMAND_TIMEOUT (default: 600s), SSH_CONNECTION_TIMEOUT (default: 60s)
18
+ # HEALTHCHECK_WAIT_SECONDS: initialization wait time (default: 10s)
19
+ # HEALTHCHECK_PATH: HTTP endpoint path (only logs are used when empty, e.g. "/health", "/api/v1/health")
20
+ # HEALTHCHECK_MAX_RETRIES: HTTP retry count (default: 5) / HEALTHCHECK_RETRY_INTERVAL: retry interval (default: 3s)
21
+ # HEALTHCHECK_LOG_PATTERN: fallback log search pattern
22
22
 
23
23
  name: PROJECT-GO-SIMPLE-CICD
24
24
 
25
25
  on:
26
26
  push:
27
27
  branches:
28
- - "{{MAIN_BRANCH}}" # ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ
29
- # @wizard paths-anchor (๋ชจ๋…ธ๋ ˆํฌ์ผ ๋•Œ integrator๊ฐ€ paths ํ•„ํ„ฐ๋ฅผ ์—ฌ๊ธฐ ์ฃผ์ž…)
30
- workflow_dispatch: # ์ˆ˜๋™ ์‹คํ–‰ ํ—ˆ์šฉ
28
+ - "{{MAIN_BRANCH}}" # production deploy
29
+ # @wizard paths-anchor (for a monorepo the integrator injects the paths filter here)
30
+ workflow_dispatch: # allow manual runs
31
31
 
32
32
  env:
33
- # ํ”„๋กœ์ ํŠธ ๊ณ ์œ  ์‹๋ณ„์ž
33
+ # Unique project identifier
34
34
  PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
35
- # ๋ชจ๋…ธ๋ ˆํฌ(--paths)์—์„œ ์ด ํ”„๋กœ์ ํŠธ๊ฐ€ ์žˆ๋Š” ํ•˜์œ„ ๊ฒฝ๋กœ. ๋นŒ๋“œยทDocker context์˜ ๊ธฐ์ค€ ('.'์ด๋ฉด ๋ ˆํฌ ๋ฃจํŠธ)
35
+ # Subdirectory of this project in a monorepo (--paths). Base for the build and Docker context ('.' means the repo root)
36
36
  PROJECT_PATH: "." # @wizard auto:project-path
37
- # ๋ชจ๋…ธ๋ ˆํฌ(PROJECT_PATH๊ฐ€ '.'์ด ์•„๋‹˜)์—์„œ๋Š” ์ด๋ฏธ์ง€ยท์ปจํ…Œ์ด๋„ˆยท๋„๋ฉ”์ธ ์ด๋ฆ„์— '-go'๋ฅผ ๋ถ™์—ฌ
38
- # ๊ฐ™์€ ๋ ˆํฌ์˜ ๋‹ค๋ฅธ ํƒ€์ž… ์„œ๋น„์Šค์™€ ์ด๋ฆ„์ด ๊ฒน์น˜์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค (์˜ˆ: my-app-go)
37
+ # In a monorepo (PROJECT_PATH is not '.') '-go' is appended to the image, container and domain names
38
+ # so they do not collide with services of other types in the same repo (e.g. my-app-go)
39
39
 
40
40
  DEPLOY_PORT: "__DEPLOY_PORT__" # @wizard ask:8080
41
41
 
42
- # ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„ ์„ค์ • (๋น„์›Œ๋‘๋ฉด PROJECT_NAME ์‚ฌ์šฉ)
42
+ # Container name (uses PROJECT_NAME when empty)
43
43
  CONTAINER_NAME: ""
44
44
 
45
- # ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ์„ค์ • (๋นˆ๊ฐ’์ด๋ฉด ๋งˆ์šดํŠธ ์•ˆํ•จ)
45
+ # Volume mount settings (no mount when empty)
46
46
  ENABLE_VOLUME_MOUNT: "false" # @wizard ask:false
47
47
  VOLUME_HOST_PATH: "/volume1/projects/__PROJECT_NAME__" # @wizard ask:/volume1/projects/__PROJECT_NAME__
48
48
  VOLUME_CONTAINER_PATH: "/mnt/__PROJECT_NAME__" # @wizard ask:/mnt/__PROJECT_NAME__
49
49
 
50
- # SSH ์ธ์ฆ ๋ฐฉ์‹: "password"(Synologyยท์ผ๋ฐ˜ ์„œ๋ฒ„) | "key"(AWS EC2ยท.pem)
51
- # password โ†’ SERVER_PASSWORD secret ์‚ฌ์šฉ
52
- # key โ†’ SSH_KEY secret ์‚ฌ์šฉ (.pem ํŒŒ์ผ ์ „์ฒด ๋‚ด์šฉ์„ secret ๊ฐ’์œผ๋กœ)
50
+ # SSH auth method: "password" (Synology, generic servers) | "key" (AWS EC2, .pem)
51
+ # password -> uses the SERVER_PASSWORD secret
52
+ # key -> uses the SSH_KEY secret (the whole .pem file content as the secret value)
53
53
  SSH_AUTH_METHOD: "password" # @wizard ask:password
54
54
 
55
55
  SSH_PORT: "__SSH_PORT__" # @wizard ask:2022
56
- SSH_COMMAND_TIMEOUT: "600s" # SSH ๋ช…๋ น ์‹คํ–‰ ํƒ€์ž„์•„์›ƒ
57
- SSH_CONNECTION_TIMEOUT: "60s" # SSH ์—ฐ๊ฒฐ ํƒ€์ž„์•„์›ƒ
56
+ SSH_COMMAND_TIMEOUT: "600s" # SSH command execution timeout
57
+ SSH_CONNECTION_TIMEOUT: "60s" # SSH connection timeout
58
58
 
59
- CONTAINER_INTERNAL_PORT: "8080" # Go ์•ฑ ๋‚ด๋ถ€ ํฌํŠธ
59
+ CONTAINER_INTERNAL_PORT: "8080" # Go app internal port
60
60
 
61
- # ํ—ฌ์Šค์ฒดํฌ ์„ค์ •
62
- HEALTHCHECK_WAIT_SECONDS: "10" # ์ปจํ…Œ์ด๋„ˆ ์ดˆ๊ธฐํ™” ๋Œ€๊ธฐ ์‹œ๊ฐ„ (์ดˆ)
63
- HEALTHCHECK_PATH: "/health" # HTTP ์—”๋“œํฌ์ธํŠธ ๊ฒฝ๋กœ (๋น„์–ด์žˆ์œผ๋ฉด ๋กœ๊ทธ ํŒจํ„ด๋งŒ ์‚ฌ์šฉ)
64
- HEALTHCHECK_MAX_RETRIES: "5" # HTTP ์žฌ์‹œ๋„ ํšŸ์ˆ˜
65
- HEALTHCHECK_RETRY_INTERVAL: "3" # ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ (์ดˆ)
66
- HEALTHCHECK_LOG_PATTERN: "listening on" # Fallback ๋กœ๊ทธ ๊ฒ€์ƒ‰ ํŒจํ„ด (ํ”„๋กœ์ ํŠธ ๋กœ๊ทธ ๋ฌธ๊ตฌ์— ๋งž๊ฒŒ ์กฐ์ •)
61
+ # Health check settings
62
+ HEALTHCHECK_WAIT_SECONDS: "10" # wait for container initialization (seconds)
63
+ HEALTHCHECK_PATH: "/health" # HTTP endpoint path (only the log pattern is used when empty)
64
+ HEALTHCHECK_MAX_RETRIES: "5" # HTTP retry count
65
+ HEALTHCHECK_RETRY_INTERVAL: "3" # retry interval (seconds)
66
+ HEALTHCHECK_LOG_PATTERN: "listening on" # Fallback log search pattern (adjust to your project's log wording)
67
67
 
68
68
  jobs:
69
- # Job: ๋นŒ๋“œ
69
+ # Job: build
70
70
  build:
71
- name: Go ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นŒ๋“œ
71
+ name: Build Go application
72
72
  runs-on: ubuntu-latest
73
73
 
74
74
  steps:
75
- - name: ์ฝ”๋“œ ์ฒดํฌ์•„์›ƒ
75
+ - name: Checkout repository
76
76
  uses: actions/checkout@v7
77
77
 
78
- # ํ•„์ˆ˜ SecretยทDockerfile์ด ์—†์œผ๋ฉด docker ๋กœ๊ทธ์ธ ๊ฐ™์€ ์„œ๋“œํŒŒํ‹ฐ ์˜ค๋ฅ˜ ๋Œ€์‹  ๋น ์ง„ ํ•ญ๋ชฉ์„ ์•Œ๋ ค ์ฃผ๊ณ  ๋ฉˆ์ถ˜๋‹ค
79
- - name: ๋ฐฐํฌ ์‚ฌ์ „ ์ ๊ฒ€ (SecretยทDockerfile)
78
+ # If a required Secret or the Dockerfile is missing, report the missing item and stop
79
+ # instead of failing later with a third-party error such as docker login
80
+ - name: Deploy pre-check (Secrets and Dockerfile)
80
81
  working-directory: ${{ env.PROJECT_PATH }}
81
82
  env:
82
83
  DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -87,11 +88,12 @@ jobs:
87
88
  SSH_KEY: ${{ secrets.SSH_KEY }}
88
89
  DOCKERFILE: Dockerfile
89
90
  run: |
91
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
90
92
  MISSING=""
91
93
  for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
92
94
  [ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
93
95
  done
94
- # SSH_AUTH_METHOD=key๋ฉด SSH_KEY, ์•„๋‹ˆ๋ฉด SERVER_PASSWORD๊ฐ€ ํ•„์š”ํ•˜๋‹ค
96
+ # SSH_AUTH_METHOD=key needs SSH_KEY, otherwise SERVER_PASSWORD is required
95
97
  if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
96
98
  [ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
97
99
  else
@@ -99,78 +101,95 @@ jobs:
99
101
  fi
100
102
  FAILED=false
101
103
  if [ -n "$MISSING" ]; then
102
- echo "::error title=ํ•„์ˆ˜ Secret ๋ˆ„๋ฝ::๋“ฑ๋ก๋˜์ง€ ์•Š์€ GitHub Secret:$MISSING โ€” Settings โ†’ Secrets and variables โ†’ Actions์—์„œ ๋“ฑ๋กํ•˜์„ธ์š”"
104
+ echo "::error title=$(m cicd.precheck_secret_title)::$(m cicd.precheck_secret_body missing="$MISSING")"
103
105
  FAILED=true
104
106
  fi
105
107
  if [ ! -f "$DOCKERFILE" ]; then
106
- echo "::error title=Dockerfile ์—†์Œ::${PROJECT_PATH}/${DOCKERFILE#./} ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด ์›Œํฌํ”Œ๋กœ์šฐ๋Š” Docker ์ด๋ฏธ์ง€๋กœ ๋ฐฐํฌํ•˜๋ฏ€๋กœ Dockerfile์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค (์„œ๋ฒ„ ๋ฐฐํฌ๋ฅผ ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ด ์›Œํฌํ”Œ๋กœ์šฐ ํŒŒ์ผ์„ ์‚ญ์ œํ•˜์„ธ์š”)"
108
+ echo "::error title=$(m cicd.precheck_dockerfile_title)::$(m cicd.precheck_dockerfile_body path="${PROJECT_PATH}/${DOCKERFILE#./}")"
107
109
  FAILED=true
108
110
  fi
109
111
  if [ "$FAILED" = "true" ]; then
110
112
  exit 1
111
113
  fi
112
- echo "โœ… ์‚ฌ์ „ ์ ๊ฒ€ ํ†ต๊ณผ: ํ•„์ˆ˜ Secret๊ณผ Dockerfile์ด ๋ชจ๋‘ ์žˆ์Šต๋‹ˆ๋‹ค"
114
+ m cicd.precheck_ok
113
115
 
114
- - name: ๋””์Šคํฌ ๊ณต๊ฐ„ ํ™•๋ณด
116
+ - name: Free up disk space
115
117
  run: |
116
- echo "๐Ÿ“Š ๋นŒ๋“œ ์ „ ๋””์Šคํฌ ์‚ฌ์šฉ๋Ÿ‰:"
118
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
119
+ m cicd.disk_before
117
120
  df -h
118
121
 
119
- echo "๐Ÿงน ๋ถˆํ•„์š”ํ•œ ํŒŒ์ผ ์ •๋ฆฌ ์ค‘..."
122
+ m cicd.disk_cleaning
120
123
  sudo rm -rf /usr/share/dotnet || true
121
124
  sudo rm -rf /opt/ghc || true
122
125
  sudo rm -rf /usr/local/share/boost || true
123
126
  sudo rm -rf "$AGENT_TOOLSDIRECTORY" || true
124
127
 
125
- echo "๐Ÿณ Docker ์‹œ์Šคํ…œ ์ •๋ฆฌ ์ค‘..."
128
+ m cicd.disk_docker_prune
126
129
  docker system prune -af --volumes || true
127
130
 
128
- echo "๐Ÿ“Š ์ •๋ฆฌ ํ›„ ๋””์Šคํฌ ์‚ฌ์šฉ๋Ÿ‰:"
131
+ m cicd.disk_after_cleanup
129
132
  df -h
130
133
 
131
- - name: .env ํŒŒ์ผ ์ƒ์„ฑ
134
+ - name: Create .env file
132
135
  working-directory: ${{ env.PROJECT_PATH }}
133
136
  run: |
137
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
134
138
  cat > .env << 'EOF'
135
139
  ${{ secrets.ENV_FILE }}
136
140
  EOF
137
- echo "โœ… .env ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค"
141
+ m cicd.dotenv_created
138
142
 
139
- - name: Docker ๋นŒ๋“œํ™˜๊ฒฝ ์„ค์ •
143
+ - name: Set up Docker Buildx
140
144
  uses: docker/setup-buildx-action@v3
141
145
 
142
- - name: DockerHub ๋กœ๊ทธ์ธ
146
+ - name: Log in to DockerHub
143
147
  uses: docker/login-action@v3
144
148
  with:
145
149
  username: ${{ secrets.DOCKERHUB_USERNAME }}
146
150
  password: ${{ secrets.DOCKERHUB_TOKEN }}
147
151
 
148
- - name: Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ ๋ฐ ํ‘ธ์‹œ
152
+ - name: Build and push Docker image
149
153
  uses: docker/build-push-action@v5
150
154
  with:
151
155
  context: ${{ env.PROJECT_PATH }}
152
156
  file: ${{ env.PROJECT_PATH }}/Dockerfile
153
157
  push: true
154
158
  tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}:${{ github.ref_name }}
155
- # GitHub Actions ์บ์‹œ ์‚ฌ์šฉ (๋กœ์ปฌ ๋””์Šคํฌ ๊ณต๊ฐ„ ์ ˆ์•ฝ)
159
+ # Use the GitHub Actions cache (saves local disk space)
156
160
  cache-from: type=gha
157
161
  cache-to: type=gha,mode=max
158
162
 
159
- - name: ๋นŒ๋“œ ํ›„ ์ •๋ฆฌ
163
+ - name: Clean up after build
160
164
  if: always()
161
165
  run: |
162
- echo "๐Ÿงน ๋นŒ๋“œ ํ›„ ์ •๋ฆฌ ์ค‘..."
166
+ m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
167
+ m cicd.post_build_cleanup
163
168
  docker system prune -f || true
164
- echo "๐Ÿ“Š ์ตœ์ข… ๋””์Šคํฌ ์‚ฌ์šฉ๋Ÿ‰:"
169
+ m cicd.disk_final
165
170
  df -h
166
171
 
167
- # Job: ๋ฐฐํฌ
172
+ # Job: deploy
168
173
  deploy:
169
- name: ์›๊ฒฉ ์„œ๋ฒ„ ๋ฐฐํฌ
174
+ name: Deploy to remote server
170
175
  needs: build
171
176
  runs-on: ubuntu-latest
172
177
 
173
178
  steps:
179
+ # The deploy script runs on the remote server, so the messages are resolved here
180
+ # and handed to it through env vars (PAW_M_*)
181
+ - name: Checkout message catalog
182
+ uses: actions/checkout@v7
183
+ with:
184
+ sparse-checkout: |
185
+ .github/scripts/messages.py
186
+ version.yml
187
+ sparse-checkout-cone-mode: false
188
+
189
+ - name: Load messages
190
+ run: |
191
+ python3 .github/scripts/messages.py dump cicd. | PYTHONIOENCODING=utf-8 python3 -c 'import json,sys; [print("PAW_M_" + k.replace(".", "_") + "=" + v) for k, v in json.load(sys.stdin).items()]' >> "$GITHUB_ENV"
192
+
174
193
  - name: Deploy
175
194
  uses: appleboy/ssh-action@v1.0.3
176
195
  env:
@@ -187,80 +206,83 @@ jobs:
187
206
  script: |
188
207
  set -e
189
208
 
190
- # ๋ฐฐํฌ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
191
- echo "๐Ÿ”ง ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •.."
209
+ # Fill the {name} placeholders of a message template (usage: m "template" name=value ...)
210
+ m() { local s="$1" kv; shift; for kv in "$@"; do s="${s//\{${kv%%=*}\}/${kv#*=}}"; done; printf '%s\n' "$s"; }
211
+
212
+ # Set deploy environment variables
213
+ m "${{ env.PAW_M_cicd_setup_env }}"
192
214
  export PATH=$PATH:/usr/local/bin
193
215
  export PW=${{ secrets.SERVER_PASSWORD }}
194
216
 
195
- # SSH ์ธ์ฆ ๋ฐฉ์‹์— ๋”ฐ๋ฅธ sudo ์ถ”์ƒํ™”
196
- # password ๋ชจ๋“œ: echo $PW | sudo -S (Synology ๋“ฑ sudo ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•„์š” ์„œ๋ฒ„)
197
- # key ๋ชจ๋“œ: sudo ์ง์ ‘ (AWS EC2 ๋“ฑ passwordless sudo ์„œ๋ฒ„)
217
+ # sudo abstraction per SSH auth method
218
+ # password mode: echo $PW | sudo -S (servers that need a sudo password, e.g. Synology)
219
+ # key mode: plain sudo (passwordless-sudo servers, e.g. AWS EC2)
198
220
  SSH_AUTH_METHOD="${SSH_AUTH_METHOD:-password}"
199
221
  if [ "${SSH_AUTH_METHOD}" = "key" ]; then
200
222
  SUDO() { sudo "$@"; }
201
223
  else
202
224
  SUDO() { echo "$PW" | sudo -S "$@"; }
203
225
  fi
204
- echo "๐Ÿ” SSH ์ธ์ฆ ๋ฐฉ์‹: ${SSH_AUTH_METHOD}"
226
+ m "${{ env.PAW_M_cicd_ssh_auth }}" method="${SSH_AUTH_METHOD}"
205
227
 
206
228
  BRANCH=${{ github.ref_name }}
207
229
 
208
230
  PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-go', env.PROJECT_NAME) || env.PROJECT_NAME }}"
209
231
 
210
- # ํฌํŠธ ๋ฐ ์ปจํ…Œ์ด๋„ˆ๋ช… ์„ค์ •
232
+ # Port and container name
211
233
  PORT=${{ env.DEPLOY_PORT }}
212
234
 
213
235
  if [ -z "${{ env.CONTAINER_NAME }}" ]; then
214
236
  CONTAINER_NAME="${PROJECT_NAME}"
215
- echo "โ„น๏ธ CONTAINER_NAME์ด ์„ค์ •๋˜์ง€ ์•Š์•„ PROJECT_NAME์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค: $CONTAINER_NAME"
237
+ m "${{ env.PAW_M_cicd_container_default }}" container="$CONTAINER_NAME"
216
238
  else
217
239
  CONTAINER_NAME="${{ env.CONTAINER_NAME }}"
218
- echo "โ„น๏ธ ์‚ฌ์šฉ์ž ์ง€์ • CONTAINER_NAME ์‚ฌ์šฉ: $CONTAINER_NAME"
240
+ m "${{ env.PAW_M_cicd_container_custom }}" container="$CONTAINER_NAME"
219
241
  fi
220
242
 
221
- echo "๐Ÿš€ ๋ฐฐํฌ ํ™˜๊ฒฝ์œผ๋กœ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค (๋ธŒ๋žœ์น˜: $BRANCH)"
243
+ m "${{ env.PAW_M_cicd_deploy_start }}" branch="$BRANCH"
222
244
 
223
- echo "๐Ÿ“‹ ๋ฐฐํฌ ์„ค์ • ์ •๋ณด:"
224
- echo " - ๋ธŒ๋žœ์น˜: $BRANCH"
225
- echo " - ํ”„๋กœ์ ํŠธ: $PROJECT_NAME"
226
- echo " - ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„: $CONTAINER_NAME"
227
- echo " - ํฌํŠธ: $PORT"
228
- echo " - Docker ์ด๋ฏธ์ง€: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
245
+ m "${{ env.PAW_M_cicd_config_title }}"
246
+ m "${{ env.PAW_M_cicd_config_branch }}" branch="$BRANCH"
247
+ m "${{ env.PAW_M_cicd_config_project }}" project="$PROJECT_NAME"
248
+ m "${{ env.PAW_M_cicd_config_container }}" container="$CONTAINER_NAME"
249
+ m "${{ env.PAW_M_cicd_config_port }}" port="$PORT"
250
+ m "${{ env.PAW_M_cicd_config_image }}" image="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
229
251
 
230
- # Docker ์ด๋ฏธ์ง€ ํ’€
231
- echo "โฌ‡๏ธ Docker ์ด๋ฏธ์ง€ ํ’€: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
252
+ # Pull the Docker image
253
+ m "${{ env.PAW_M_cicd_pull_image }}" image="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
232
254
  SUDO docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
233
255
 
234
- # ๊ธฐ์กด ์ปจํ…Œ์ด๋„ˆ ์ •๋ฆฌ
235
- echo "๐Ÿงน ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ ์ค‘..."
256
+ # Clean up the existing container
257
+ m "${{ env.PAW_M_cicd_container_check }}" container="$CONTAINER_NAME"
236
258
 
237
259
  if SUDO docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
238
- echo "โš ๏ธ ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์ด(๊ฐ€) ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ค‘์ง€ ๋ฐ ์‚ญ์ œ ์ค‘..."
260
+ m "${{ env.PAW_M_cicd_container_exists }}" container="$CONTAINER_NAME"
239
261
  SUDO docker rm -f $CONTAINER_NAME
240
- echo "โœ… ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์ด(๊ฐ€) ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
262
+ m "${{ env.PAW_M_cicd_container_removed }}" container="$CONTAINER_NAME"
241
263
  else
242
- echo "โ„น๏ธ ์กด์žฌํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์ด(๊ฐ€) ์—†์Šต๋‹ˆ๋‹ค."
264
+ m "${{ env.PAW_M_cicd_container_none }}" container="$CONTAINER_NAME"
243
265
  fi
244
266
 
245
- # ์ƒˆ ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰
246
- echo "๐Ÿš€ ์ƒˆ๋กœ์šด ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์‹คํ–‰ ์ค‘..."
267
+ # Start the new container
268
+ m "${{ env.PAW_M_cicd_container_run }}" container="$CONTAINER_NAME"
247
269
 
248
- # - ํฌํŠธ ๋งคํ•‘: ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }} (์™ธ๋ถ€:๋‚ด๋ถ€)
249
- # - ์‹œ๊ฐ„๋Œ€: Asia/Seoul
250
- # - ๋ณผ๋ฅจ ๋งˆ์šดํŠธ: ๋กœ์ปฌ ์‹œ๊ฐ„ ๋™๊ธฐํ™” ๋ฐ ํ”„๋กœ์ ํŠธ ๋ฐ์ดํ„ฐ
251
- # .env ํŒŒ์ผ์€ ์ด๋ฏธ Docker ์ด๋ฏธ์ง€์— ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค
270
+ # - Port mapping: ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }} (external:internal)
271
+ # - Time zone: Asia/Seoul
272
+ # - Volume mount: local time sync and project data
273
+ # The .env file is already baked into the Docker image
252
274
 
253
- # ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ์˜ต์…˜ ๊ตฌ์„ฑ
275
+ # Build the volume mount options
254
276
  VOLUME_OPTS="-v /etc/localtime:/etc/localtime:ro"
255
277
  if [ "${{ env.ENABLE_VOLUME_MOUNT }}" == "true" ]; then
256
278
  if [ ! -d "${{ env.VOLUME_HOST_PATH }}" ]; then
257
- echo "๐Ÿ“ ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ: ${{ env.VOLUME_HOST_PATH }}"
279
+ m "${{ env.PAW_M_cicd_volume_mkdir }}" path="${{ env.VOLUME_HOST_PATH }}"
258
280
  SUDO mkdir -p "${{ env.VOLUME_HOST_PATH }}"
259
281
  fi
260
282
  VOLUME_OPTS="$VOLUME_OPTS -v ${{ env.VOLUME_HOST_PATH }}:${{ env.VOLUME_CONTAINER_PATH }}"
261
- echo "๐Ÿ“ฆ ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ํ™œ์„ฑํ™”: $VOLUME_OPTS"
283
+ m "${{ env.PAW_M_cicd_volume_on }}" opts="$VOLUME_OPTS"
262
284
  else
263
- echo "โ„น๏ธ ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ๋น„ํ™œ์„ฑํ™”๋จ"
285
+ m "${{ env.PAW_M_cicd_volume_off }}"
264
286
  fi
265
287
 
266
288
  SUDO docker run -d \
@@ -270,85 +292,85 @@ jobs:
270
292
  $VOLUME_OPTS \
271
293
  ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
272
294
 
273
- # ์ปจํ…Œ์ด๋„ˆ ํ—ฌ์Šค์ฒดํฌ
274
- echo "โณ ์ปจํ…Œ์ด๋„ˆ ์ดˆ๊ธฐํ™” ๋Œ€๊ธฐ ์ค‘... (${{ env.HEALTHCHECK_WAIT_SECONDS }}์ดˆ)"
295
+ # Container health check
296
+ m "${{ env.PAW_M_cicd_health_wait }}" seconds="${{ env.HEALTHCHECK_WAIT_SECONDS }}"
275
297
  sleep ${{ env.HEALTHCHECK_WAIT_SECONDS }}
276
298
 
277
- # 1๋‹จ๊ณ„: ์ปจํ…Œ์ด๋„ˆ ํ”„๋กœ์„ธ์Šค ํ™•์ธ
278
- echo "๐Ÿฅ ์ปจํ…Œ์ด๋„ˆ ํ—ฌ์Šค์ฒดํฌ ์ค‘..."
299
+ # Step 1: check the container process
300
+ m "${{ env.PAW_M_cicd_health_check }}"
279
301
  if ! SUDO docker ps --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
280
- echo "โŒ ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค"
302
+ m "${{ env.PAW_M_cicd_health_no_process }}" container="$CONTAINER_NAME"
281
303
  SUDO docker logs --tail 50 $CONTAINER_NAME || true
282
304
  exit 1
283
305
  fi
284
- echo "โœ… ์ปจํ…Œ์ด๋„ˆ ํ”„๋กœ์„ธ์Šค ์‹คํ–‰ ํ™•์ธ ์™„๋ฃŒ"
306
+ m "${{ env.PAW_M_cicd_health_process_ok }}"
285
307
 
286
- # 2๋‹จ๊ณ„: HTTP ์—”๋“œํฌ์ธํŠธ ์ฒดํฌ (์„ค์ •๋œ ๊ฒฝ์šฐ)
308
+ # Step 2: check the HTTP endpoint (when configured)
287
309
  HEALTHCHECK_SUCCESS=false
288
310
  if [ -n "${{ env.HEALTHCHECK_PATH }}" ]; then
289
- echo "๐ŸŒ HTTP ํ—ฌ์Šค์ฒดํฌ ์‹œ์ž‘: http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }}"
311
+ m "${{ env.PAW_M_cicd_http_start }}" url="http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }}"
290
312
 
291
313
  for i in $(seq 1 ${{ env.HEALTHCHECK_MAX_RETRIES }}); do
292
- echo " ์‹œ๋„ $i/${{ env.HEALTHCHECK_MAX_RETRIES }}..."
314
+ m "${{ env.PAW_M_cicd_http_try }}" i="$i" max="${{ env.HEALTHCHECK_MAX_RETRIES }}"
293
315
 
294
316
  HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }} || echo "000")
295
317
 
296
318
  if [ "$HTTP_CODE" = "200" ]; then
297
- echo "โœ… HTTP ํ—ฌ์Šค์ฒดํฌ ์„ฑ๊ณต (์‘๋‹ต ์ฝ”๋“œ: $HTTP_CODE)"
319
+ m "${{ env.PAW_M_cicd_http_ok }}" code="$HTTP_CODE"
298
320
  HEALTHCHECK_SUCCESS=true
299
321
  break
300
322
  else
301
- echo "โš ๏ธ HTTP ํ—ฌ์Šค์ฒดํฌ ์‹คํŒจ (์‘๋‹ต ์ฝ”๋“œ: $HTTP_CODE)"
323
+ m "${{ env.PAW_M_cicd_http_fail }}" code="$HTTP_CODE"
302
324
  if [ $i -lt ${{ env.HEALTHCHECK_MAX_RETRIES }} ]; then
303
- echo " ${{ env.HEALTHCHECK_RETRY_INTERVAL }}์ดˆ ํ›„ ์žฌ์‹œ๋„..."
325
+ m "${{ env.PAW_M_cicd_http_retry }}" interval="${{ env.HEALTHCHECK_RETRY_INTERVAL }}"
304
326
  sleep ${{ env.HEALTHCHECK_RETRY_INTERVAL }}
305
327
  fi
306
328
  fi
307
329
  done
308
330
 
309
- # HTTP ์ฒดํฌ ์‹คํŒจ ์‹œ fallback์œผ๋กœ ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ
331
+ # If the HTTP check fails, fall back to log pattern matching
310
332
  if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
311
- echo "โš ๏ธ HTTP ํ—ฌ์Šค์ฒดํฌ ์ตœ์ข… ์‹คํŒจ, ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ์œผ๋กœ fallback..."
333
+ m "${{ env.PAW_M_cicd_http_final_fail }}"
312
334
  fi
313
335
  else
314
- echo "โ„น๏ธ HEALTHCHECK_PATH๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•„ ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค"
336
+ m "${{ env.PAW_M_cicd_http_skipped }}"
315
337
  fi
316
338
 
317
- # 3๋‹จ๊ณ„: ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ (HTTP ์‹คํŒจ ์‹œ ๋˜๋Š” HTTP ์ฒดํฌ ๋น„ํ™œ์„ฑํ™” ์‹œ)
339
+ # Step 3: log pattern matching (when HTTP failed or the HTTP check is disabled)
318
340
  if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
319
- echo "๐Ÿ“‹ ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ ์‹œ์ž‘: '${{ env.HEALTHCHECK_LOG_PATTERN }}'"
341
+ m "${{ env.PAW_M_cicd_log_start }}" pattern="${{ env.HEALTHCHECK_LOG_PATTERN }}"
320
342
 
321
343
  if SUDO docker logs $CONTAINER_NAME 2>&1 | grep -q "${{ env.HEALTHCHECK_LOG_PATTERN }}"; then
322
- echo "โœ… ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ ์„ฑ๊ณต: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ •์ƒ์ ์œผ๋กœ ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค"
344
+ m "${{ env.PAW_M_cicd_log_ok }}"
323
345
  HEALTHCHECK_SUCCESS=true
324
346
  else
325
- echo "โŒ ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ ์‹คํŒจ: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ๋กœ๊ทธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"
347
+ m "${{ env.PAW_M_cicd_log_fail }}"
326
348
  echo ""
327
- echo "๐Ÿ“‹ ๋””๋ฒ„๊น… ์ •๋ณด:"
328
- echo " - ๊ฒ€์ƒ‰ ํŒจํ„ด: ${{ env.HEALTHCHECK_LOG_PATTERN }}"
329
- echo " - ์ปจํ…Œ์ด๋„ˆ: $CONTAINER_NAME"
330
- echo " - ์ด๋ฏธ์ง€: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
331
- echo " - ํฌํŠธ: ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }}"
332
- echo " - ๋ณผ๋ฅจ: $VOLUME_OPTS"
349
+ m "${{ env.PAW_M_cicd_debug_title }}"
350
+ m "${{ env.PAW_M_cicd_debug_pattern }}" pattern="${{ env.HEALTHCHECK_LOG_PATTERN }}"
351
+ m "${{ env.PAW_M_cicd_debug_container }}" container="$CONTAINER_NAME"
352
+ m "${{ env.PAW_M_cicd_debug_image }}" image="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
353
+ m "${{ env.PAW_M_cicd_debug_port }}" port="${PORT}:${{ env.CONTAINER_INTERNAL_PORT }}"
354
+ m "${{ env.PAW_M_cicd_debug_volume }}" volume="$VOLUME_OPTS"
333
355
  echo ""
334
- echo "๐Ÿ“‹ ์ปจํ…Œ์ด๋„ˆ ๋กœ๊ทธ (์ตœ๊ทผ 50์ค„):"
356
+ m "${{ env.PAW_M_cicd_debug_logs }}"
335
357
  SUDO docker logs --tail 50 $CONTAINER_NAME || true
336
358
  exit 1
337
359
  fi
338
360
  fi
339
361
 
340
362
  if [ "$HEALTHCHECK_SUCCESS" = "true" ]; then
341
- echo "โœ… ํ—ฌ์Šค์ฒดํฌ ์™„๋ฃŒ: ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์ด(๊ฐ€) ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค"
363
+ m "${{ env.PAW_M_cicd_health_done }}" container="$CONTAINER_NAME"
342
364
  fi
343
365
 
344
- # ๋ฐฐํฌ ์™„๋ฃŒ ํ™•์ธ
345
- echo "โœ… ๋ฐฐํฌ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!"
366
+ # Confirm the deployment
367
+ m "${{ env.PAW_M_cicd_deploy_done }}"
346
368
  echo ""
347
- echo "๐Ÿ“‹ ๋ฐฐํฌ ๊ฒฐ๊ณผ ์š”์•ฝ:"
348
- echo " ๐ŸŽฏ ํ”„๋กœ์ ํŠธ: $PROJECT_NAME"
349
- echo " ๐ŸŒฟ ๋ธŒ๋žœ์น˜: $BRANCH"
350
- echo " ๐Ÿณ ์ปจํ…Œ์ด๋„ˆ: $CONTAINER_NAME"
351
- echo " ๐ŸŒ ํฌํŠธ: $PORT"
352
- echo " โฐ ๋ฐฐํฌ ์‹œ๊ฐ„: $(date '+%Y-%m-%d %H:%M:%S')"
369
+ m "${{ env.PAW_M_cicd_summary_title }}"
370
+ m "${{ env.PAW_M_cicd_summary_project }}" project="$PROJECT_NAME"
371
+ m "${{ env.PAW_M_cicd_summary_branch }}" branch="$BRANCH"
372
+ m "${{ env.PAW_M_cicd_summary_container }}" container="$CONTAINER_NAME"
373
+ m "${{ env.PAW_M_cicd_summary_port }}" port="$PORT"
374
+ m "${{ env.PAW_M_cicd_summary_time }}" time="$(date '+%Y-%m-%d %H:%M:%S')"
353
375
  echo ""
354
- echo "๐Ÿ”— ์ ‘์† URL: http://${{ secrets.SERVER_HOST }}:${PORT}"
376
+ m "${{ env.PAW_M_cicd_summary_url }}" url="http://${{ secrets.SERVER_HOST }}:${PORT}"