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,127 +1,127 @@
1
1
  # project-auto-wizard:managed-workflow
2
- # Spring Boot CI/CD ๋ฐฐํฌ (SSH + Docker)
2
+ # Spring Boot CI/CD deploy (SSH + Docker)
3
3
  #
4
- # deploy ๋ธŒ๋žœ์น˜ ํ‘ธ์‹œ ์‹œ Docker ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ณ 
5
- # SSH๋กœ ์ ‘์† ๊ฐ€๋Šฅํ•œ ์„œ๋ฒ„(SynologyยทAWS EC2ยทVPS ๋“ฑ)์— ์ž๋™ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.
4
+ # On a push to the deploy branch, builds a Docker image and
5
+ # deploys it automatically to any server reachable over SSH (Synology, AWS EC2, VPS, ...).
6
6
  #
7
- # ํ•„์ˆ˜ GitHub Secrets
8
- # APPLICATION_PROD_YML (์„ ํƒ): application-prod.yml ๋‚ด์šฉ
9
- # DOCKERHUB_USERNAME: DockerHub ์‚ฌ์šฉ์ž๋ช…
10
- # DOCKERHUB_TOKEN: DockerHub ์•ก์„ธ์Šค ํ† ํฐ
11
- # SERVER_HOST: ๋ฐฐํฌ ์„œ๋ฒ„ ์ฃผ์†Œ
12
- # SERVER_USER: SSH ์‚ฌ์šฉ์ž๋ช…
13
- # SERVER_PASSWORD: SSH ๋น„๋ฐ€๋ฒˆํ˜ธ (SSH_AUTH_METHOD=password์ผ ๋•Œ)
14
- # SSH_KEY: SSH ๊ฐœ์ธํ‚ค .pem ๋‚ด์šฉ (SSH_AUTH_METHOD=key์ผ ๋•Œ, AWS EC2 ๋“ฑ)
7
+ # Required GitHub Secrets
8
+ # APPLICATION_PROD_YML (optional): contents of application-prod.yml
9
+ # DOCKERHUB_USERNAME: DockerHub username
10
+ # DOCKERHUB_TOKEN: DockerHub access token
11
+ # SERVER_HOST: deployment server address
12
+ # SERVER_USER: SSH username
13
+ # SERVER_PASSWORD: SSH password (when SSH_AUTH_METHOD=password)
14
+ # SSH_KEY: contents of the SSH private key .pem (when SSH_AUTH_METHOD=key, e.g. AWS EC2)
15
15
  #
16
- # ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ • (env ์„น์…˜์—์„œ ์„ค์ •)
16
+ # Environment variables (set in the env section)
17
17
  #
18
- # ํฌํŠธ ๋ฐ ์ปจํ…Œ์ด๋„ˆ:
19
- # CONTAINER_INTERNAL_PORT: Spring Boot ๋‚ด๋ถ€ ํฌํŠธ (๊ธฐ๋ณธ: 8080)
20
- # DEPLOY_PORT: ๋ฐฐํฌ ํฌํŠธ
21
- # CONTAINER_NAME: ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„ (๊ธฐ๋ณธ: PROJECT_NAME)
18
+ # Ports and container:
19
+ # CONTAINER_INTERNAL_PORT: Spring Boot internal port (default: 8080)
20
+ # DEPLOY_PORT: deploy port
21
+ # CONTAINER_NAME: container name (default: PROJECT_NAME)
22
22
  #
23
- # ๋ณผ๋ฅจ ๋งˆ์šดํŠธ:
24
- # ENABLE_VOLUME_MOUNT: ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ํ™œ์„ฑํ™” (true/false)
25
- # VOLUME_HOST_PATH: ํ˜ธ์ŠคํŠธ ๋ณผ๋ฅจ ๊ฒฝ๋กœ
26
- # VOLUME_CONTAINER_PATH: ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ ๋งˆ์šดํŠธ ๊ฒฝ๋กœ
23
+ # Volume mount:
24
+ # ENABLE_VOLUME_MOUNT: enable the volume mount (true/false)
25
+ # VOLUME_HOST_PATH: host volume path
26
+ # VOLUME_CONTAINER_PATH: mount path inside the container
27
27
  #
28
- # SSH ์—ฐ๊ฒฐ:
29
- # SSH_PORT: SSH ํฌํŠธ (๊ธฐ๋ณธ: 2022)
30
- # SSH_COMMAND_TIMEOUT: SSH ๋ช…๋ น ํƒ€์ž„์•„์›ƒ (๊ธฐ๋ณธ: 600s)
31
- # SSH_CONNECTION_TIMEOUT: SSH ์—ฐ๊ฒฐ ํƒ€์ž„์•„์›ƒ (๊ธฐ๋ณธ: 60s)
28
+ # SSH connection:
29
+ # SSH_PORT: SSH port (default: 2022)
30
+ # SSH_COMMAND_TIMEOUT: SSH command timeout (default: 600s)
31
+ # SSH_CONNECTION_TIMEOUT: SSH connection timeout (default: 60s)
32
32
  #
