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.
- package/README.md +1 -1
- package/bin/project-auto-wizard.js +18 -4
- package/package.json +2 -2
- package/payload/config/breaking-changes.json +28 -14
- package/payload/config/wizard-prompts.yml +102 -46
- package/payload/flutter-app/android/fastlane/Fastfile.playstore +44 -32
- package/payload/flutter-app/ios/ExportOptions.plist +8 -8
- package/payload/flutter-app/ios/fastlane/Fastfile +57 -43
- package/payload/scripts/changelog_manager.py +296 -257
- package/payload/scripts/issue_helper.py +54 -50
- package/payload/scripts/messages.py +2279 -0
- package/payload/scripts/truncate_release_notes.py +7 -6
- package/payload/scripts/version_manager.py +64 -61
- package/payload/version.yml.template +2 -1
- package/payload/workflows/common/PROJECT-COMMON-AI-PR-SUMMARY.yaml +17 -12
- package/payload/workflows/common/PROJECT-COMMON-AUTO-CHANGELOG-CONTROL.yaml +59 -34
- package/payload/workflows/common/PROJECT-COMMON-ISSUE-HELPER.yaml +17 -15
- package/payload/workflows/common/PROJECT-COMMON-README-VERSION-UPDATE.yaml +17 -10
- package/payload/workflows/common/PROJECT-COMMON-RELEASE-PUBLISH.yaml +54 -44
- package/payload/workflows/common/PROJECT-COMMON-VERSION-CONTROL.yaml +24 -17
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-FIREBASE-CICD.yaml +194 -184
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-PLAYSTORE-CICD.yaml +226 -213
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-SELFHOSTED-CICD.yaml +46 -42
- package/payload/workflows/flutter/PROJECT-FLUTTER-ANDROID-TEST-APK.yaml +334 -241
- package/payload/workflows/flutter/PROJECT-FLUTTER-APP-BUILD-TRIGGER.yaml +152 -128
- package/payload/workflows/flutter/PROJECT-FLUTTER-CI.yaml +246 -188
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TEST-TESTFLIGHT.yaml +353 -238
- package/payload/workflows/flutter/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml +96 -85
- package/payload/workflows/go/PROJECT-GO-CI.yaml +47 -36
- package/payload/workflows/go/PROJECT-GO-PR-PREVIEW.yaml +945 -508
- package/payload/workflows/go/PROJECT-GO-SIMPLE-CICD.yaml +155 -133
- package/payload/workflows/next/PROJECT-NEXT-CI.yaml +76 -60
- package/payload/workflows/next/PROJECT-NEXT-CICD.yaml +97 -77
- package/payload/workflows/python/PROJECT-PYTHON-CI.yaml +62 -47
- package/payload/workflows/python/PROJECT-PYTHON-PR-PREVIEW.yaml +942 -505
- package/payload/workflows/python/PROJECT-PYTHON-SIMPLE-CICD.yaml +148 -126
- package/payload/workflows/react/PROJECT-REACT-CI.yaml +86 -70
- package/payload/workflows/react/PROJECT-REACT-CICD.yaml +112 -92
- package/payload/workflows/spring/PROJECT-SPRING-CI.yml +112 -82
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-NGINX-CICD.yaml +180 -150
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-NONSTOP-TRAEFIK-CICD.yaml +161 -131
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-PR-PREVIEW.yaml +970 -549
- package/payload/workflows/spring/server-deploy/PROJECT-SPRING-SIMPLE-CICD.yaml +193 -162
- package/src/cli/args.js +98 -52
- package/src/cli/help.js +8 -41
- package/src/commands/doctor.js +85 -89
- package/src/commands/dry-run.js +63 -43
- package/src/commands/full.js +79 -77
- package/src/commands/install-settings.js +16 -15
- package/src/commands/interactive-flutter.js +20 -19
- package/src/commands/interactive.js +135 -132
- package/src/commands/purge.js +47 -42
- package/src/commands/status.js +36 -36
- package/src/commands/uninstall.js +43 -41
- package/src/context.js +12 -11
- package/src/core/assets.js +12 -11
- package/src/core/baseline.js +27 -27
- package/src/core/branches.js +29 -27
- package/src/core/branding.js +7 -6
- package/src/core/breaking-check.js +22 -21
- package/src/core/breaking.js +15 -8
- package/src/core/copy/app-files.js +5 -4
- package/src/core/copy/flutter-app.js +8 -8
- package/src/core/copy/gitignore.js +58 -50
- package/src/core/copy/readme.js +45 -35
- package/src/core/copy/simple.js +16 -15
- package/src/core/copy/workflows.js +131 -124
- package/src/core/deploy-style.js +51 -48
- package/src/core/detect-fs.js +57 -57
- package/src/core/detect.js +67 -65
- package/src/core/errors.js +2 -2
- package/src/core/flutter-doctor.js +17 -16
- package/src/core/flutter-hooks.js +33 -32
- package/src/core/flutter-options.js +34 -33
- package/src/core/fsutil.js +12 -12
- package/src/core/installed-stores.js +4 -4
- package/src/core/logger.js +42 -40
- package/src/core/paths-resolve.js +88 -86
- package/src/core/paths.js +10 -10
- package/src/core/release-options.js +8 -8
- package/src/core/removal-exec.js +14 -13
- package/src/core/removal-plan.js +37 -37
- package/src/core/types.js +47 -47
- package/src/core/verify.js +44 -38
- package/src/core/version-yml.js +76 -68
- package/src/core/wizard-env.js +46 -46
- package/src/core/wizard-labels.js +63 -41
- package/src/i18n/catalog/en/cli.js +50 -0
- package/src/i18n/catalog/en/commands.js +187 -0
- package/src/i18n/catalog/en/copy.js +19 -0
- package/src/i18n/catalog/en/core.js +74 -0
- package/src/i18n/catalog/en/core2.js +15 -0
- package/src/i18n/catalog/en/ui.js +221 -0
- package/src/i18n/catalog/en.js +12 -0
- package/src/i18n/catalog/index.js +5 -0
- package/src/i18n/catalog/ko/cli.js +50 -0
- package/src/i18n/catalog/ko/commands.js +187 -0
- package/src/i18n/catalog/ko/copy.js +19 -0
- package/src/i18n/catalog/ko/core.js +74 -0
- package/src/i18n/catalog/ko/core2.js +15 -0
- package/src/i18n/catalog/ko/ui.js +221 -0
- package/src/i18n/catalog/ko.js +10 -0
- package/src/i18n/index.js +44 -0
- package/src/i18n/languages.js +9 -0
- package/src/index.js +128 -94
- package/src/ui/ansi.js +7 -7
- package/src/ui/banner.js +7 -6
- package/src/ui/env-plan.js +82 -81
- package/src/ui/prompts.js +97 -122
- package/src/ui/readline-engine.js +59 -58
- package/src/ui/status-cards.js +37 -31
- package/src/ui/summary.js +89 -88
|
@@ -1,127 +1,127 @@
|
|
|
1
1
|
# project-auto-wizard:managed-workflow
|
|
2
|
-
# Spring Boot CI/CD
|
|
2
|
+
# Spring Boot CI/CD deploy (SSH + Docker)
|
|
3
3
|
#
|
|
4
|
-
# deploy
|
|
5
|
-
#
|
|
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
|
-
#
|
|
8
|
-
# APPLICATION_PROD_YML (
|
|
9
|
-
# DOCKERHUB_USERNAME: DockerHub
|
|
10
|
-
# DOCKERHUB_TOKEN: DockerHub
|
|
11
|
-
# SERVER_HOST:
|
|
12
|
-
# SERVER_USER: SSH
|
|
13
|
-
# SERVER_PASSWORD: SSH
|
|
14
|
-
# SSH_KEY: SSH
|
|
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
|
-
#
|
|
16
|
+
# Environment variables (set in the env section)
|
|
17
17
|
#
|
|
18
|
-
#
|
|
19
|
-
# CONTAINER_INTERNAL_PORT: Spring Boot
|
|
20
|
-
# DEPLOY_PORT:
|
|
21
|
-
# CONTAINER_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:
|
|
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
|
|
30
|
-
# SSH_COMMAND_TIMEOUT: SSH
|
|
31
|
-
# SSH_CONNECTION_TIMEOUT: SSH
|
|
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:
|
|
35
|
-
# HEALTHCHECK_PATH: HTTP
|
|
36
|
-
# HEALTHCHECK_MAX_RETRIES: HTTP
|
|
37
|
-
# HEALTHCHECK_RETRY_INTERVAL:
|
|
38
|
-
# HEALTHCHECK_LOG_PATTERN:
|
|
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
|
-
#
|
|
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
|
|
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
|
-
#
|
|
55
|
-
# {{MAIN_BRANCH}}
|
|
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}}" #
|
|
60
|
-
# @wizard paths-anchor (
|
|
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
|
-
#
|
|
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
|
-
#
|
|
69
|
-
#
|
|
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" #
|
|
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
|
-
#
|
|
86
|
-
#
|
|
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
|
|
97
|
-
# password
|
|
98
|
-
# key
|
|
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" #
|
|
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
|
-
#
|
|
124
|
-
- name:
|
|
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
|
|
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
|
|
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=
|
|
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
|
-
|
|
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
|
|
167
|
+
- name: Make Gradle wrapper executable
|
|
167
168
|
working-directory: ${{ env.PROJECT_PATH }}
|
|
168
169
|
run: chmod +x gradlew
|
|
169
170
|
|
|
170
|
-
# GitHub Secrets
|
|
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
|
-
|
|
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:
|
|
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
|
-
#
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
298
|
+
m simple_container_name_default name="$CONTAINER_NAME"
|
|
268
299
|
else
|
|
269
300
|
CONTAINER_NAME="${{ env.CONTAINER_NAME }}"
|
|
270
|
-
|
|
301
|
+
m simple_container_name_custom name="$CONTAINER_NAME"
|
|
271
302
|
fi
|
|
272
303
|
|
|
273
|
-
|
|
304
|
+
m simple_deploy_start branch="$BRANCH"
|
|
274
305
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
#
|
|
283
|
-
|
|
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
|
-
|
|
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
|
-
|
|
321
|
+
m simple_container_exists name="$CONTAINER_NAME"
|
|
291
322
|
SUDO docker rm -f $CONTAINER_NAME
|
|
292
|
-
|
|
323
|
+
m simple_container_removed name="$CONTAINER_NAME"
|
|
293
324
|
else
|
|
294
|
-
|
|
325
|
+
m simple_container_missing name="$CONTAINER_NAME"
|
|
295
326
|
fi
|
|
296
327
|
|
|
297
|
-
#
|
|
298
|
-
|
|
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
|
-
|
|
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
|
-
|
|
338
|
+
m simple_volume_enabled option="$VOLUME_MOUNT_OPTION"
|
|
308
339
|
else
|
|
309
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
363
|
+
m simple_hc_process_ok
|
|
333
364
|
|
|
334
|
-
# 2
|
|
365
|
+
# Step 2: check the HTTP endpoint (if configured)
|
|
335
366
|
HEALTHCHECK_SUCCESS=false
|
|
336
367
|
if [ -n "${{ env.HEALTHCHECK_PATH }}" ]; then
|
|
337
|
-
|
|
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
|
-
|
|
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
|
-
|
|
376
|
+
m simple_http_ok code="$HTTP_CODE"
|
|
346
377
|
HEALTHCHECK_SUCCESS=true
|
|
347
378
|
break
|
|
348
379
|
else
|
|
349
|
-
|
|
380
|
+
m simple_http_fail code="$HTTP_CODE"
|
|
350
381
|
if [ $i -lt ${{ env.HEALTHCHECK_MAX_RETRIES }} ]; then
|
|
351
|
-
|
|
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
|
|
388
|
+
# If the HTTP check fails, fall back to log pattern matching
|
|
358
389
|
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
359
|
-
|
|
390
|
+
m simple_http_fallback
|
|
360
391
|
fi
|
|
361
392
|
else
|
|
362
|
-
|
|
393
|
+
m simple_hc_path_unset
|
|
363
394
|
fi
|
|
364
395
|
|
|
365
|
-
# 3
|
|
396
|
+
# Step 3: log pattern matching (when the HTTP check failed or is disabled)
|
|
366
397
|
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
367
|
-
|
|
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
|
-
|
|
401
|
+
m simple_log_match_ok
|
|
371
402
|
HEALTHCHECK_SUCCESS=true
|
|
372
403
|
else
|
|
373
|
-
|
|
404
|
+
m simple_log_match_fail
|
|
374
405
|
echo ""
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
|
|
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
|
-
|
|
421
|
+
m simple_hc_done name="$CONTAINER_NAME"
|
|
391
422
|
fi
|
|
392
423
|
|
|
393
|
-
#
|
|
394
|
-
|
|
424
|
+
# Confirm the deploy finished
|
|
425
|
+
m simple_deploy_done
|
|
395
426
|
echo ""
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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
|
-
|
|
434
|
+
m simple_access_url url="http://${{ secrets.SERVER_HOST }}:${PORT}"
|
|
404
435
|
|
|
405
|
-
#
|
|
406
|
-
# 1. GitHub Secrets
|
|
407
|
-
# 2.
|
|
408
|
-
# 3.
|
|
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
|