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,82 +1,83 @@
|
|
|
1
1
|
# project-auto-wizard:managed-workflow
|
|
2
|
-
# Go CI/CD
|
|
3
|
-
# {{MAIN_BRANCH}}
|
|
4
|
-
#
|
|
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
|
-
#
|
|
7
|
-
# ENV_FILE: .env
|
|
8
|
-
# DOCKERHUB_USERNAME / DOCKERHUB_TOKEN: DockerHub
|
|
9
|
-
# SERVER_HOST / SERVER_USER:
|
|
10
|
-
# SERVER_PASSWORD: SSH
|
|
11
|
-
# SSH_KEY: SSH
|
|
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
|
-
#
|
|
14
|
-
# CONTAINER_INTERNAL_PORT: Go
|
|
15
|
-
# CONTAINER_NAME:
|
|
16
|
-
# ENABLE_VOLUME_MOUNT (true/false), VOLUME_HOST_PATH, VOLUME_CONTAINER_PATH:
|
|
17
|
-
# SSH_PORT (
|
|
18
|
-
# HEALTHCHECK_WAIT_SECONDS:
|
|
19
|
-
# HEALTHCHECK_PATH: HTTP
|
|
20
|
-
# HEALTHCHECK_MAX_RETRIES: HTTP
|
|
21
|
-
# HEALTHCHECK_LOG_PATTERN:
|
|
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 (
|
|
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
|
-
#
|
|
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
|
-
#
|
|
38
|
-
#
|
|
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
|
-
#
|
|
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
|
|
51
|
-
# password
|
|
52
|
-
# key
|
|
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
|
-
#
|
|
79
|
-
-
|
|
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
|
|
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
|
|
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=
|
|
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
|
-
|
|
114
|
+
m cicd.precheck_ok
|
|
113
115
|
|
|
114
|
-
- name:
|
|
116
|
+
- name: Free up disk space
|
|
115
117
|
run: |
|
|
116
|
-
|
|
118
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
119
|
+
m cicd.disk_before
|
|
117
120
|
df -h
|
|
118
121
|
|
|
119
|
-
|
|
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
|
-
|
|
128
|
+
m cicd.disk_docker_prune
|
|
126
129
|
docker system prune -af --volumes || true
|
|
127
130
|
|
|
128
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
196
|
-
# password
|
|
197
|
-
# key
|
|
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
|
-
|
|
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
|
-
|
|
237
|
+
m "${{ env.PAW_M_cicd_container_default }}" container="$CONTAINER_NAME"
|
|
216
238
|
else
|
|
217
239
|
CONTAINER_NAME="${{ env.CONTAINER_NAME }}"
|
|
218
|
-
|
|
240
|
+
m "${{ env.PAW_M_cicd_container_custom }}" container="$CONTAINER_NAME"
|
|
219
241
|
fi
|
|
220
242
|
|
|
221
|
-
|
|
243
|
+
m "${{ env.PAW_M_cicd_deploy_start }}" branch="$BRANCH"
|
|
222
244
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
260
|
+
m "${{ env.PAW_M_cicd_container_exists }}" container="$CONTAINER_NAME"
|
|
239
261
|
SUDO docker rm -f $CONTAINER_NAME
|
|
240
|
-
|
|
262
|
+
m "${{ env.PAW_M_cicd_container_removed }}" container="$CONTAINER_NAME"
|
|
241
263
|
else
|
|
242
|
-
|
|
264
|
+
m "${{ env.PAW_M_cicd_container_none }}" container="$CONTAINER_NAME"
|
|
243
265
|
fi
|
|
244
266
|
|
|
245
|
-
#
|
|
246
|
-
|
|
267
|
+
# Start the new container
|
|
268
|
+
m "${{ env.PAW_M_cicd_container_run }}" container="$CONTAINER_NAME"
|
|
247
269
|
|
|
248
|
-
# -
|
|
249
|
-
# -
|
|
250
|
-
# -
|
|
251
|
-
# .env
|
|
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
|
-
|
|
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
|
-
|
|
283
|
+
m "${{ env.PAW_M_cicd_volume_on }}" opts="$VOLUME_OPTS"
|
|
262
284
|
else
|
|
263
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
306
|
+
m "${{ env.PAW_M_cicd_health_process_ok }}"
|
|
285
307
|
|
|
286
|
-
# 2
|
|
308
|
+
# Step 2: check the HTTP endpoint (when configured)
|
|
287
309
|
HEALTHCHECK_SUCCESS=false
|
|
288
310
|
if [ -n "${{ env.HEALTHCHECK_PATH }}" ]; then
|
|
289
|
-
|
|
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
|
-
|
|
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
|
-
|
|
319
|
+
m "${{ env.PAW_M_cicd_http_ok }}" code="$HTTP_CODE"
|
|
298
320
|
HEALTHCHECK_SUCCESS=true
|
|
299
321
|
break
|
|
300
322
|
else
|
|
301
|
-
|
|
323
|
+
m "${{ env.PAW_M_cicd_http_fail }}" code="$HTTP_CODE"
|
|
302
324
|
if [ $i -lt ${{ env.HEALTHCHECK_MAX_RETRIES }} ]; then
|
|
303
|
-
|
|
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
|
|
331
|
+
# If the HTTP check fails, fall back to log pattern matching
|
|
310
332
|
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
311
|
-
|
|
333
|
+
m "${{ env.PAW_M_cicd_http_final_fail }}"
|
|
312
334
|
fi
|
|
313
335
|
else
|
|
314
|
-
|
|
336
|
+
m "${{ env.PAW_M_cicd_http_skipped }}"
|
|
315
337
|
fi
|
|
316
338
|
|
|
317
|
-
# 3
|
|
339
|
+
# Step 3: log pattern matching (when HTTP failed or the HTTP check is disabled)
|
|
318
340
|
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
319
|
-
|
|
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
|
-
|
|
344
|
+
m "${{ env.PAW_M_cicd_log_ok }}"
|
|
323
345
|
HEALTHCHECK_SUCCESS=true
|
|
324
346
|
else
|
|
325
|
-
|
|
347
|
+
m "${{ env.PAW_M_cicd_log_fail }}"
|
|
326
348
|
echo ""
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
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
|
-
|
|
363
|
+
m "${{ env.PAW_M_cicd_health_done }}" container="$CONTAINER_NAME"
|
|
342
364
|
fi
|
|
343
365
|
|
|
344
|
-
#
|
|
345
|
-
|
|
366
|
+
# Confirm the deployment
|
|
367
|
+
m "${{ env.PAW_M_cicd_deploy_done }}"
|
|
346
368
|
echo ""
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
376
|
+
m "${{ env.PAW_M_cicd_summary_url }}" url="http://${{ secrets.SERVER_HOST }}:${PORT}"
|