33
- # ํ—ฌ์Šค์ฒดํฌ:
34
- # HEALTHCHECK_WAIT_SECONDS: ์ดˆ๊ธฐํ™” ๋Œ€๊ธฐ ์‹œ๊ฐ„ (๊ธฐ๋ณธ: 10์ดˆ)
35
- # HEALTHCHECK_PATH: HTTP ์—”๋“œํฌ์ธํŠธ ๊ฒฝ๋กœ (๋น„์–ด์žˆ์œผ๋ฉด ๋กœ๊ทธ๋งŒ ์‚ฌ์šฉ)
36
- # HEALTHCHECK_MAX_RETRIES: HTTP ์žฌ์‹œ๋„ ํšŸ์ˆ˜ (๊ธฐ๋ณธ: 5ํšŒ)
37
- # HEALTHCHECK_RETRY_INTERVAL: ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ (๊ธฐ๋ณธ: 3์ดˆ)
38
- # HEALTHCHECK_LOG_PATTERN: Fallback ๋กœ๊ทธ ๊ฒ€์ƒ‰ ํŒจํ„ด
33
+ # Health check:
34
+ # HEALTHCHECK_WAIT_SECONDS: initialization wait time (default: 10s)
35
+ # HEALTHCHECK_PATH: HTTP endpoint path (log pattern only if empty)
36
+ # HEALTHCHECK_MAX_RETRIES: HTTP retry count (default: 5)
37
+ # HEALTHCHECK_RETRY_INTERVAL: retry interval (default: 3s)
38
+ # HEALTHCHECK_LOG_PATTERN: fallback log search pattern
39
39
  #
40
- # HEALTHCHECK_PATH ์˜ˆ์‹œ:
40
+ # HEALTHCHECK_PATH examples:
41
41
  # Spring Boot (Actuator): "/actuator/health"
42
- # ์ปค์Šคํ…€: "/api/v1/health"
42
+ # Custom: "/api/v1/health"
43
43
  #
44
- # HEALTHCHECK_LOG_PATTERN ์˜ˆ์‹œ:
44
+ # HEALTHCHECK_LOG_PATTERN example:
45
45
  # Spring Boot: "Tomcat started on port"
46
46
  #
47
- # ๋ธŒ๋žœ์น˜๋ณ„ ๋ฐฐํฌ:
48
- # - deploy ๋ธŒ๋žœ์น˜: ์ž๋™ ๋ฐฐํฌ (DEPLOY_PORT ์‚ฌ์šฉ)
49
- # - {{MAIN_BRANCH}} ๋ธŒ๋žœ์น˜: ์ˆ˜๋™ ์‹คํ–‰๋งŒ ๊ฐ€๋Šฅ
47
+ # Per-branch deploy:
48
+ # - deploy branch: automatic deploy (uses DEPLOY_PORT)
49
+ # - {{MAIN_BRANCH}} branch: manual runs only
50
50
 
51
51
  name: PROJECT-SPRING-SIMPLE-CICD
52
52
 
53
- # ํŠธ๋ฆฌ๊ฑฐ ์„ค์ •
54
- # deploy ๋ธŒ๋žœ์น˜์— pushํ•  ๋•Œ ์ž๋™์œผ๋กœ CI/CD๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
55
- # {{MAIN_BRANCH}} ๋ธŒ๋žœ์น˜๋Š” workflow_dispatch๋กœ ์ˆ˜๋™ ์‹คํ–‰๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
53
+ # Trigger settings
54
+ # CI/CD runs automatically on a push to the deploy branch.
55
+ # The {{MAIN_BRANCH}} branch can only be run manually via workflow_dispatch.
56
56
  on:
57
57
  push:
58
58
  branches:
59
- - "{{MAIN_BRANCH}}" # ๋ฐฐํฌ ํ™˜๊ฒฝ (DEPLOY_PORT ์‚ฌ์šฉ)
60
- # @wizard paths-anchor (๋ชจ๋…ธ๋ ˆํฌ์ผ ๋•Œ integrator๊ฐ€ paths ํ•„ํ„ฐ๋ฅผ ์—ฌ๊ธฐ ์ฃผ์ž…)
61
- workflow_dispatch: # ์ˆ˜๋™ ์‹คํ–‰ ํ—ˆ์šฉ
59
+ - "{{MAIN_BRANCH}}" # deploy environment (uses DEPLOY_PORT)
60
+ # @wizard paths-anchor (in a monorepo the integrator injects the paths filter here)
61
+ workflow_dispatch: # allow manual runs
62
62
 
63
63
  env:
64
- # ํ”„๋กœ์ ํŠธ ์„ค์ • - ์‹ค์ œ ํ”„๋กœ์ ํŠธ๋ช…์œผ๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”
64
+ # Project settings - change to your actual project name
65
65
  PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
66
- # ๋ชจ๋…ธ๋ ˆํฌ(--paths)์—์„œ ์ด ํ”„๋กœ์ ํŠธ๊ฐ€ ์žˆ๋Š” ํ•˜์œ„ ๊ฒฝ๋กœ. ๋นŒ๋“œยทDocker context์˜ ๊ธฐ์ค€ ('.'์ด๋ฉด ๋ ˆํฌ ๋ฃจํŠธ)
66
+ # Subdirectory of this project in a monorepo (--paths). Base for the build and Docker context ('.' = repo root)
67
67
  PROJECT_PATH: "." # @wizard auto:project-path
68
- # ๋ชจ๋…ธ๋ ˆํฌ(PROJECT_PATH๊ฐ€ '.'์ด ์•„๋‹˜)์—์„œ๋Š” ์ด๋ฏธ์ง€ยท์ปจํ…Œ์ด๋„ˆยท๋„๋ฉ”์ธ ์ด๋ฆ„์— '-spring'๋ฅผ ๋ถ™์—ฌ
69
- # ๊ฐ™์€ ๋ ˆํฌ์˜ ๋‹ค๋ฅธ ํƒ€์ž… ์„œ๋น„์Šค์™€ ์ด๋ฆ„์ด ๊ฒน์น˜์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค (์˜ˆ: my-app-spring)
68
+ # In a monorepo (PROJECT_PATH is not '.') '-spring' is appended to image, container and domain names
69
+ # so they do not collide with services of other types in the same repo (e.g. my-app-spring)
70
70
 
71
- # Spring Boot ์„ค์ •
71
+ # Spring Boot settings
72
72
  SPRING_PROFILE: "prod"
