project-auto-wizard 0.13.2 โ 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,80 +1,81 @@
|
|
|
1
1
|
# project-auto-wizard:managed-workflow
|
|
2
|
-
# Python FastAPI CI/CD
|
|
3
|
-
# {{MAIN_BRANCH}}
|
|
4
|
-
#
|
|
2
|
+
# Python FastAPI 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
|
|
9
|
-
# DOCKERHUB_TOKEN: DockerHub
|
|
10
|
-
# SERVER_HOST:
|
|
11
|
-
# SERVER_USER: SSH
|
|
12
|
-
# SERVER_PASSWORD: SSH
|
|
13
|
-
# SSH_KEY: SSH
|
|
6
|
+
# Required GitHub Secrets
|
|
7
|
+
# ENV_FILE: contents of the .env file
|
|
8
|
+
# DOCKERHUB_USERNAME: DockerHub username
|
|
9
|
+
# DOCKERHUB_TOKEN: DockerHub access token
|
|
10
|
+
# SERVER_HOST: deploy server address
|
|
11
|
+
# SERVER_USER: SSH username
|
|
12
|
+
# SERVER_PASSWORD: SSH password (when SSH_AUTH_METHOD=password)
|
|
13
|
+
# SSH_KEY: contents of the SSH private key .pem (when SSH_AUTH_METHOD=key, e.g. AWS EC2)
|
|
14
14
|
#
|
|
15
|
-
#
|
|
16
|
-
# HEALTHCHECK_PATH
|
|
15
|
+
# Environment variables are set in the env section below; each line has its description and default.
|
|
16
|
+
# HEALTHCHECK_PATH examples: FastAPI "/docs", custom "/api/v1/health"
|
|
17
17
|
|
|
18
18
|
name: PROJECT-PYTHON-SIMPLE-CICD
|
|
19
19
|
|
|
20
20
|
on:
|
|
21
21
|
push:
|
|
22
22
|
branches:
|
|
23
|
-
- "{{MAIN_BRANCH}}" #
|
|
24
|
-
# @wizard paths-anchor (
|
|
25
|
-
workflow_dispatch: #
|
|
23
|
+
- "{{MAIN_BRANCH}}" # production deploy
|
|
24
|
+
# @wizard paths-anchor (for a monorepo the integrator injects the paths filter here)
|
|
25
|
+
workflow_dispatch: # allow manual runs
|
|
26
26
|
|
|
27
27
|
env:
|
|
28
|
-
#
|
|
28
|
+
# Unique project identifier
|
|
29
29
|
PROJECT_NAME: "__PROJECT_NAME__" # @wizard ask:@repo
|
|
30
|
-
#
|
|
30
|
+
# Subdirectory of this project in a monorepo (--paths). Base for the build and Docker context ('.' means the repo root)
|
|
31
31
|
PROJECT_PATH: "." # @wizard auto:project-path
|
|
32
|
-
#
|
|
33
|
-
#
|
|
32
|
+
# In a monorepo (PROJECT_PATH is not '.') '-python' is appended to the image, container and domain names
|
|
33
|
+
# so they do not collide with services of other types in the same repo (e.g. my-app-python)
|
|
34
34
|
|
|
35
|
-
#
|
|
35
|
+
# Port settings
|
|
36
36
|
DEPLOY_PORT: "__DEPLOY_PORT__" # @wizard ask:8000
|
|
37
37
|
|
|
38
|
-
#
|
|
38
|
+
# Container name (uses PROJECT_NAME when empty)
|
|
39
39
|
CONTAINER_NAME: ""
|
|
40
40
|
|
|
41
|
-
#
|
|
41
|
+
# Volume mount settings (no mount when empty)
|
|
42
42
|
ENABLE_VOLUME_MOUNT: "false" # @wizard ask:false
|
|
43
43
|
VOLUME_HOST_PATH: "/volume1/projects/__PROJECT_NAME__" # @wizard ask:/volume1/projects/__PROJECT_NAME__
|
|
44
44
|
VOLUME_CONTAINER_PATH: "/mnt/__PROJECT_NAME__" # @wizard ask:/mnt/__PROJECT_NAME__
|
|
45
45
|
|
|
46
|
-
# SSH
|
|
47
|
-
# password
|
|
48
|
-
# key
|
|
46
|
+
# SSH auth method: "password" (Synology, generic servers) | "key" (AWS EC2, .pem)
|
|
47
|
+
# password -> uses the SERVER_PASSWORD secret
|
|
48
|
+
# key -> uses the SSH_KEY secret (the whole .pem file content as the secret value)
|
|
49
49
|
SSH_AUTH_METHOD: "password" # @wizard ask:password
|
|
50
50
|
|
|
51
|
-
# SSH
|
|
51
|
+
# SSH connection settings
|
|
52
52
|
SSH_PORT: "__SSH_PORT__" # @wizard ask:2022
|
|
53
|
-
SSH_COMMAND_TIMEOUT: "600s" # SSH
|
|
54
|
-
SSH_CONNECTION_TIMEOUT: "60s" # SSH
|
|
53
|
+
SSH_COMMAND_TIMEOUT: "600s" # SSH command execution timeout
|
|
54
|
+
SSH_CONNECTION_TIMEOUT: "60s" # SSH connection timeout
|
|
55
55
|
|
|
56
|
-
#
|
|
57
|
-
CONTAINER_INTERNAL_PORT: "8000" # FastAPI
|
|
56
|
+
# Container port settings
|
|
57
|
+
CONTAINER_INTERNAL_PORT: "8000" # FastAPI internal port
|
|
58
58
|
|
|
59
|
-
#
|
|
60
|
-
HEALTHCHECK_WAIT_SECONDS: "10" #
|
|
61
|
-
HEALTHCHECK_PATH: "/docs" # HTTP
|
|
62
|
-
HEALTHCHECK_MAX_RETRIES: "5" # HTTP
|
|
63
|
-
HEALTHCHECK_RETRY_INTERVAL: "3" #
|
|
64
|
-
HEALTHCHECK_LOG_PATTERN: "Uvicorn running on" # Fallback
|
|
59
|
+
# Health check settings
|
|
60
|
+
HEALTHCHECK_WAIT_SECONDS: "10" # wait for container initialization (seconds)
|
|
61
|
+
HEALTHCHECK_PATH: "/docs" # HTTP endpoint path (only the log pattern is used when empty)
|
|
62
|
+
HEALTHCHECK_MAX_RETRIES: "5" # HTTP retry count
|
|
63
|
+
HEALTHCHECK_RETRY_INTERVAL: "3" # retry interval (seconds)
|
|
64
|
+
HEALTHCHECK_LOG_PATTERN: "Uvicorn running on" # Fallback log search pattern
|
|
65
65
|
|
|
66
66
|
jobs:
|
|
67
|
-
# Job:
|
|
67
|
+
# Job: build
|
|
68
68
|
build:
|
|
69
|
-
name: Python FastAPI
|
|
69
|
+
name: Build Python FastAPI application
|
|
70
70
|
runs-on: ubuntu-latest
|
|
71
71
|
|
|
72
72
|
steps:
|
|
73
|
-
- name:
|
|
73
|
+
- name: Checkout repository
|
|
74
74
|
uses: actions/checkout@v7
|
|
75
75
|
|
|
76
|
-
#
|
|
77
|
-
-
|
|
76
|
+
# If a required Secret or the Dockerfile is missing, report the missing item and stop
|
|
77
|
+
# instead of failing later with a third-party error such as docker login
|
|
78
|
+
- name: Deploy pre-check (Secrets and Dockerfile)
|
|
78
79
|
working-directory: ${{ env.PROJECT_PATH }}
|
|
79
80
|
env:
|
|
80
81
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
@@ -85,11 +86,12 @@ jobs:
|
|
|
85
86
|
SSH_KEY: ${{ secrets.SSH_KEY }}
|
|
86
87
|
DOCKERFILE: Dockerfile
|
|
87
88
|
run: |
|
|
89
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
88
90
|
MISSING=""
|
|
89
91
|
for NAME in DOCKERHUB_USERNAME DOCKERHUB_TOKEN SERVER_HOST SERVER_USER; do
|
|
90
92
|
[ -n "${!NAME}" ] || MISSING="$MISSING $NAME"
|
|
91
93
|
done
|
|
92
|
-
# SSH_AUTH_METHOD=key
|
|
94
|
+
# SSH_AUTH_METHOD=key needs SSH_KEY, otherwise SERVER_PASSWORD is required
|
|
93
95
|
if [ "${SSH_AUTH_METHOD:-password}" = "key" ]; then
|
|
94
96
|
[ -n "$SSH_KEY" ] || MISSING="$MISSING SSH_KEY"
|
|
95
97
|
else
|
|
@@ -97,78 +99,95 @@ jobs:
|
|
|
97
99
|
fi
|
|
98
100
|
FAILED=false
|
|
99
101
|
if [ -n "$MISSING" ]; then
|
|
100
|
-
echo "::error title
|
|
102
|
+
echo "::error title=$(m cicd.precheck_secret_title)::$(m cicd.precheck_secret_body missing="$MISSING")"
|
|
101
103
|
FAILED=true
|
|
102
104
|
fi
|
|
103
105
|
if [ ! -f "$DOCKERFILE" ]; then
|
|
104
|
-
echo "::error title=
|
|
106
|
+
echo "::error title=$(m cicd.precheck_dockerfile_title)::$(m cicd.precheck_dockerfile_body path="${PROJECT_PATH}/${DOCKERFILE#./}")"
|
|
105
107
|
FAILED=true
|
|
106
108
|
fi
|
|
107
109
|
if [ "$FAILED" = "true" ]; then
|
|
108
110
|
exit 1
|
|
109
111
|
fi
|
|
110
|
-
|
|
112
|
+
m cicd.precheck_ok
|
|
111
113
|
|
|
112
|
-
- name:
|
|
114
|
+
- name: Free up disk space
|
|
113
115
|
run: |
|
|
114
|
-
|
|
116
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
117
|
+
m cicd.disk_before
|
|
115
118
|
df -h
|
|
116
119
|
|
|
117
|
-
|
|
120
|
+
m cicd.disk_cleaning
|
|
118
121
|
sudo rm -rf /usr/share/dotnet || true
|
|
119
122
|
sudo rm -rf /opt/ghc || true
|
|
120
123
|
sudo rm -rf /usr/local/share/boost || true
|
|
121
124
|
sudo rm -rf "$AGENT_TOOLSDIRECTORY" || true
|
|
122
125
|
|
|
123
|
-
|
|
126
|
+
m cicd.disk_docker_prune
|
|
124
127
|
docker system prune -af --volumes || true
|
|
125
128
|
|
|
126
|
-
|
|
129
|
+
m cicd.disk_after_cleanup
|
|
127
130
|
df -h
|
|
128
131
|
|
|
129
|
-
- name: .env
|
|
132
|
+
- name: Create .env file
|
|
130
133
|
working-directory: ${{ env.PROJECT_PATH }}
|
|
131
134
|
run: |
|
|
135
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
132
136
|
cat > .env << 'EOF'
|
|
133
137
|
${{ secrets.ENV_FILE }}
|
|
134
138
|
EOF
|
|
135
|
-
|
|
139
|
+
m cicd.dotenv_created
|
|
136
140
|
|
|
137
|
-
- name: Docker
|
|
141
|
+
- name: Set up Docker Buildx
|
|
138
142
|
uses: docker/setup-buildx-action@v3
|
|
139
143
|
|
|
140
|
-
- name: DockerHub
|
|
144
|
+
- name: Log in to DockerHub
|
|
141
145
|
uses: docker/login-action@v3
|
|
142
146
|
with:
|
|
143
147
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
144
148
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
145
149
|
|
|
146
|
-
- name:
|
|
150
|
+
- name: Build and push Docker image
|
|
147
151
|
uses: docker/build-push-action@v5
|
|
148
152
|
with:
|
|
149
153
|
context: ${{ env.PROJECT_PATH }}
|
|
150
154
|
file: ${{ env.PROJECT_PATH }}/Dockerfile
|
|
151
155
|
push: true
|
|
152
156
|
tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ env.PROJECT_PATH != '.' && format('{0}-python', env.PROJECT_NAME) || env.PROJECT_NAME }}:${{ github.ref_name }}
|
|
153
|
-
# GitHub Actions
|
|
157
|
+
# Use the GitHub Actions cache (saves local disk space)
|
|
154
158
|
cache-from: type=gha
|
|
155
159
|
cache-to: type=gha,mode=max
|
|
156
160
|
|
|
157
|
-
- name:
|
|
161
|
+
- name: Clean up after build
|
|
158
162
|
if: always()
|
|
159
163
|
run: |
|
|
160
|
-
|
|
164
|
+
m() { (cd "$GITHUB_WORKSPACE" && python3 .github/scripts/messages.py get "$@"); }
|
|
165
|
+
m cicd.post_build_cleanup
|
|
161
166
|
docker system prune -f || true
|
|
162
|
-
|
|
167
|
+
m cicd.disk_final
|
|
163
168
|
df -h
|
|
164
169
|
|
|
165
|
-
# Job:
|
|
170
|
+
# Job: deploy
|
|
166
171
|
deploy:
|
|
167
|
-
name:
|
|
172
|
+
name: Deploy to remote server
|
|
168
173
|
needs: build
|
|
169
174
|
runs-on: ubuntu-latest
|
|
170
175
|
|
|
171
176
|
steps:
|
|
177
|
+
# The deploy script runs on the remote server, so the messages are resolved here
|
|
178
|
+
# and handed to it through env vars (PAW_M_*)
|
|
179
|
+
- name: Checkout message catalog
|
|
180
|
+
uses: actions/checkout@v7
|
|
181
|
+
with:
|
|
182
|
+
sparse-checkout: |
|
|
183
|
+
.github/scripts/messages.py
|
|
184
|
+
version.yml
|
|
185
|
+
sparse-checkout-cone-mode: false
|
|
186
|
+
|
|
187
|
+
- name: Load messages
|
|
188
|
+
run: |
|
|
189
|
+
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"
|
|
190
|
+
|
|
172
191
|
- name: Deploy
|
|
173
192
|
uses: appleboy/ssh-action@v1.0.3
|
|
174
193
|
env:
|
|
@@ -185,76 +204,79 @@ jobs:
|
|
|
185
204
|
script: |
|
|
186
205
|
set -e
|
|
187
206
|
|
|
188
|
-
#
|
|
189
|
-
|
|
207
|
+
# Fill the {name} placeholders of a message template (usage: m "template" name=value ...)
|
|
208
|
+
m() { local s="$1" kv; shift; for kv in "$@"; do s="${s//\{${kv%%=*}\}/${kv#*=}}"; done; printf '%s\n' "$s"; }
|
|
209
|
+
|
|
210
|
+
# Set deploy environment variables
|
|
211
|
+
m "${{ env.PAW_M_cicd_setup_env }}"
|
|
190
212
|
export PATH=$PATH:/usr/local/bin
|
|
191
213
|
export PW=${{ secrets.SERVER_PASSWORD }}
|
|
192
214
|
|
|
193
|
-
#
|
|
194
|
-
# password
|
|
195
|
-
# key
|
|
215
|
+
# sudo abstraction per SSH auth method
|
|
216
|
+
# password mode: echo $PW | sudo -S (servers that need a sudo password, e.g. Synology)
|
|
217
|
+
# key mode: plain sudo (passwordless-sudo servers, e.g. AWS EC2)
|
|
196
218
|
SSH_AUTH_METHOD="${SSH_AUTH_METHOD:-password}"
|
|
197
219
|
if [ "${SSH_AUTH_METHOD}" = "key" ]; then
|
|
198
220
|
SUDO() { sudo "$@"; }
|
|
199
221
|
else
|
|
200
222
|
SUDO() { echo "$PW" | sudo -S "$@"; }
|
|
201
223
|
fi
|
|
202
|
-
|
|
224
|
+
m "${{ env.PAW_M_cicd_ssh_auth }}" method="${SSH_AUTH_METHOD}"
|
|
203
225
|
|
|
204
226
|
BRANCH=${{ github.ref_name }}
|
|
205
227
|
|
|
206
228
|
PROJECT_NAME="${{ env.PROJECT_PATH != '.' && format('{0}-python', env.PROJECT_NAME) || env.PROJECT_NAME }}"
|
|
207
229
|
|
|
208
|
-
#
|
|
230
|
+
# Port and container name
|
|
209
231
|
PORT=${{ env.DEPLOY_PORT }}
|
|
210
232
|
|
|
211
233
|
if [ -z "${{ env.CONTAINER_NAME }}" ]; then
|
|
212
234
|
CONTAINER_NAME="${PROJECT_NAME}"
|
|
213
|
-
|
|
235
|
+
m "${{ env.PAW_M_cicd_container_default }}" container="$CONTAINER_NAME"
|
|
214
236
|
else
|
|
215
237
|
CONTAINER_NAME="${{ env.CONTAINER_NAME }}"
|
|
216
|
-
|
|
238
|
+
m "${{ env.PAW_M_cicd_container_custom }}" container="$CONTAINER_NAME"
|
|
217
239
|
fi
|
|
218
240
|
|
|
219
|
-
|
|
241
|
+
m "${{ env.PAW_M_cicd_deploy_start }}" branch="$BRANCH"
|
|
220
242
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
243
|
+
m "${{ env.PAW_M_cicd_config_title }}"
|
|
244
|
+
m "${{ env.PAW_M_cicd_config_branch }}" branch="$BRANCH"
|
|
245
|
+
m "${{ env.PAW_M_cicd_config_project }}" project="$PROJECT_NAME"
|
|
246
|
+
m "${{ env.PAW_M_cicd_config_container }}" container="$CONTAINER_NAME"
|
|
247
|
+
m "${{ env.PAW_M_cicd_config_port }}" port="$PORT"
|
|
248
|
+
m "${{ env.PAW_M_cicd_config_image }}" image="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
|
|
227
249
|
|
|
228
|
-
#
|
|
229
|
-
|
|
250
|
+
# Pull the Docker image
|
|
251
|
+
m "${{ env.PAW_M_cicd_pull_image }}" image="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
|
|
230
252
|
SUDO docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
|
|
231
253
|
|
|
232
|
-
#
|
|
233
|
-
|
|
254
|
+
# Clean up the existing container
|
|
255
|
+
m "${{ env.PAW_M_cicd_container_check }}" container="$CONTAINER_NAME"
|
|
234
256
|
|
|
235
257
|
if SUDO docker ps -a --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
|
|
236
|
-
|
|
258
|
+
m "${{ env.PAW_M_cicd_container_exists }}" container="$CONTAINER_NAME"
|
|
237
259
|
SUDO docker rm -f $CONTAINER_NAME
|
|
238
|
-
|
|
260
|
+
m "${{ env.PAW_M_cicd_container_removed }}" container="$CONTAINER_NAME"
|
|
239
261
|
else
|
|
240
|
-
|
|
262
|
+
m "${{ env.PAW_M_cicd_container_none }}" container="$CONTAINER_NAME"
|
|
241
263
|
fi
|
|
242
264
|
|
|
243
|
-
#
|
|
244
|
-
|
|
265
|
+
# Start the new container
|
|
266
|
+
m "${{ env.PAW_M_cicd_container_run }}" container="$CONTAINER_NAME"
|
|
245
267
|
|
|
246
|
-
# .env
|
|
268
|
+
# The .env file is already baked into the Docker image
|
|
247
269
|
|
|
248
270
|
VOLUME_OPTS="-v /etc/localtime:/etc/localtime:ro"
|
|
249
271
|
if [ "${{ env.ENABLE_VOLUME_MOUNT }}" == "true" ]; then
|
|
250
272
|
if [ ! -d "${{ env.VOLUME_HOST_PATH }}" ]; then
|
|
251
|
-
|
|
273
|
+
m "${{ env.PAW_M_cicd_volume_mkdir }}" path="${{ env.VOLUME_HOST_PATH }}"
|
|
252
274
|
SUDO mkdir -p "${{ env.VOLUME_HOST_PATH }}"
|
|
253
275
|
fi
|
|
254
276
|
VOLUME_OPTS="$VOLUME_OPTS -v ${{ env.VOLUME_HOST_PATH }}:${{ env.VOLUME_CONTAINER_PATH }}"
|
|
255
|
-
|
|
277
|
+
m "${{ env.PAW_M_cicd_volume_on }}" opts="$VOLUME_OPTS"
|
|
256
278
|
else
|
|
257
|
-
|
|
279
|
+
m "${{ env.PAW_M_cicd_volume_off }}"
|
|
258
280
|
fi
|
|
259
281
|
|
|
260
282
|
SUDO docker run -d \
|
|
@@ -264,84 +286,84 @@ jobs:
|
|
|
264
286
|
$VOLUME_OPTS \
|
|
265
287
|
${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}
|
|
266
288
|
|
|
267
|
-
#
|
|
268
|
-
|
|
289
|
+
# Container health check
|
|
290
|
+
m "${{ env.PAW_M_cicd_health_wait }}" seconds="${{ env.HEALTHCHECK_WAIT_SECONDS }}"
|
|
269
291
|
sleep ${{ env.HEALTHCHECK_WAIT_SECONDS }}
|
|
270
292
|
|
|
271
|
-
# 1
|
|
272
|
-
|
|
293
|
+
# Step 1: check the container process
|
|
294
|
+
m "${{ env.PAW_M_cicd_health_check }}"
|
|
273
295
|
if ! SUDO docker ps --format '{{.Names}}' | grep -Eq "^${CONTAINER_NAME}\$"; then
|
|
274
|
-
|
|
296
|
+
m "${{ env.PAW_M_cicd_health_no_process }}" container="$CONTAINER_NAME"
|
|
275
297
|
SUDO docker logs --tail 50 $CONTAINER_NAME || true
|
|
276
298
|
exit 1
|
|
277
299
|
fi
|
|
278
|
-
|
|
300
|
+
m "${{ env.PAW_M_cicd_health_process_ok }}"
|
|
279
301
|
|
|
280
|
-
# 2
|
|
302
|
+
# Step 2: check the HTTP endpoint (when configured)
|
|
281
303
|
HEALTHCHECK_SUCCESS=false
|
|
282
304
|
if [ -n "${{ env.HEALTHCHECK_PATH }}" ]; then
|
|
283
|
-
|
|
305
|
+
m "${{ env.PAW_M_cicd_http_start }}" url="http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }}"
|
|
284
306
|
|
|
285
307
|
for i in $(seq 1 ${{ env.HEALTHCHECK_MAX_RETRIES }}); do
|
|
286
|
-
|
|
308
|
+
m "${{ env.PAW_M_cicd_http_try }}" i="$i" max="${{ env.HEALTHCHECK_MAX_RETRIES }}"
|
|
287
309
|
|
|
288
310
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:${PORT}${{ env.HEALTHCHECK_PATH }} || echo "000")
|
|
289
311
|
|
|
290
312
|
if [ "$HTTP_CODE" = "200" ]; then
|
|
291
|
-
|
|
313
|
+
m "${{ env.PAW_M_cicd_http_ok }}" code="$HTTP_CODE"
|
|
292
314
|
HEALTHCHECK_SUCCESS=true
|
|
293
315
|
break
|
|
294
316
|
else
|
|
295
|
-
|
|
317
|
+
m "${{ env.PAW_M_cicd_http_fail }}" code="$HTTP_CODE"
|
|
296
318
|
if [ $i -lt ${{ env.HEALTHCHECK_MAX_RETRIES }} ]; then
|
|
297
|
-
|
|
319
|
+
m "${{ env.PAW_M_cicd_http_retry }}" interval="${{ env.HEALTHCHECK_RETRY_INTERVAL }}"
|
|
298
320
|
sleep ${{ env.HEALTHCHECK_RETRY_INTERVAL }}
|
|
299
321
|
fi
|
|
300
322
|
fi
|
|
301
323
|
done
|
|
302
324
|
|
|
303
|
-
# HTTP
|
|
325
|
+
# If the HTTP check fails, fall back to log pattern matching
|
|
304
326
|
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
305
|
-
|
|
327
|
+
m "${{ env.PAW_M_cicd_http_final_fail }}"
|
|
306
328
|
fi
|
|
307
329
|
else
|
|
308
|
-
|
|
330
|
+
m "${{ env.PAW_M_cicd_http_skipped }}"
|
|
309
331
|
fi
|
|
310
332
|
|
|
311
|
-
# 3
|
|
333
|
+
# Step 3: log pattern matching (when HTTP failed or the HTTP check is disabled)
|
|
312
334
|
if [ "$HEALTHCHECK_SUCCESS" = "false" ]; then
|
|
313
|
-
|
|
335
|
+
m "${{ env.PAW_M_cicd_log_start }}" pattern="${{ env.HEALTHCHECK_LOG_PATTERN }}"
|
|
314
336
|
|
|
315
337
|
if SUDO docker logs $CONTAINER_NAME 2>&1 | grep -q "${{ env.HEALTHCHECK_LOG_PATTERN }}"; then
|
|
316
|
-
|
|
338
|
+
m "${{ env.PAW_M_cicd_log_ok }}"
|
|
317
339
|
HEALTHCHECK_SUCCESS=true
|
|
318
340
|
else
|
|
319
|
-
|
|
341
|
+
m "${{ env.PAW_M_cicd_log_fail }}"
|
|
320
342
|
echo ""
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
343
|
+
m "${{ env.PAW_M_cicd_debug_title }}"
|
|
344
|
+
m "${{ env.PAW_M_cicd_debug_pattern }}" pattern="${{ env.HEALTHCHECK_LOG_PATTERN }}"
|
|
345
|
+
m "${{ env.PAW_M_cicd_debug_container }}" container="$CONTAINER_NAME"
|
|
346
|
+
m "${{ env.PAW_M_cicd_debug_image }}" image="${{ secrets.DOCKERHUB_USERNAME }}/${PROJECT_NAME}:${BRANCH}"
|
|
347
|
+
m "${{ env.PAW_M_cicd_debug_port }}" port="${PORT}:${{ env.CONTAINER_INTERNAL_PORT }}"
|
|
348
|
+
m "${{ env.PAW_M_cicd_debug_volume }}" volume="$VOLUME_OPTS"
|
|
327
349
|
echo ""
|
|
328
|
-
|
|
350
|
+
m "${{ env.PAW_M_cicd_debug_logs }}"
|
|
329
351
|
SUDO docker logs --tail 50 $CONTAINER_NAME || true
|
|
330
352
|
exit 1
|
|
331
353
|
fi
|
|
332
354
|
fi
|
|
333
355
|
|
|
334
356
|
if [ "$HEALTHCHECK_SUCCESS" = "true" ]; then
|
|
335
|
-
|
|
357
|
+
m "${{ env.PAW_M_cicd_health_done }}" container="$CONTAINER_NAME"
|
|
336
358
|
fi
|
|
337
359
|
|
|
338
|
-
|
|
360
|
+
m "${{ env.PAW_M_cicd_deploy_done }}"
|
|
339
361
|
echo ""
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
362
|
+
m "${{ env.PAW_M_cicd_summary_title }}"
|
|
363
|
+
m "${{ env.PAW_M_cicd_summary_project }}" project="$PROJECT_NAME"
|
|
364
|
+
m "${{ env.PAW_M_cicd_summary_branch }}" branch="$BRANCH"
|
|
365
|
+
m "${{ env.PAW_M_cicd_summary_container }}" container="$CONTAINER_NAME"
|
|
366
|
+
m "${{ env.PAW_M_cicd_summary_port }}" port="$PORT"
|
|
367
|
+
m "${{ env.PAW_M_cicd_summary_time }}" time="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
346
368
|
echo ""
|
|
347
|
-
|
|
369
|
+
m "${{ env.PAW_M_cicd_summary_url }}" url="http://${{ secrets.SERVER_HOST }}:${PORT}/docs"
|