@ranger1/dx 0.1.84 → 0.1.85
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.
|
@@ -38,6 +38,17 @@ function resolveVerifyConfig(verifyConfig = {}) {
|
|
|
38
38
|
healthCheckConfig.timeoutSeconds == null
|
|
39
39
|
? 10
|
|
40
40
|
: requirePositiveInteger(healthCheckConfig.timeoutSeconds, 'verify.healthCheck.timeoutSeconds'),
|
|
41
|
+
maxWaitSeconds:
|
|
42
|
+
healthCheckConfig.maxWaitSeconds == null
|
|
43
|
+
? 24
|
|
44
|
+
: requirePositiveInteger(healthCheckConfig.maxWaitSeconds, 'verify.healthCheck.maxWaitSeconds'),
|
|
45
|
+
retryIntervalSeconds:
|
|
46
|
+
healthCheckConfig.retryIntervalSeconds == null
|
|
47
|
+
? 2
|
|
48
|
+
: requirePositiveInteger(
|
|
49
|
+
healthCheckConfig.retryIntervalSeconds,
|
|
50
|
+
'verify.healthCheck.retryIntervalSeconds',
|
|
51
|
+
),
|
|
41
52
|
},
|
|
42
53
|
}
|
|
43
54
|
}
|
|
@@ -28,6 +28,7 @@ export function parseRemoteResult({ stdout = '', stderr = '', exitCode = 0 }) {
|
|
|
28
28
|
message: 'ok',
|
|
29
29
|
rollbackAttempted: false,
|
|
30
30
|
rollbackSucceeded: null,
|
|
31
|
+
summary: null,
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -38,5 +39,6 @@ export function parseRemoteResult({ stdout = '', stderr = '', exitCode = 0 }) {
|
|
|
38
39
|
message,
|
|
39
40
|
rollbackAttempted: false,
|
|
40
41
|
rollbackSucceeded: null,
|
|
42
|
+
summary: null,
|
|
41
43
|
}
|
|
42
44
|
}
|
|
@@ -32,6 +32,8 @@ export function buildRemoteDeployScript(phaseModel = []) {
|
|
|
32
32
|
const shouldMigrate = deploy.prismaMigrateDeploy !== false && deploy.skipMigration !== true
|
|
33
33
|
const healthCheckUrl = healthCheck?.url ? String(healthCheck.url) : ''
|
|
34
34
|
const healthCheckTimeoutSeconds = Number(healthCheck?.timeoutSeconds || 10)
|
|
35
|
+
const healthCheckMaxWaitSeconds = Number(healthCheck?.maxWaitSeconds || 24)
|
|
36
|
+
const healthCheckRetryIntervalSeconds = Number(healthCheck?.retryIntervalSeconds || 2)
|
|
35
37
|
|
|
36
38
|
return `#!/usr/bin/env bash
|
|
37
39
|
set -euo pipefail
|
|
@@ -55,6 +57,8 @@ SERVICE_NAME=${escapeShell(serviceName)}
|
|
|
55
57
|
START_ENTRY=${escapeShell(startupEntry)}
|
|
56
58
|
HEALTHCHECK_URL=${escapeShell(healthCheckUrl)}
|
|
57
59
|
HEALTHCHECK_TIMEOUT_SECONDS=${healthCheckTimeoutSeconds}
|
|
60
|
+
HEALTHCHECK_MAX_WAIT_SECONDS=${healthCheckMaxWaitSeconds}
|
|
61
|
+
HEALTHCHECK_RETRY_DELAY_SECONDS=${healthCheckRetryIntervalSeconds}
|
|
58
62
|
KEEP_RELEASES=${keepReleases}
|
|
59
63
|
SHOULD_GENERATE=${shouldGenerate ? '1' : '0'}
|
|
60
64
|
SHOULD_MIGRATE=${shouldMigrate ? '1' : '0'}
|
|
@@ -82,6 +86,7 @@ emit_result() {
|
|
|
82
86
|
local message="$3"
|
|
83
87
|
local rollback_attempted="$4"
|
|
84
88
|
local rollback_succeeded="$5"
|
|
89
|
+
local summary_json="\${6:-null}"
|
|
85
90
|
if [[ "$RESULT_EMITTED" -eq 1 ]]; then
|
|
86
91
|
return
|
|
87
92
|
fi
|
|
@@ -89,8 +94,8 @@ emit_result() {
|
|
|
89
94
|
message="\${message//\\\\/\\\\\\\\}"
|
|
90
95
|
message="\${message//\"/\\\\\"}"
|
|
91
96
|
message="\${message//$'\\n'/\\\\n}"
|
|
92
|
-
printf 'DX_REMOTE_RESULT={"ok":%s,"phase":"%s","message":"%s","rollbackAttempted":%s,"rollbackSucceeded":%s}\\n' \\
|
|
93
|
-
"$ok" "$phase" "$message" "$rollback_attempted" "$rollback_succeeded"
|
|
97
|
+
printf 'DX_REMOTE_RESULT={"ok":%s,"phase":"%s","message":"%s","rollbackAttempted":%s,"rollbackSucceeded":%s,"summary":%s}\\n' \\
|
|
98
|
+
"$ok" "$phase" "$message" "$rollback_attempted" "$rollback_succeeded" "$summary_json"
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
cleanup() {
|
|
@@ -189,6 +194,16 @@ run_with_env() {
|
|
|
189
194
|
)
|
|
190
195
|
}
|
|
191
196
|
|
|
197
|
+
json_escape() {
|
|
198
|
+
local value="\${1-}"
|
|
199
|
+
value="\${value//\\\\/\\\\\\\\}"
|
|
200
|
+
value="\${value//\"/\\\\\"}"
|
|
201
|
+
value="\${value//$'\\n'/\\\\n}"
|
|
202
|
+
value="\${value//$'\\r'/\\\\r}"
|
|
203
|
+
value="\${value//$'\\t'/\\\\t}"
|
|
204
|
+
printf '%s' "$value"
|
|
205
|
+
}
|
|
206
|
+
|
|
192
207
|
read_pm2_env_var() {
|
|
193
208
|
local key="$1"
|
|
194
209
|
pm2 jlist | node -e '
|
|
@@ -200,6 +215,32 @@ read_pm2_env_var() {
|
|
|
200
215
|
' "$key" "$SERVICE_NAME"
|
|
201
216
|
}
|
|
202
217
|
|
|
218
|
+
read_pm2_status() {
|
|
219
|
+
pm2 jlist | node -e '
|
|
220
|
+
const fs = require("node:fs")
|
|
221
|
+
const list = JSON.parse(fs.readFileSync(0, "utf8"))
|
|
222
|
+
const app = list.find(item => item?.name === process.argv[1])
|
|
223
|
+
process.stdout.write(String(app?.pm2_env?.status || ""))
|
|
224
|
+
' "$SERVICE_NAME"
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
build_summary_json() {
|
|
228
|
+
local release_name="$1"
|
|
229
|
+
local current_release_path="$2"
|
|
230
|
+
local service_status="$3"
|
|
231
|
+
local app_env="$4"
|
|
232
|
+
local node_env="$5"
|
|
233
|
+
local health_url="$6"
|
|
234
|
+
printf '{"releaseName":"%s","currentRelease":"%s","serviceName":"%s","serviceStatus":"%s","appEnv":"%s","nodeEnv":"%s","healthUrl":"%s"}' \
|
|
235
|
+
"$(json_escape "$release_name")" \
|
|
236
|
+
"$(json_escape "$current_release_path")" \
|
|
237
|
+
"$(json_escape "$SERVICE_NAME")" \
|
|
238
|
+
"$(json_escape "$service_status")" \
|
|
239
|
+
"$(json_escape "$app_env")" \
|
|
240
|
+
"$(json_escape "$node_env")" \
|
|
241
|
+
"$(json_escape "$health_url")"
|
|
242
|
+
}
|
|
243
|
+
|
|
203
244
|
attempt_pm2_restore() {
|
|
204
245
|
if [[ -z "$PREVIOUS_CURRENT_TARGET" || ! -e "$PREVIOUS_CURRENT_TARGET/$ECOSYSTEM_CONFIG" ]]; then
|
|
205
246
|
ROLLBACK_SUCCEEDED=false
|
|
@@ -366,6 +407,7 @@ if [[ "$START_MODE" == "pm2" ]]; then
|
|
|
366
407
|
exit 1
|
|
367
408
|
fi
|
|
368
409
|
|
|
410
|
+
pm2_service_status="$(read_pm2_status)"
|
|
369
411
|
pm2_app_env="$(read_pm2_env_var APP_ENV)"
|
|
370
412
|
if [[ "$pm2_app_env" != "$EXPECTED_APP_ENV" ]]; then
|
|
371
413
|
echo "APP_ENV 不匹配,期望=$EXPECTED_APP_ENV,实际=\${pm2_app_env:-<empty>}" >&2
|
|
@@ -382,9 +424,20 @@ if [[ "$START_MODE" == "pm2" ]]; then
|
|
|
382
424
|
fi
|
|
383
425
|
|
|
384
426
|
if [[ -n "$HEALTHCHECK_URL" ]]; then
|
|
385
|
-
|
|
427
|
+
healthcheck_started_at="$(date +%s)"
|
|
428
|
+
until curl -fsS --max-time "$HEALTHCHECK_TIMEOUT_SECONDS" "$HEALTHCHECK_URL" >/dev/null; do
|
|
429
|
+
healthcheck_elapsed_seconds=$(( $(date +%s) - healthcheck_started_at ))
|
|
430
|
+
if [[ "$healthcheck_elapsed_seconds" -ge "$HEALTHCHECK_MAX_WAIT_SECONDS" ]]; then
|
|
431
|
+
echo "health check failed within $HEALTHCHECK_MAX_WAIT_SECONDS seconds: $HEALTHCHECK_URL" >&2
|
|
432
|
+
exit 1
|
|
433
|
+
fi
|
|
434
|
+
sleep "$HEALTHCHECK_RETRY_DELAY_SECONDS"
|
|
435
|
+
done
|
|
386
436
|
fi
|
|
387
437
|
|
|
438
|
+
summary_service_status="\${pm2_service_status:-direct-attached}"
|
|
439
|
+
summary_json="$(build_summary_json "$VERSION_NAME" "$current_release" "$summary_service_status" "$EXPECTED_APP_ENV" "$EXPECTED_NODE_ENV" "$HEALTHCHECK_URL")"
|
|
440
|
+
|
|
388
441
|
CURRENT_PHASE="cleanup"
|
|
389
442
|
echo "DX_REMOTE_PHASE=cleanup"
|
|
390
443
|
release_count=0
|
|
@@ -402,6 +455,6 @@ done < <(
|
|
|
402
455
|
fi
|
|
403
456
|
)
|
|
404
457
|
|
|
405
|
-
emit_result true "cleanup" "ok" false null
|
|
458
|
+
emit_result true "cleanup" "ok" false null "$summary_json"
|
|
406
459
|
`
|
|
407
460
|
}
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import { buildBackendArtifact } from './backend-artifact-deploy/artifact-builder.js'
|
|
2
2
|
import { resolveBackendDeployConfig } from './backend-artifact-deploy/config.js'
|
|
3
3
|
import { deployBackendArtifactRemotely } from './backend-artifact-deploy/remote-transport.js'
|
|
4
|
+
import { logger as defaultLogger } from './logger.js'
|
|
5
|
+
|
|
6
|
+
function printSuccessfulDeploySummary(result, logger) {
|
|
7
|
+
const summary = result?.summary
|
|
8
|
+
if (!summary) return
|
|
9
|
+
|
|
10
|
+
logger.success(`后端部署成功: ${summary.releaseName || 'unknown-release'}`)
|
|
11
|
+
if (summary.currentRelease) {
|
|
12
|
+
logger.info(`[deploy-summary] current=${summary.currentRelease}`)
|
|
13
|
+
}
|
|
14
|
+
if (summary.serviceName || summary.serviceStatus) {
|
|
15
|
+
logger.info(
|
|
16
|
+
`[deploy-summary] service=${summary.serviceName || 'unknown'} status=${summary.serviceStatus || 'unknown'}`,
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
if (summary.appEnv || summary.nodeEnv) {
|
|
20
|
+
logger.info(
|
|
21
|
+
`[deploy-summary] APP_ENV=${summary.appEnv || '<empty>'} NODE_ENV=${summary.nodeEnv || '<empty>'}`,
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
if (summary.healthUrl) {
|
|
25
|
+
logger.info(`[deploy-summary] health=${summary.healthUrl}`)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
4
28
|
|
|
5
29
|
export async function runBackendArtifactDeploy({
|
|
6
30
|
cli,
|
|
@@ -9,6 +33,7 @@ export async function runBackendArtifactDeploy({
|
|
|
9
33
|
environment,
|
|
10
34
|
deps = {},
|
|
11
35
|
}) {
|
|
36
|
+
const logger = deps.logger || defaultLogger
|
|
12
37
|
const resolveConfig = deps.resolveConfig || resolveBackendDeployConfig
|
|
13
38
|
const buildArtifact = deps.buildArtifact || buildBackendArtifact
|
|
14
39
|
const deployRemotely =
|
|
@@ -28,5 +53,9 @@ export async function runBackendArtifactDeploy({
|
|
|
28
53
|
return bundle
|
|
29
54
|
}
|
|
30
55
|
|
|
31
|
-
|
|
56
|
+
const result = await deployRemotely(config, bundle, deps)
|
|
57
|
+
if (result?.ok) {
|
|
58
|
+
printSuccessfulDeploySummary(result, logger)
|
|
59
|
+
}
|
|
60
|
+
return result
|
|
32
61
|
}
|