73
73
  JAVA_VERSION: "__JAVA_VERSION__" # @wizard ask:@jdk
74
74
  GRADLE_OPTS: "-Dspring.profiles.active=prod"
75
75
 
76
- # ๊ฒฝ๋กœ ์„ค์ •
76
+ # Path settings
77
77
  APPLICATION_YML_DIR: "__APPLICATION_YML_DIR__" # @wizard auto:spring-app-yml-dir
78
- DOCKERFILE_PATH: "./Dockerfile" # PROJECT_PATH ๊ธฐ์ค€ ์ƒ๋Œ€ ๊ฒฝ๋กœ
78
+ DOCKERFILE_PATH: "./Dockerfile" # relative to PROJECT_PATH
79
79
 
80
- # ํฌํŠธ ์„ค์ •
81
- CONTAINER_INTERNAL_PORT: "8080" # Spring Boot ๋‚ด๋ถ€ ํฌํŠธ (๊ณ ์ •๊ฐ’)
80
+ # Port settings
81
+ CONTAINER_INTERNAL_PORT: "8080" # Spring Boot internal port (fixed)
82
82
  DEPLOY_PORT: "__DEPLOY_PORT__" # @wizard ask:8080
83
83
 
84
- # ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„ ์„ค์ •
85
- # ๋น„์›Œ๋‘๋ฉด PROJECT_NAME์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ (์˜ˆ: mapsy-back)
86
- # ์ปค์Šคํ…€ ์ด๋ฆ„์„ ์›ํ•˜๋ฉด ์ง์ ‘ ์ž…๋ ฅ (์˜ˆ: "my-custom-container")
84
+ # Container name
85
+ # If empty, PROJECT_NAME is used (e.g. mapsy-back)
86
+ # Enter a custom name to override (e.g. "my-custom-container")
87
87
  CONTAINER_NAME: ""
88
88
 
89
- # ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ์„ค์ •
90
- # true: ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ํ™œ์„ฑํ™” (๋””๋ ‰ํ† ๋ฆฌ ์—†์œผ๋ฉด ์ž๋™ ์ƒ์„ฑ)
91
- # false: ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ๋น„ํ™œ์„ฑํ™” (๊ธฐ๋ณธ๊ฐ’)
89
+ # Volume mount settings
90
+ # true: enable the volume mount (the directory is created if missing)
91
+ # false: disable the volume mount (default)
92
92
  ENABLE_VOLUME_MOUNT: "false" # @wizard ask:false
93
93
  VOLUME_HOST_PATH: "/volume1/projects/__PROJECT_NAME__" # @wizard ask:/volume1/projects/__PROJECT_NAME__
94
94
  VOLUME_CONTAINER_PATH: "/mnt/__PROJECT_NAME__" # @wizard ask:/mnt/__PROJECT_NAME__
95
95
 
96
- # SSH ์ธ์ฆ ๋ฐฉ์‹: "password"(Synologyยท์ผ๋ฐ˜ ์„œ๋ฒ„) | "key"(AWS EC2ยท.pem)
97
- # password โ†’ SERVER_PASSWORD secret ์‚ฌ์šฉ
98
- # key โ†’ SSH_KEY secret ์‚ฌ์šฉ (.pem ํŒŒ์ผ ์ „์ฒด ๋‚ด์šฉ์„ secret ๊ฐ’์œผ๋กœ)
96
+ # SSH auth method: "password" (Synology / generic servers) | "key" (AWS EC2 / .pem)
97
+ # password -> uses the SERVER_PASSWORD secret
98
+ # key -> uses the SSH_KEY secret (the full .pem contents as the secret value)
99
99
  SSH_AUTH_METHOD: "password" # @wizard ask:password
100
100
 
101
- # SSH ์—ฐ๊ฒฐ ์„ค์ •
101
+ # SSH connection settings
102
102
  SSH_PORT: "__SSH_PORT__" # @wizard ask:2022
103
- SSH_COMMAND_TIMEOUT: "600s" # SSH ๋ช…๋ น ์‹คํ–‰ ํƒ€์ž„์•„์›ƒ
104
- SSH_CONNECTION_TIMEOUT: "60s" # SSH ์—ฐ๊ฒฐ ํƒ€์ž„์•„์›ƒ
103
+ SSH_COMMAND_TIMEOUT: "600s" # SSH command execution timeout
104
+ SSH_CONNECTION_TIMEOUT: "60s" # SSH connection timeout
105
105
 
106
- # ํ—ฌ์Šค์ฒดํฌ ์„ค์ •
107
- HEALTHCHECK_WAIT_SECONDS: "10" # ์ปจํ…Œ์ด๋„ˆ ์ดˆ๊ธฐํ™” ๋Œ€๊ธฐ ์‹œ๊ฐ„ (์ดˆ)
108
- HEALTHCHECK_PATH: "/actuator/health" # HTTP ์—”๋“œํฌ์ธํŠธ ๊ฒฝ๋กœ (๋น„์–ด์žˆ์œผ๋ฉด ๋กœ๊ทธ ํŒจํ„ด๋งŒ ์‚ฌ์šฉ)
109
- HEALTHCHECK_MAX_RETRIES: "5" # HTTP ์žฌ์‹œ๋„ ํšŸ์ˆ˜
110
- HEALTHCHECK_RETRY_INTERVAL: "3" # ์žฌ์‹œ๋„ ๊ฐ„๊ฒฉ (์ดˆ)
111
- HEALTHCHECK_LOG_PATTERN: "Tomcat started on port" # Fallback ๋กœ๊ทธ ๊ฒ€์ƒ‰ ํŒจํ„ด
106
+ # Health check settings
107
+ HEALTHCHECK_WAIT_SECONDS: "10" # container initialization wait (seconds)
108
+ HEALTHCHECK_PATH: "/actuator/health" # HTTP endpoint path (log pattern only if empty)
109
+ HEALTHCHECK_MAX_RETRIES: "5" # HTTP retry count
110
+ HEALTHCHECK_RETRY_INTERVAL: "3" # retry interval (seconds)
111
+ HEALTHCHECK_LOG_PATTERN: "Tomcat started on port" # fallback log search pattern
112
112
 
