@nubjs/nub 0.0.25 → 0.0.27
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/launch.js +30 -7
- package/package.json +14 -10
- package/postinstall.js +56 -0
package/bin/launch.js
CHANGED
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
// The native binary selects its verb from argv[0]'s basename (nub vs nubx); the
|
|
29
29
|
// healed trampoline exec's bin/<verb> in the platform package (which ships both
|
|
30
30
|
// names), so no argv0 override is needed past the heal.
|
|
31
|
-
const {
|
|
31
|
+
const { spawn } = require("child_process");
|
|
32
|
+
const os = require("os");
|
|
32
33
|
const fs = require("fs");
|
|
33
34
|
const path = require("path");
|
|
34
35
|
const { platformPackage } = require("../platform.js");
|
|
@@ -140,13 +141,35 @@ module.exports = function launch(argv0Name) {
|
|
|
140
141
|
healPathEntry(verb, binPath);
|
|
141
142
|
// This call still runs through Node; spawn the native binary. argv0 basename of
|
|
142
143
|
// binPath is the verb (bin/nub or bin/nubx), so the Rust CLI dispatches correctly
|
|
143
|
-
// without an argv0 override.
|
|
144
|
+
// without an argv0 override. We use async `spawn` (not `spawnSync`) ONLY so this
|
|
145
|
+
// Node launcher can forward terminating signals to the native child: `spawnSync`
|
|
146
|
+
// blocks the event loop, so a SIGTERM (docker stop on a `nub run` entrypoint whose
|
|
147
|
+
// first-ever call hasn't been healed to the sh trampoline yet) would terminate
|
|
148
|
+
// this launcher and orphan the workload. With async spawn we relay SIGTERM/INT/HUP
|
|
149
|
+
// to the child — which then relays to its own subtree — and mirror its exit
|
|
150
|
+
// status. (Subsequent calls skip Node entirely via the healed trampoline's `exec`,
|
|
151
|
+
// where signals reach the binary directly.)
|
|
144
152
|
const opts = { stdio: "inherit", windowsHide: true };
|
|
145
153
|
if (argv0Name) opts.argv0 = argv0Name; // belt-and-suspenders for the bin/nub fallback path
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
154
|
+
const child = spawn(binPath, process.argv.slice(2), opts);
|
|
155
|
+
let forwarding = true;
|
|
156
|
+
const forward = (sig) => {
|
|
157
|
+
if (forwarding && child.pid) {
|
|
158
|
+
try { process.kill(child.pid, sig); } catch {}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
for (const sig of ["SIGTERM", "SIGINT", "SIGHUP"]) process.on(sig, () => forward(sig));
|
|
162
|
+
child.on("error", (err) => {
|
|
163
|
+
console.error(`@nubjs/nub: failed to launch ${binPath}: ${err.message}`);
|
|
149
164
|
process.exit(1);
|
|
150
|
-
}
|
|
151
|
-
|
|
165
|
+
});
|
|
166
|
+
child.on("exit", (code, signal) => {
|
|
167
|
+
forwarding = false;
|
|
168
|
+
if (signal) {
|
|
169
|
+
// Mirror death-by-signal as 128+signo, matching the native binary and a shell.
|
|
170
|
+
const signo = (os.constants && os.constants.signals && os.constants.signals[signal]) || 0;
|
|
171
|
+
process.exit(signo ? 128 + signo : 1);
|
|
172
|
+
}
|
|
173
|
+
process.exit(code == null ? 1 : code);
|
|
174
|
+
});
|
|
152
175
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nubjs/nub",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"description": "TypeScript-first developer supertool — a fast script runner and TS runtime powered by Node.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/nubjs/nub",
|
|
@@ -9,18 +9,22 @@
|
|
|
9
9
|
"nub": "bin/nub",
|
|
10
10
|
"nubx": "bin/nubx"
|
|
11
11
|
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"postinstall": "node postinstall.js"
|
|
14
|
+
},
|
|
12
15
|
"files": [
|
|
13
16
|
"bin",
|
|
14
|
-
"platform.js"
|
|
17
|
+
"platform.js",
|
|
18
|
+
"postinstall.js"
|
|
15
19
|
],
|
|
16
20
|
"optionalDependencies": {
|
|
17
|
-
"@nubjs/nub-darwin-arm64": "0.0.
|
|
18
|
-
"@nubjs/nub-darwin-x64": "0.0.
|
|
19
|
-
"@nubjs/nub-linux-x64": "0.0.
|
|
20
|
-
"@nubjs/nub-linux-x64-musl": "0.0.
|
|
21
|
-
"@nubjs/nub-linux-arm64": "0.0.
|
|
22
|
-
"@nubjs/nub-linux-arm64-musl": "0.0.
|
|
23
|
-
"@nubjs/nub-win32-x64": "0.0.
|
|
24
|
-
"@nubjs/nub-win32-arm64": "0.0.
|
|
21
|
+
"@nubjs/nub-darwin-arm64": "0.0.27",
|
|
22
|
+
"@nubjs/nub-darwin-x64": "0.0.27",
|
|
23
|
+
"@nubjs/nub-linux-x64": "0.0.27",
|
|
24
|
+
"@nubjs/nub-linux-x64-musl": "0.0.27",
|
|
25
|
+
"@nubjs/nub-linux-arm64": "0.0.27",
|
|
26
|
+
"@nubjs/nub-linux-arm64-musl": "0.0.27",
|
|
27
|
+
"@nubjs/nub-win32-x64": "0.0.27",
|
|
28
|
+
"@nubjs/nub-win32-arm64": "0.0.27"
|
|
25
29
|
}
|
|
26
30
|
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Set the execute bit on the platform binary at INSTALL time.
|
|
3
|
+
//
|
|
4
|
+
// npm normalizes file modes on extract: a file referenced by a package's `bin`
|
|
5
|
+
// field lands 0o755, everything else 0o644. The platform packages
|
|
6
|
+
// (`@nubjs/nub-<platform>`) deliberately declare NO `bin` field — they're carriers
|
|
7
|
+
// selected by npm's os/cpu filters — so their `bin/nub` / `bin/nubx` extract 0o644
|
|
8
|
+
// (no +x). Something must add it back.
|
|
9
|
+
//
|
|
10
|
+
// `bin/launch.js` also chmods at runtime, but that runs as the END user and chmod
|
|
11
|
+
// only succeeds for the file's OWNER. The canonical container/CI pattern installs
|
|
12
|
+
// as root (`RUN npm i -g`) then drops to a non-root user (`USER app`); that user's
|
|
13
|
+
// first `nub` can't chmod a root-owned 0o644 binary and dies EACCES. THIS script
|
|
14
|
+
// runs as the INSTALLER (root, during the image build), so it sets the bit for
|
|
15
|
+
// everyone before any privilege drop. The launcher chmod stays as the fallback for
|
|
16
|
+
// PMs that skip postinstall — it just can't cover the non-owner case, which is why
|
|
17
|
+
// this exists. (Same shape as esbuild / @swc / @biomejs install-time chmod.)
|
|
18
|
+
//
|
|
19
|
+
// Best-effort and silent: a missing/already-executable binary, an unsupported
|
|
20
|
+
// platform, or a PM that ran us in a sandbox are all non-fatal — the launcher's
|
|
21
|
+
// runtime chmod is the second line of defense.
|
|
22
|
+
|
|
23
|
+
const fs = require("fs");
|
|
24
|
+
const path = require("path");
|
|
25
|
+
|
|
26
|
+
function chmodExecutable() {
|
|
27
|
+
let platformPackage;
|
|
28
|
+
try {
|
|
29
|
+
({ platformPackage } = require("./platform.js"));
|
|
30
|
+
} catch {
|
|
31
|
+
return; // platform.js absent (shouldn't happen) — let the launcher handle it.
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { pkg } = platformPackage();
|
|
35
|
+
if (!pkg) return; // unsupported platform — nothing to chmod.
|
|
36
|
+
|
|
37
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
38
|
+
for (const verb of ["nub", "nubx"]) {
|
|
39
|
+
let binPath;
|
|
40
|
+
try {
|
|
41
|
+
binPath = require.resolve(`${pkg}/bin/${verb}${ext}`);
|
|
42
|
+
} catch {
|
|
43
|
+
continue; // an older platform package may not ship bin/nubx.
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
// Preserve read/write bits, add execute for user/group/other (umask-free —
|
|
47
|
+
// an install-time binary should be runnable by whoever the image runs as).
|
|
48
|
+
const mode = fs.statSync(binPath).mode;
|
|
49
|
+
fs.chmodSync(binPath, mode | 0o111);
|
|
50
|
+
} catch {
|
|
51
|
+
// Not the owner, read-only store, etc. — the launcher chmod is the fallback.
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
chmodExecutable();
|