onepipeline-api-cli 0.58.0 → 0.58.1
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/bin/onepipeline-api.js +112 -38
- package/package.json +6 -6
package/bin/onepipeline-api.js
CHANGED
|
@@ -7,9 +7,18 @@
|
|
|
7
7
|
// compile, no download at install time. The platform-specific binary ships
|
|
8
8
|
// inside a per-platform package (`onepipeline-api-cli-<platform>-<arch>`)
|
|
9
9
|
// declared in this package's `optionalDependencies`; npm installs only the one
|
|
10
|
-
// whose `os`/`cpu` match the host, and this shim resolves it and
|
|
10
|
+
// whose `os`/`cpu` match the host, and this shim resolves it and runs it with
|
|
11
11
|
// the caller's argv.
|
|
12
12
|
//
|
|
13
|
+
// On Linux and macOS it *becomes* the binary (`process.execve`): the PID the
|
|
14
|
+
// caller started is the server's own, so a signal sent to it reaches the server,
|
|
15
|
+
// a parent-PID check sees the server, and there is no node process left to
|
|
16
|
+
// orphan anything. There is no other path on those platforms: a node without
|
|
17
|
+
// `process.execve` (a release this package's `engines` excludes) is refused
|
|
18
|
+
// before anything starts, naming that range. Windows has no exec,
|
|
19
|
+
// so there the binary runs as a child this process supervises; `supervise`
|
|
20
|
+
// below says how that child is kept faithful.
|
|
21
|
+
//
|
|
13
22
|
// This file is committed source; the version and the optionalDependency
|
|
14
23
|
// versions are stamped from Cargo.toml at publish time by scripts/npm-build.mjs,
|
|
15
24
|
// which also generates the per-platform packages from the release binaries.
|
|
@@ -89,49 +98,114 @@ function binaryPath() {
|
|
|
89
98
|
}
|
|
90
99
|
}
|
|
91
100
|
|
|
92
|
-
// The signals that mean "stop",
|
|
93
|
-
//
|
|
94
|
-
// A supervisor — systemd, Docker, a CI job, the smoke test in
|
|
95
|
-
// scripts/smoke-published.sh — stops this command by signalling the process it
|
|
96
|
-
// started, which is *this* one and not the binary behind it. Under a synchronous
|
|
97
|
-
// spawn node has no chance to react: the default disposition kills the launcher
|
|
98
|
-
// where it stands, so the caller sees 128+15 instead of the server's own `0`,
|
|
99
|
-
// and the binary is left running with nothing waiting on it. So the child is
|
|
100
|
-
// spawned asynchronously and each of these is passed on to it, which is what
|
|
101
|
-
// lets the server run the graceful shutdown it already has and lets its exit
|
|
102
|
-
// status be the one the caller observes.
|
|
101
|
+
// The signals that mean "stop", for the Windows launcher, which runs the binary
|
|
102
|
+
// as a child rather than becoming it.
|
|
103
103
|
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
104
|
+
// Listening for one replaces node's default disposition, which would end the
|
|
105
|
+
// launcher where it stands and report a status the server never chose. Exactly
|
|
106
|
+
// the two the server installs handlers for (`StopSignal` in src/server.rs);
|
|
107
|
+
// listening for one the binary does not handle would trade a clean end for a
|
|
108
|
+
// launcher that outlives its terminal. On Windows node only ever raises
|
|
109
|
+
// `SIGINT`, for the console's Ctrl-C.
|
|
108
110
|
const STOP_SIGNALS = ["SIGTERM", "SIGINT"];
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
// Windows: run the binary as a child and make it indistinguishable from running
|
|
113
|
+
// it directly — its stdio is this process's, an interrupt reaches it as the same
|
|
114
|
+
// interrupt (the console delivers it to the child directly; this process passes
|
|
115
|
+
// nothing on), it cannot outlive this process, and this process exits with its
|
|
116
|
+
// status.
|
|
117
|
+
function supervise(binary, args) {
|
|
118
|
+
// Not detached: libuv puts every attached child in a job object that closes
|
|
119
|
+
// with this process, so a launcher that is ended — even by `TerminateProcess`
|
|
120
|
+
// — takes the server with it rather than orphaning it.
|
|
121
|
+
const child = spawn(binary, args, { stdio: "inherit" });
|
|
111
122
|
|
|
112
|
-
child.on("error", (error) => {
|
|
113
|
-
|
|
114
|
-
});
|
|
123
|
+
child.on("error", (error) => {
|
|
124
|
+
fail(`failed to launch the onepipeline-api binary: ${error.message}`);
|
|
125
|
+
});
|
|
115
126
|
|
|
116
|
-
for (const signal of STOP_SIGNALS) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
for (const signal of STOP_SIGNALS) {
|
|
128
|
+
// Caught, never passed on. The console already delivered the Ctrl-C to
|
|
129
|
+
// every process attached to it, the server included, and `child.kill` on
|
|
130
|
+
// Windows is `TerminateProcess`, which would cut short the very shutdown
|
|
131
|
+
// the interrupt started. This process only has to outlive it and report
|
|
132
|
+
// how it ended.
|
|
133
|
+
process.on(signal, () => {});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
child.on("exit", (status, signal) => {
|
|
137
|
+
// Propagate the child's exit code verbatim — a caller scripting against the
|
|
138
|
+
// documented exit codes depends on seeing them — or re-raise what ended it.
|
|
139
|
+
// The listeners go first: re-raising into one of them would swallow it.
|
|
140
|
+
for (const stop of STOP_SIGNALS) {
|
|
141
|
+
process.removeAllListeners(stop);
|
|
142
|
+
}
|
|
143
|
+
if (signal) {
|
|
144
|
+
process.kill(process.pid, signal);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
process.exit(status === null ? 1 : status);
|
|
148
|
+
});
|
|
121
149
|
}
|
|
122
150
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
for (const
|
|
130
|
-
process.
|
|
151
|
+
// Linux and macOS: replace this process with the binary, or fail before
|
|
152
|
+
// anything has started.
|
|
153
|
+
function become(binary, args) {
|
|
154
|
+
// An ignored signal stays ignored across exec, and node ignores these two at
|
|
155
|
+
// startup. Listening and then not listening hands each back to the default
|
|
156
|
+
// disposition, so the binary starts with the signals a shell would give it.
|
|
157
|
+
for (const inherited of ["SIGPIPE", "SIGXFSZ"]) {
|
|
158
|
+
process.on(inherited, () => {});
|
|
159
|
+
process.removeAllListeners(inherited);
|
|
131
160
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
161
|
+
// A file the kernel would refuse is reported here, before the call: node's
|
|
162
|
+
// 22 and 24 lines abort the whole process when `process.execve` fails rather
|
|
163
|
+
// than throwing, so past this point a refusal would print node's own crash
|
|
164
|
+
// report instead of saying what to do. Missing, not a file, and not
|
|
165
|
+
// executable are the refusals a broken install produces; one only the kernel
|
|
166
|
+
// can see (a binary for another architecture) still reaches the call.
|
|
167
|
+
const fs = require("node:fs");
|
|
168
|
+
try {
|
|
169
|
+
if (!fs.statSync(binary).isFile()) {
|
|
170
|
+
throw new Error(`${binary} is not a file`);
|
|
171
|
+
}
|
|
172
|
+
fs.accessSync(binary, fs.constants.X_OK);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
fail(
|
|
175
|
+
`failed to launch the onepipeline-api binary: ${error.message}. ` +
|
|
176
|
+
`Reinstall onepipeline-api-cli. ${OTHER_INSTALLS}`
|
|
177
|
+
);
|
|
135
178
|
}
|
|
136
|
-
|
|
137
|
-
|
|
179
|
+
// Returns only by throwing, and on this package's supported range only on a
|
|
180
|
+
// node without `process.execve` to call, or on a release that throws for a
|
|
181
|
+
// refusal the check above could not see. Either way nothing was started, and
|
|
182
|
+
// nothing will be — running the binary as a child instead is the process
|
|
183
|
+
// tree this launcher exists not to build.
|
|
184
|
+
if (typeof process.execve !== "function") {
|
|
185
|
+
// The supported range is this package's `engines`, read rather than
|
|
186
|
+
// restated, so the diagnostic cannot name a release the manifest refuses.
|
|
187
|
+
const { engines } = require("../package.json");
|
|
188
|
+
fail(
|
|
189
|
+
"failed to launch the onepipeline-api binary: this command replaces " +
|
|
190
|
+
"itself with the binary through process.execve, which needs Node " +
|
|
191
|
+
`${engines.node}; this is Node ${process.versions.node}. Upgrade Node. ` +
|
|
192
|
+
OTHER_INSTALLS
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
process.execve(binary, [binary, ...args], process.env);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
fail(
|
|
199
|
+
`failed to launch the onepipeline-api binary: ${error.message}. ` +
|
|
200
|
+
`Reinstall onepipeline-api-cli. ${OTHER_INSTALLS}`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const binary = binaryPath();
|
|
206
|
+
const args = process.argv.slice(2);
|
|
207
|
+
if (process.platform === "win32") {
|
|
208
|
+
supervise(binary, args);
|
|
209
|
+
} else {
|
|
210
|
+
become(binary, args);
|
|
211
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onepipeline-api-cli",
|
|
3
|
-
"version": "0.58.
|
|
3
|
+
"version": "0.58.1",
|
|
4
4
|
"description": "Read API and browser view for onepipeline runs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
"bin/onepipeline-api.js"
|
|
25
25
|
],
|
|
26
26
|
"engines": {
|
|
27
|
-
"node": ">=
|
|
27
|
+
"node": ">=22.15.0 <23 || >=23.11.0"
|
|
28
28
|
},
|
|
29
29
|
"optionalDependencies": {
|
|
30
|
-
"onepipeline-api-cli-linux-x64": "0.58.
|
|
31
|
-
"onepipeline-api-cli-linux-arm64": "0.58.
|
|
32
|
-
"onepipeline-api-cli-darwin-arm64": "0.58.
|
|
33
|
-
"onepipeline-api-cli-win32-x64": "0.58.
|
|
30
|
+
"onepipeline-api-cli-linux-x64": "0.58.1",
|
|
31
|
+
"onepipeline-api-cli-linux-arm64": "0.58.1",
|
|
32
|
+
"onepipeline-api-cli-darwin-arm64": "0.58.1",
|
|
33
|
+
"onepipeline-api-cli-win32-x64": "0.58.1"
|
|
34
34
|
}
|
|
35
35
|
}
|