113
113
  jobs:
114
- # ๋นŒ๋“œ ์ž‘์—…
114
+ # Build job
115
115
  build:
116
- name: Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นŒ๋“œ
116
+ name: Build Spring Boot application
117
117
  runs-on: ubuntu-latest
118
118
 
119
119
  steps:
120
- - name: ์ฝ”๋“œ ์ฒดํฌ์•„์›ƒ
120
+ - name: Checkout code
121
121
  uses: actions/checkout@v7
122
122
 
123
- # ํ•„์ˆ˜ SecretยทDockerfile์ด ์—†์œผ๋ฉด docker ๋กœ๊ทธ์ธ ๊ฐ™์€ ์„œ๋“œํŒŒํ‹ฐ ์˜ค๋ฅ˜ ๋Œ€์‹  ๋น ์ง„ ํ•ญ๋ชฉ์„ ์•Œ๋ ค ์ฃผ๊ณ  ๋ฉˆ์ถ˜๋‹ค
124
- - name: ๋ฐฐํฌ ์‚ฌ์ „ ์ ๊ฒ€ (SecretยทDockerfile)
123
+ # If a required Secret or the Dockerfile is missing, name it and stop instead of failing later in a third-party step such as docker login
124
+ - name: Deploy pre-check (Secrets and Dockerfile)
125
125
  working-directory: ${{ env.PROJECT_PATH }}
126
126
  env:
127
127
  DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -132,11 +132,12 @@ jobs:
132
132
  SSH_KEY: ${{ secrets.SSH_KEY }}
133
133
  DOCKERFILE: ${{ env.DOCKERFILE_PATH }}
134
134
  run: |
135
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
135
136
  MISSING=""
136
137
  for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
