@yunsoft/yuncms 0.1.24 → 0.1.25
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/package.json +3 -3
- package/src/runtime-probe.js +23 -4
- package/src/start-command.js +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yunsoft/yuncms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"description": "Command-line setup and runtime launcher for YunCMS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"test": "node --test"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@yunsoft/yuncms-api": "0.1.
|
|
34
|
-
"@yunsoft/yuncms-core": "0.1.
|
|
33
|
+
"@yunsoft/yuncms-api": "0.1.25",
|
|
34
|
+
"@yunsoft/yuncms-core": "0.1.25"
|
|
35
35
|
},
|
|
36
36
|
"funding": {
|
|
37
37
|
"type": "github",
|
package/src/runtime-probe.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
1
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
|
|
4
4
|
import { MAINTENANCE_BYPASS_ENV } from '@yunsoft/yuncms-core';
|
|
5
|
+
import { ATTACHED_RUNTIME_ENV } from './start-command.js';
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_PROBE_SHUTDOWN_GRACE_MS = 12_000;
|
|
5
8
|
|
|
6
9
|
function delay(ms) {
|
|
7
10
|
return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
|
|
@@ -9,8 +12,10 @@ function delay(ms) {
|
|
|
9
12
|
|
|
10
13
|
function probeError(code, message, details = {}) {
|
|
11
14
|
const error = new Error(message);
|
|
15
|
+
const { code: exitCode, ...otherDetails } = details;
|
|
16
|
+
Object.assign(error, otherDetails);
|
|
17
|
+
if (Object.hasOwn(details, 'code')) error.exitCode = exitCode;
|
|
12
18
|
error.code = code;
|
|
13
|
-
Object.assign(error, details);
|
|
14
19
|
return error;
|
|
15
20
|
}
|
|
16
21
|
|
|
@@ -22,7 +27,7 @@ export async function verifyInstalledRuntime({
|
|
|
22
27
|
fetchFn = globalThis.fetch,
|
|
23
28
|
spawnProcess = spawn,
|
|
24
29
|
timeoutMs = 15_000,
|
|
25
|
-
shutdownGraceMs =
|
|
30
|
+
shutdownGraceMs = DEFAULT_PROBE_SHUTDOWN_GRACE_MS,
|
|
26
31
|
} = {}) {
|
|
27
32
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
28
33
|
throw probeError('UPDATE_PROBE_PORT_INVALID', `Invalid probe port: ${port}`);
|
|
@@ -48,6 +53,7 @@ export async function verifyInstalledRuntime({
|
|
|
48
53
|
PORT: String(port),
|
|
49
54
|
STUDIO_ORIGIN: origin,
|
|
50
55
|
AUTH_PUBLIC_URL: origin,
|
|
56
|
+
[ATTACHED_RUNTIME_ENV]: '1',
|
|
51
57
|
};
|
|
52
58
|
if (maintenanceBypassToken !== null) childEnv[MAINTENANCE_BYPASS_ENV] = maintenanceBypassToken;
|
|
53
59
|
|
|
@@ -55,6 +61,7 @@ export async function verifyInstalledRuntime({
|
|
|
55
61
|
cwd,
|
|
56
62
|
env: childEnv,
|
|
57
63
|
stdio: ['ignore', 'ignore', 'pipe'],
|
|
64
|
+
detached: process.platform !== 'win32',
|
|
58
65
|
});
|
|
59
66
|
|
|
60
67
|
let stderr = '';
|
|
@@ -65,6 +72,7 @@ export async function verifyInstalledRuntime({
|
|
|
65
72
|
|
|
66
73
|
let settled = false;
|
|
67
74
|
let shutdownAttempted = false;
|
|
75
|
+
let readyAndStopped = false;
|
|
68
76
|
const exitPromise = new Promise((resolveExit, rejectExit) => {
|
|
69
77
|
child.once('error', (error) => {
|
|
70
78
|
if (settled) return;
|
|
@@ -81,9 +89,18 @@ export async function verifyInstalledRuntime({
|
|
|
81
89
|
|
|
82
90
|
function requestKill(signal) {
|
|
83
91
|
try {
|
|
92
|
+
if (signal === 'SIGKILL' && Number.isInteger(child.pid)) {
|
|
93
|
+
if (process.platform === 'win32') {
|
|
94
|
+
execFileSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { stdio: 'ignore', timeout: 1000 });
|
|
95
|
+
} else {
|
|
96
|
+
process.kill(-child.pid, signal);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
84
100
|
child.kill(signal);
|
|
85
101
|
} catch {
|
|
86
|
-
//
|
|
102
|
+
// ESRCH is expected for an exited group; keep a bounded direct-child fallback.
|
|
103
|
+
try { child.kill(signal); } catch {}
|
|
87
104
|
}
|
|
88
105
|
}
|
|
89
106
|
|
|
@@ -145,6 +162,7 @@ export async function verifyInstalledRuntime({
|
|
|
145
162
|
if (result.code !== 0 && result.signal !== 'SIGTERM') {
|
|
146
163
|
throw probeError('UPDATE_PROBE_SHUTDOWN_FAILED', 'Updated runtime did not stop cleanly', result);
|
|
147
164
|
}
|
|
165
|
+
readyAndStopped = true;
|
|
148
166
|
return { ready: true, origin };
|
|
149
167
|
}
|
|
150
168
|
}
|
|
@@ -161,5 +179,6 @@ export async function verifyInstalledRuntime({
|
|
|
161
179
|
if (!settled && !shutdownAttempted) {
|
|
162
180
|
await stopProbe({ strict: false }).catch(() => null);
|
|
163
181
|
}
|
|
182
|
+
if (settled && !readyAndStopped && Number.isInteger(child.pid)) requestKill('SIGKILL');
|
|
164
183
|
}
|
|
165
184
|
}
|
package/src/start-command.js
CHANGED
|
@@ -3,6 +3,9 @@ import { fileURLToPath } from 'node:url';
|
|
|
3
3
|
|
|
4
4
|
import { assertMaintenanceStartupAllowed } from '@yunsoft/yuncms-core';
|
|
5
5
|
|
|
6
|
+
// Internal updater transport flag: the probe owns the entire process group.
|
|
7
|
+
export const ATTACHED_RUNTIME_ENV = 'YUNCMS_CLI_ATTACHED_RUNTIME';
|
|
8
|
+
|
|
6
9
|
export async function runStartCommand({
|
|
7
10
|
env = process.env,
|
|
8
11
|
cwd = process.cwd(),
|
|
@@ -21,7 +24,7 @@ export async function runStartCommand({
|
|
|
21
24
|
cwd,
|
|
22
25
|
env: { ...env },
|
|
23
26
|
stdio: 'inherit',
|
|
24
|
-
detached: process.platform !== 'win32',
|
|
27
|
+
detached: process.platform !== 'win32' && env[ATTACHED_RUNTIME_ENV] !== '1',
|
|
25
28
|
});
|
|
26
29
|
|
|
27
30
|
return new Promise((resolve, reject) => {
|