137
138
  [ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
138
139
  done
139
- # SSH_AUTH_METHOD=key๋ฉด SSH_KEY, ์•„๋‹ˆ๋ฉด SERVER_PASSWORD๊ฐ€ ํ•„์š”ํ•˜๋‹ค
140
+ # SSH_AUTH_METHOD=key needs SSH_KEY, anything else needs SERVER_PASSWORD
140
141
  if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
141
142
  [ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
142
143
  else
@@ -144,50 +145,51 @@ jobs:
144
145
  fi
145
146
  FAILED=false
146
147
  if [ -n "$MISSING" ]; then
147
- echo "::error title=ํ•„์ˆ˜ Secret ๋ˆ„๋ฝ::๋“ฑ๋ก๋˜์ง€ ์•Š์€ GitHub Secret:$MISSING โ€” Settings โ†’ Secrets and variables โ†’ Actions์—์„œ ๋“ฑ๋กํ•˜์„ธ์š”"
148
+ echo "::error title=$(m cicd.precheck_secret_title)::$(m cicd.precheck_secret_body missing="$MISSING")"
148
149
  FAILED=true
149
150
  fi
150
151
  if [ ! -f "$DOCKERFILE" ]; then
151
- echo "::error title=Dockerfile ์—†์Œ::${PROJECT_PATH}/${DOCKERFILE#./} ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด ์›Œํฌํ”Œ๋กœ์šฐ๋Š” Docker ์ด๋ฏธ์ง€๋กœ ๋ฐฐํฌํ•˜๋ฏ€๋กœ Dockerfile์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค (์„œ๋ฒ„ ๋ฐฐํฌ๋ฅผ ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ด ์›Œํฌํ”Œ๋กœ์šฐ ํŒŒ์ผ์„ ์‚ญ์ œํ•˜์„ธ์š”)"
152
+ echo "::error title=$(m cicd.precheck_dockerfile_title)::$(m cicd.precheck_dockerfile_body path="${PROJECT_PATH}/${DOCKERFILE#./}")"
152
153
  FAILED=true
153
154
  fi
154
155
  if [ "$FAILED" = "true" ]; then
155
156
  exit 1
156
157
  fi
157
- echo "โœ… ์‚ฌ์ „ ์ ๊ฒ€ ํ†ต๊ณผ: ํ•„์ˆ˜ Secret๊ณผ Dockerfile์ด ๋ชจ๋‘ ์žˆ์Šต๋‹ˆ๋‹ค"
158
+ m cicd.precheck_ok
158
159
 
159
- - name: Java ์„ค์ •
160
+ - name: Set up Java
160
161
  uses: actions/setup-java@v5
161
162
  with:
162
163
  java-version: ${{ env.JAVA_VERSION }}
163
164
  distribution: 'temurin'
164
165
  cache: 'gradle'
165
166
 
166
- - name: Gradle Wrapper ์‹คํ–‰๊ถŒํ•œ ๋ถ€์—ฌ
167
+ - name: Make Gradle wrapper executable
167
168
  working-directory: ${{ env.PROJECT_PATH }}
168
169
  run: chmod +x gradlew
169
170
 
170
- # GitHub Secrets์—์„œ APPLICATION_PROD_YML ๊ฐ’์„ ์ฝ์–ด์™€์„œ
171
- # ๋ฆฌ์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ์— ํŒŒ์ผ๋กœ ์ƒ์„ฑ
172
- - name: application-prod.yml ์ƒ์„ฑ
171
+ # Read APPLICATION_PROD_YML from GitHub Secrets and
172
+ # write it to a file in the resources directory
173
+ - name: Generate application-prod.yml
173
174
  run: |
175
+ m() { python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" get "$@"; }
174
176
  mkdir -p ${{ env.APPLICATION_YML_DIR }}
175
177
 
176
178
  cat << 'EOF' > ./${{ env.APPLICATION_YML_DIR }}/application-prod.yml
177
179
  ${{ secrets.APPLICATION_PROD_YML }}
178
180
  EOF
179
181
 
180
- echo "โœ… application-prod.yml ํŒŒ์ผ ์ƒ์„ฑ ์™„๋ฃŒ (${{ env.APPLICATION_YML_DIR }})"
182
+ m wf_spring_deploy.prod_yml_created dir="${{ env.APPLICATION_YML_DIR }}"
181
183
 
182
- # ํ…Œ์ŠคํŠธ๋Š” ์ œ์™ธํ•˜๊ณ  ์šด์˜ ํ”„๋กœํŒŒ์ผ๋กœ ๋นŒ๋“œ
184
+ # Build with the production profile, skipping tests
183
185
  - name: Build with Gradle
184
186
  working-directory: ${{ env.PROJECT_PATH }}
185
187
  run: ./gradlew clean build -x test ${{ env.GRADLE_OPTS }}
186
188
 
187
- - name: Docker ๋นŒ๋“œํ™˜๊ฒฝ ์„ค์ •
189
+ - name: Set up Docker Buildx
188
190
  uses: docker/setup-buildx-action@v3
189
191
 
190
- - name: DockerHub ๋กœ๊ทธ์ธ
192
+ - name: Log in to DockerHub
191
193
  uses: docker/login-action@v3
192
194
  with:
193
195
  username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -201,8 +203,8 @@ jobs:
201
203
  restore-keys: |
202
204
  ${{ runner.os }}-buildx-
203
205
 
204
- # ๋ธŒ๋žœ์น˜๋ช…์„ ํƒœ๊ทธ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํ™˜๊ฒฝ๋ณ„๋กœ ์ด๋ฏธ์ง€ ๊ด€๋ฆฌ
205
- - name: Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ ๋ฐ ํ‘ธ์‹œ
206
+ # Use the branch name as the image tag to manage images per environment
207
+ - name: Build and push Docker image
206
208
  uses: docker/build-push-action@v5
207
209
  with:
208
210
  context: ${{ env.PROJECT_PATH }}
@@ -217,96 +219,125 @@ jobs:
217
219
  rm -rf /tmp/.buildx-cache
218
220
  mv /tmp/.buildx-cache-new /tmp/.buildx-cache
219
221
 
220
- # ๋ฐฐํฌ ์ž‘์—…
222
+ # Deploy job
221
223
  deploy:
222
- name: ์›๊ฒฉ ์„œ๋ฒ„ ๋ฐฐํฌ
224
+ name: Deploy to remote server
223
225
  needs: build
224
226
  runs-on: ubuntu-latest
225
227
 
226
228
  steps:
229
+ # The deploy script runs on the server, which has no messages.py:
230
+ # render the message catalog here and pass it along as a base64 env var
231
+ - name: Checkout message catalog
232
+ uses: actions/checkout@v7
233
+ with:
234
+ sparse-checkout: |
235
+ .github/scripts/messages.py
236
+ version.yml
237
+ sparse-checkout-cone-mode: false
238
+
239
+ - name: Load messages
240
+ run: |
241
+ MSGS="$(python3 "$GITHUB_WORKSPACE/.github/scripts/messages.py" dump wf_spring_deploy. \
242
+ | python3 -c 'import json, shlex, sys; d = json.load(sys.stdin); print("\n".join("M_%s=%s" % (k.replace(".", "_"), shlex.quote(v)) for k, v in d.items()))' \
243
+ | base64 | tr -d '\n')"
244
+ echo "PAW_MSG_B64=$MSGS" >> "$GITHUB_ENV"
245
+
227
246
  - name: Deploy
228
247
  uses: appleboy/ssh-action@v1.0.3
229
248
  env:
230
249
  SSH_AUTH_METHOD: ${{ env.SSH_AUTH_METHOD }}
250
+ PAW_MSG_B64: ${{ env.PAW_MSG_B64 }}
231
251
  with:
232
252
  host: ${{ secrets.SERVER_HOST }}
233
253
  username: ${{ secrets.SERVER_USER }}
234
254
  password: ${{ secrets.SERVER_PASSWORD }}
235
255
  key: ${{ secrets.SSH_KEY }}
236
256
  port: ${{ env.SSH_PORT }}
237
- envs: SSH_AUTH_METHOD
257
+ envs: SSH_AUTH_METHOD,PAW_MSG_B64
238
258
  script: |
239
259
  set -e
240
260
 
241
- # ๋ฐฐํฌ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
242
- echo "๐Ÿ”ง ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •.."
261
+ # Deployment environment
243
262
  export PATH=$PATH:/usr/local/bin
244
263
  export PW=${{ secrets.SERVER_PASSWORD }}
245
264
 
246
- # SSH ์ธ์ฆ ๋ฐฉ์‹์— ๋”ฐ๋ฅธ sudo ์ถ”์ƒํ™”
247
- # password ๋ชจ๋“œ: echo $PW | sudo -S (Synology ๋“ฑ sudo ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•„์š” ์„œ๋ฒ„)
248
- # key ๋ชจ๋“œ: sudo ์ง์ ‘ (AWS EC2 ๋“ฑ passwordless sudo ์„œ๋ฒ„)
265
+ # Messages arrive as base64 shell assignments (the server has no messages.py)
266
+ eval "$(printf '%s' "$PAW_MSG_B64" | base64 -d)"
267
+ m() {
268
+ local _v="M_wf_spring_deploy_$1" _kv
269
+ local _s="${!_v}"
270
+ shift
271
+ for _kv in "$@"; do _s=${_s//\{${_kv%%=*}\}/${_kv#*=}}; done
272
+ printf '%s\n' "$_s"
273
+ }
274
+
275
+ m simple_env_setup
276
+
277
+ # Abstract sudo by SSH auth method
278
+ # password mode: echo $PW | sudo -S (servers that need a sudo password, e.g. Synology)
279
+ # key mode: plain sudo (passwordless sudo servers, e.g. AWS EC2)
249
280
  SSH_AUTH_METHOD="${SSH_AUTH_METHOD:-password}"
250
281
  if [ "${SSH_AUTH_METHOD}" = "key" ]; then
251
282
  SUDO() { sudo "$@"; }
252
283
  else
253
284
  SUDO() { echo "$PW" | sudo -S "$@"; }
254
285
  fi
255
- echo "๐Ÿ” SSH ์ธ์ฆ ๋ฐฉ์‹: ${SSH_AUTH_METHOD}"
286
+ m ssh_auth_method method="${SSH_AUTH_METHOD}"
256
287
 
257
288
  BRANCH=${{ github.ref_name }}
258
289
 
259
290
  PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-spring', env.PROJECT_NAME) || env.PROJECT_NAME }}"
260
291
 
261
- # ํฌํŠธ ๋ฐ ์ปจํ…Œ์ด๋„ˆ๋ช… ์„ค์ •
292
+ # Port and container name
262
293
  PORT=${{ env.DEPLOY_PORT }}
263
294
 
264
- # ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„ ์„ค์ • (ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ๋น„์–ด์žˆ์œผ๋ฉด PROJECT_NAME์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ)
295
+ # Container name (defaults to PROJECT_NAME when the env var is empty)
265
296
  if [ -z "${{ env.CONTAINER_NAME }}" ]; then
266
297
  CONTAINER_NAME="${PROJECT_NAME}"
267
- echo "โ„น๏ธ CONTAINER_NAME์ด ์„ค์ •๋˜์ง€ ์•Š์•„ PROJECT_NAME์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค: $CONTAINER_NAME"
298
+ m simple_container_name_default name="$CONTAINER_NAME"
268
299
  else
269
300
  CONTAINER_NAME="${{ env.CONTAINER_NAME }}"
270
- echo "โ„น๏ธ ์‚ฌ์šฉ์ž ์ง€์ • CONTAINER_NAME ์‚ฌ์šฉ: $CONTAINER_NAME"
301
+ m simple_container_name_custom name="$CONTAINER_NAME"
271
302
  fi
272
303
 
273
- echo "๐Ÿš€ ๋ฐฐํฌ ํ™˜๊ฒฝ์œผ๋กœ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค (๋ธŒ๋žœ์น˜: $BRANCH)"
304
+ m simple_deploy_start branch="$BRANCH"
274
305
 
275
- echo "๐Ÿ“‹ ๋ฐฐํฌ ์„ค์ • ์ •๋ณด:"
276
- echo " - ๋ธŒ๋žœ์น˜: $BRANCH"
277
- echo " - ํ”„๋กœ์ ํŠธ: $PROJECT_NAME"
278
- echo " - ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„: $CONTAINER_NAME"
279
- echo " - ํฌํŠธ: $PORT"
280
- echo " - Docker ์ด๋ฏธ์ง€: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
306
+ m simple_config_header
307
+ m simple_config_branch branch="$BRANCH"
308
+ m simple_config_project name="$PROJECT_NAME"
309
+ m simple_config_container name="$CONTAINER_NAME"
310
+ m simple_config_port port="$PORT"
311
+ m simple_config_image image="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
281
312
 
282
- # Docker ์ด๋ฏธ์ง€ ํ’€ (Pull)
283
- echo "โฌ‡๏ธ Docker ์ด๋ฏธ์ง€ ํ’€: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
313
+ # Pull the Docker image
314
+ m simple_image_pull image="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
284
315
  SUDO docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
285
316
 
286
- # ๊ธฐ์กด ์ปจํ…Œ์ด๋„ˆ ์ •๋ฆฌ
287
- echo "๐Ÿงน ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ ์ค‘..."
317
+ # Clean up the existing container
318
+ m simple_container_check name="$CONTAINER_NAME"
288
319
 
289
320
  if SUDO docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
290
- echo "โš ๏ธ ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์ด(๊ฐ€) ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ค‘์ง€ ๋ฐ ์‚ญ์ œ ์ค‘..."
321
+ m simple_container_exists name="$CONTAINER_NAME"
291
322
  SUDO docker rm -f $CONTAINER_NAME
292
- echo "โœ… ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์ด(๊ฐ€) ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
323
+ m simple_container_removed name="$CONTAINER_NAME"
293
324
  else
294
- echo "โ„น๏ธ ์กด์žฌํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์ด(๊ฐ€) ์—†์Šต๋‹ˆ๋‹ค."
325
+ m simple_container_missing name="$CONTAINER_NAME"
295
326
  fi
296
327
 
297
- # ์ƒˆ ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰
298
- echo "๐Ÿš€ ์ƒˆ๋กœ์šด ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์‹คํ–‰ ์ค‘..."
328
+ # Run the new container
329
+ m simple_container_starting name="$CONTAINER_NAME"
299
330
 
300
331
  VOLUME_MOUNT_OPTION=""
301
332
  if [ "${{ env.ENABLE_VOLUME_MOUNT }}" == "true" ]; then
302
333
  if [ ! -d "${{ env.VOLUME_HOST_PATH }}" ]; then
303
- echo "๐Ÿ“ ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ: ${{ env.VOLUME_HOST_PATH }}"
334
+ m volume_mkdir path="${{ env.VOLUME_HOST_PATH }}"
304
335
  SUDO mkdir -p "${{ env.VOLUME_HOST_PATH }}"
305
336
  fi
306
337
  VOLUME_MOUNT_OPTION="-v ${{ env.VOLUME_HOST_PATH }}:${{ env.VOLUME_CONTAINER_PATH }}"
307
- echo "๐Ÿ“ฆ ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ํ™œ์„ฑํ™”: $VOLUME_MOUNT_OPTION"
338
+ m simple_volume_enabled option="$VOLUME_MOUNT_OPTION"
308
339
  else
309
- echo "โ„น๏ธ ๋ณผ๋ฅจ ๋งˆ์šดํŠธ ๋น„ํ™œ์„ฑํ™”๋จ"
340
+ m volume_disabled
310
341
  fi
311
342
 
312
343
  SUDO docker run -d \
@@ -318,91 +349,91 @@ jobs:
318
349
  $VOLUME_MOUNT_OPTION \
319
350
  ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
320
351
 
321
- # ์ปจํ…Œ์ด๋„ˆ ํ—ฌ์Šค์ฒดํฌ
322
- echo "โณ ์ปจํ…Œ์ด๋„ˆ ์ดˆ๊ธฐํ™” ๋Œ€๊ธฐ ์ค‘... (${{ env.HEALTHCHECK_WAIT_SECONDS }}์ดˆ)"
352
+ # Container health check
353
+ m simple_init_wait sec="${{ env.HEALTHCHECK_WAIT_SECONDS }}"
323
354
  sleep ${{ env.HEALTHCHECK_WAIT_SECONDS }}
324
355
 
325
- # 1๋‹จ๊ณ„: ์ปจํ…Œ์ด๋„ˆ ํ”„๋กœ์„ธ์Šค ํ™•์ธ
326
- echo "๐Ÿฅ ์ปจํ…Œ์ด๋„ˆ ํ—ฌ์Šค์ฒดํฌ ์ค‘..."
356
+ # Step 1: check the container process
357
+ m simple_hc_check
327
358
  if ! SUDO docker ps --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
328
- echo "โŒ ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค"
359
+ m simple_hc_process_missing name="$CONTAINER_NAME"
329
360
  SUDO docker logs --tail 50 $CONTAINER_NAME || true
330
361
  exit 1
331
362
  fi
332
- echo "โœ… ์ปจํ…Œ์ด๋„ˆ ํ”„๋กœ์„ธ์Šค ์‹คํ–‰ ํ™•์ธ ์™„๋ฃŒ"
363
+ m simple_hc_process_ok
333
364
 
334
- # 2๋‹จ๊ณ„: HTTP ์—”๋“œํฌ์ธํŠธ ์ฒดํฌ (์„ค์ •๋œ ๊ฒฝ์šฐ)
365
+ # Step 2: check the HTTP endpoint (if configured)
335
366
  HEALTHCHECK_SUCCESS=false
336
367
  if [ -n "${{ env.HEALTHCHECK_PATH }}" ]; then
337
- echo "๐ŸŒ HTTP ํ—ฌ์Šค์ฒดํฌ ์‹œ์ž‘: http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }}"
368
+ m simple_http_start url="http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }}"
338
369
 
339
370
  for i in $(seq 1 ${{ env.HEALTHCHECK_MAX_RETRIES }}); do
340
- echo " ์‹œ๋„ $i/${{ env.HEALTHCHECK_MAX_RETRIES }}..."
371
+ m simple_http_attempt i="$i" max="${{ env.HEALTHCHECK_MAX_RETRIES }}"
341
372
 
342
373
  HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }} || echo "000")
343
374
 
344
375
  if [ "$HTTP_CODE" = "200" ]; then
345
- echo "โœ… HTTP ํ—ฌ์Šค์ฒดํฌ ์„ฑ๊ณต (์‘๋‹ต ์ฝ”๋“œ: $HTTP_CODE)"
376
+ m simple_http_ok code="$HTTP_CODE"
346
377
  HEALTHCHECK_SUCCESS=true
347
378
  break
348
379
  else
349
- echo "โš ๏ธ HTTP ํ—ฌ์Šค์ฒดํฌ ์‹คํŒจ (์‘๋‹ต ์ฝ”๋“œ: $HTTP_CODE)"
380
+ m simple_http_fail code="$HTTP_CODE"
350
381
  if [ $i -lt ${{ env.HEALTHCHECK_MAX_RETRIES }} ]; then
351
- echo " ${{ env.HEALTHCHECK_RETRY_INTERVAL }}์ดˆ ํ›„ ์žฌ์‹œ๋„..."
382
+ m simple_http_retry sec="${{ env.HEALTHCHECK_RETRY_INTERVAL }}"
352
383
  sleep ${{ env.HEALTHCHECK_RETRY_INTERVAL }}
353
384
  fi
354
385
  fi
355
386
  done
356
387
 
357
- # HTTP ์ฒดํฌ ์‹คํŒจ ์‹œ fallback์œผ๋กœ ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ
388
+ # If the HTTP check fails, fall back to log pattern matching
358
389
  if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
359
- echo "โš ๏ธ HTTP ํ—ฌ์Šค์ฒดํฌ ์ตœ์ข… ์‹คํŒจ, ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ์œผ๋กœ fallback..."
390
+ m simple_http_fallback
360
391
  fi
361
392
  else
362
- echo "โ„น๏ธ HEALTHCHECK_PATH๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•„ ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค"
393
+ m simple_hc_path_unset
363
394
  fi
364
395
 
365
- # 3๋‹จ๊ณ„: ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ (HTTP ์‹คํŒจ ์‹œ ๋˜๋Š” HTTP ์ฒดํฌ ๋น„ํ™œ์„ฑํ™” ์‹œ)
396
+ # Step 3: log pattern matching (when the HTTP check failed or is disabled)
366
397
  if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
367
- echo "๐Ÿ“‹ ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ ์‹œ์ž‘: '${{ env.HEALTHCHECK_LOG_PATTERN }}'"
398
+ m simple_log_match_start pattern="${{ env.HEALTHCHECK_LOG_PATTERN }}"
368
399
 
369
400
  if SUDO docker logs $CONTAINER_NAME 2>&1 | grep -q "${{ env.HEALTHCHECK_LOG_PATTERN }}"; then
370
- echo "โœ… ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ ์„ฑ๊ณต: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ •์ƒ์ ์œผ๋กœ ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค"
401
+ m simple_log_match_ok
371
402
  HEALTHCHECK_SUCCESS=true
372
403
  else
373
- echo "โŒ ๋กœ๊ทธ ํŒจํ„ด ๋งค์นญ ์‹คํŒจ: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ๋กœ๊ทธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"
404
+ m simple_log_match_fail
374
405
  echo ""
375
- echo "๐Ÿ“‹ ๋””๋ฒ„๊น… ์ •๋ณด:"
376
- echo " - ๊ฒ€์ƒ‰ ํŒจํ„ด: ${{ env.HEALTHCHECK_LOG_PATTERN }}"
377
- echo " - ์ปจํ…Œ์ด๋„ˆ: $CONTAINER_NAME"
378
- echo " - ์ด๋ฏธ์ง€: ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
379
- echo " - ํฌํŠธ: ${PORT}:${{ env.CONTAINER_INTERNAL_PORT }}"
380
- echo " - ๋ณผ๋ฅจ: $VOLUME_MOUNT_OPTION"
381
- echo " - ํ”„๋กœํŒŒ์ผ: ${{ env.SPRING_PROFILE }}"
406
+ m simple_debug_header
407
+ m simple_debug_pattern pattern="${{ env.HEALTHCHECK_LOG_PATTERN }}"
408
+ m simple_debug_container name="$CONTAINER_NAME"
409
+ m simple_debug_image image="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
410
+ m simple_debug_port port="${PORT}:${{ env.CONTAINER_INTERNAL_PORT }}"
411
+ m simple_debug_volume volume="$VOLUME_MOUNT_OPTION"
412
+ m simple_debug_profile profile="${{ env.SPRING_PROFILE }}"
382
413
  echo ""
383
- echo "๐Ÿ“‹ ์ปจํ…Œ์ด๋„ˆ ๋กœ๊ทธ (์ตœ๊ทผ 50์ค„):"
414
+ m simple_logs_tail
384
415
  SUDO docker logs --tail 50 $CONTAINER_NAME || true
385
416
  exit 1
386
417
  fi
387
418
  fi
388
419
 
389
420
  if [ "$HEALTHCHECK_SUCCESS" = "true" ]; then
390
- echo "โœ… ํ—ฌ์Šค์ฒดํฌ ์™„๋ฃŒ: ์ปจํ…Œ์ด๋„ˆ $CONTAINER_NAME ์ด(๊ฐ€) ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค"
421
+ m simple_hc_done name="$CONTAINER_NAME"
391
422
  fi
392
423
 
393
- # ๋ฐฐํฌ ์™„๋ฃŒ ํ™•์ธ
394
- echo "โœ… ๋ฐฐํฌ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!"
424
+ # Confirm the deploy finished
425
+ m simple_deploy_done
395
426
  echo ""
396
- echo "๐Ÿ“‹ ๋ฐฐํฌ ๊ฒฐ๊ณผ ์š”์•ฝ:"
397
- echo " ๐ŸŽฏ ํ”„๋กœ์ ํŠธ: $PROJECT_NAME"
398
- echo " ๐ŸŒฟ ๋ธŒ๋žœ์น˜: $BRANCH"
399
- echo " ๐Ÿณ ์ปจํ…Œ์ด๋„ˆ: $CONTAINER_NAME"
400
- echo " ๐ŸŒ ํฌํŠธ: $PORT"
401
- echo " โฐ ๋ฐฐํฌ ์‹œ๊ฐ„: $(date '+%Y-%m-%d %H:%M:%S')"
427
+ m simple_summary_header
428
+ m simple_summary_project name="$PROJECT_NAME"
429
+ m simple_summary_branch branch="$BRANCH"
430
+ m simple_summary_container name="$CONTAINER_NAME"
431
+ m simple_summary_port port="$PORT"
432
+ m simple_summary_time time="$(date '+%Y-%m-%d %H:%M:%S')"
402
433
  echo ""
403
- echo "๐Ÿ”— ์ ‘์† URL: http://${{ secrets.SERVER_HOST }}:${PORT}"
434
+ m simple_access_url url="http://${{ secrets.SERVER_HOST }}:${PORT}"
404
435
 
405
- # ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
406
- # 1. GitHub Secrets ์„ค์ • (DOCKERHUB_*, SERVER_*)
407
- # 2. env ์„น์…˜์—์„œ PROJECT_NAME, DEPLOY_PORT ์„ค์ •
408
- # 3. deploy ๋ธŒ๋žœ์น˜์— push โ†’ ์ž๋™ CI/CD ์‹คํ–‰
436
+ # Usage
437
+ # 1. Set up GitHub Secrets (DOCKERHUB_*, SERVER_*)
438
+ # 2. Set PROJECT_NAME and DEPLOY_PORT in the env section
439
+ # 3. Push to the deploy branch -> CI/CD runs automatically