@zackees/soldr 0.7.28 → 0.7.30
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/soldr.js +35 -1
- package/package.json +1 -1
- package/scripts/install.js +137 -69
- package/scripts/test-npm-package.js +73 -3
package/bin/soldr.js
CHANGED
|
@@ -6,7 +6,8 @@ const fs = require("fs");
|
|
|
6
6
|
const path = require("path");
|
|
7
7
|
|
|
8
8
|
const binaryName = process.platform === "win32" ? "soldr.exe" : "soldr";
|
|
9
|
-
const
|
|
9
|
+
const nativeDir = path.join(__dirname, "native");
|
|
10
|
+
const binaryPath = path.join(nativeDir, binaryName);
|
|
10
11
|
|
|
11
12
|
if (!fs.existsSync(binaryPath)) {
|
|
12
13
|
console.error(
|
|
@@ -19,8 +20,41 @@ if (!fs.existsSync(binaryPath)) {
|
|
|
19
20
|
process.exit(1);
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
// Wire the bundled zccache trio (zccache, zccache-daemon, zccache-fp,
|
|
24
|
+
// shipped alongside soldr in `bin/native/` since the combined-archive
|
|
25
|
+
// release format landed) into soldr's local-zccache resolution path.
|
|
26
|
+
// Soldr's `fetch_zccache_with_paths` checks SOLDR_ZCCACHE_LOCAL_DIR
|
|
27
|
+
// ahead of the managed-download chain, so this turns the bundled
|
|
28
|
+
// binaries into the active zccache automatically — no manual env
|
|
29
|
+
// setup, no managed fetch over the network. Users who explicitly set
|
|
30
|
+
// the env var themselves keep their override.
|
|
31
|
+
const exeExt = process.platform === "win32" ? ".exe" : "";
|
|
32
|
+
const childEnv = { ...process.env };
|
|
33
|
+
if (
|
|
34
|
+
!childEnv.SOLDR_ZCCACHE_LOCAL_DIR &&
|
|
35
|
+
fs.existsSync(path.join(nativeDir, `zccache${exeExt}`)) &&
|
|
36
|
+
fs.existsSync(path.join(nativeDir, `zccache-daemon${exeExt}`)) &&
|
|
37
|
+
fs.existsSync(path.join(nativeDir, `zccache-fp${exeExt}`))
|
|
38
|
+
) {
|
|
39
|
+
childEnv.SOLDR_ZCCACHE_LOCAL_DIR = nativeDir;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Same wiring for the bundled crgx binary (shipped alongside soldr
|
|
43
|
+
// since the crgx-bundling follow-up to the combined-archive release
|
|
44
|
+
// format). soldr's `fetch_tool_with_paths` honors
|
|
45
|
+
// SOLDR_CRGX_LOCAL_DIR ahead of the GitHub Releases / crates.io
|
|
46
|
+
// fetch chain, so `soldr crgx ...` runs the bundled binary with no
|
|
47
|
+
// network round trip. Caller-set overrides win.
|
|
48
|
+
if (
|
|
49
|
+
!childEnv.SOLDR_CRGX_LOCAL_DIR &&
|
|
50
|
+
fs.existsSync(path.join(nativeDir, `crgx${exeExt}`))
|
|
51
|
+
) {
|
|
52
|
+
childEnv.SOLDR_CRGX_LOCAL_DIR = nativeDir;
|
|
53
|
+
}
|
|
54
|
+
|
|
22
55
|
const child = childProcess.spawn(binaryPath, process.argv.slice(2), {
|
|
23
56
|
stdio: "inherit",
|
|
57
|
+
env: childEnv,
|
|
24
58
|
windowsHide: false,
|
|
25
59
|
});
|
|
26
60
|
|
package/package.json
CHANGED
package/scripts/install.js
CHANGED
|
@@ -12,43 +12,86 @@ const path = require("path");
|
|
|
12
12
|
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
13
13
|
const PACKAGE_JSON = require(path.join(PACKAGE_ROOT, "package.json"));
|
|
14
14
|
|
|
15
|
+
// Every release ships a single .tar.zst per target that bundles soldr
|
|
16
|
+
// alongside its matching-target zccache trio (zccache, zccache-daemon,
|
|
17
|
+
// zccache-fp) and a same-target crgx. One fetch installs everything,
|
|
18
|
+
// and `bin/soldr.js` wires SOLDR_ZCCACHE_LOCAL_DIR + SOLDR_CRGX_LOCAL_DIR
|
|
19
|
+
// to the install dir so soldr finds the sibling binaries without going
|
|
20
|
+
// through the managed-download path.
|
|
21
|
+
const ARCHIVE_EXT = "tar.zst";
|
|
22
|
+
|
|
15
23
|
const TARGETS = {
|
|
16
|
-
"linux-x64": {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
binary: "soldr",
|
|
25
|
-
},
|
|
26
|
-
"darwin-x64": {
|
|
27
|
-
triple: "x86_64-apple-darwin",
|
|
28
|
-
archive: "tar.gz",
|
|
29
|
-
binary: "soldr",
|
|
30
|
-
},
|
|
31
|
-
"darwin-arm64": {
|
|
32
|
-
triple: "aarch64-apple-darwin",
|
|
33
|
-
archive: "tar.gz",
|
|
34
|
-
binary: "soldr",
|
|
35
|
-
},
|
|
36
|
-
"win32-x64": {
|
|
37
|
-
triple: "x86_64-pc-windows-msvc",
|
|
38
|
-
archive: "zip",
|
|
39
|
-
binary: "soldr.exe",
|
|
40
|
-
},
|
|
41
|
-
"win32-arm64": {
|
|
42
|
-
triple: "aarch64-pc-windows-msvc",
|
|
43
|
-
archive: "zip",
|
|
44
|
-
binary: "soldr.exe",
|
|
45
|
-
},
|
|
24
|
+
"linux-x64-gnu": { triple: "x86_64-unknown-linux-gnu", binary: "soldr" },
|
|
25
|
+
"linux-x64-musl": { triple: "x86_64-unknown-linux-musl", binary: "soldr" },
|
|
26
|
+
"linux-arm64-gnu": { triple: "aarch64-unknown-linux-gnu", binary: "soldr" },
|
|
27
|
+
"linux-arm64-musl": { triple: "aarch64-unknown-linux-musl", binary: "soldr" },
|
|
28
|
+
"darwin-x64": { triple: "x86_64-apple-darwin", binary: "soldr" },
|
|
29
|
+
"darwin-arm64": { triple: "aarch64-apple-darwin", binary: "soldr" },
|
|
30
|
+
"win32-x64": { triple: "x86_64-pc-windows-msvc", binary: "soldr.exe" },
|
|
31
|
+
"win32-arm64": { triple: "aarch64-pc-windows-msvc", binary: "soldr.exe" },
|
|
46
32
|
};
|
|
47
33
|
|
|
48
|
-
|
|
49
|
-
|
|
34
|
+
// Files we expect to find at the root of every extracted release
|
|
35
|
+
// archive. Names line up with what `release-auto.yml`'s
|
|
36
|
+
// `Fetch matched zccache release`, `Stage soldr binary`, and
|
|
37
|
+
// `Build crgx from pinned source` steps drop into `dist/package/`
|
|
38
|
+
// before the tar.zst is built. `.exe` suffix is appended at install
|
|
39
|
+
// time based on `target.binary`.
|
|
40
|
+
const BUNDLED_BINARIES = [
|
|
41
|
+
"soldr",
|
|
42
|
+
"zccache",
|
|
43
|
+
"zccache-daemon",
|
|
44
|
+
"zccache-fp",
|
|
45
|
+
"crgx",
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// Detect whether the running Linux uses musl or glibc. Three layered probes:
|
|
49
|
+
// 1. process.report.header.glibcVersionRuntime is the documented Node
|
|
50
|
+
// surface for runtime glibc version — present on glibc, absent /
|
|
51
|
+
// empty on musl. Same approach used by @swc/core, @napi-rs/*, etc.
|
|
52
|
+
// 2. Filesystem check for `/lib/ld-musl-*.so.1` covers cases where the
|
|
53
|
+
// Node binary itself is glibc (e.g. someone running glibc Node on
|
|
54
|
+
// alpine via apk add nodejs-current) but the system is musl — the
|
|
55
|
+
// soldr binary we download must match the SYSTEM libc, not Node's.
|
|
56
|
+
// 3. Final fallback: assume glibc. The mismatch will surface
|
|
57
|
+
// immediately at runtime as "soldr: not found" or
|
|
58
|
+
// "soldr: error while loading shared libraries", which is louder
|
|
59
|
+
// than silently downloading the wrong tarball.
|
|
60
|
+
function detectLibc(platform = process.platform) {
|
|
61
|
+
if (platform !== "linux") {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const header = process.report && process.report.getReport && process.report.getReport().header;
|
|
66
|
+
if (header && typeof header.glibcVersionRuntime === "string" && header.glibcVersionRuntime.length > 0) {
|
|
67
|
+
return "gnu";
|
|
68
|
+
}
|
|
69
|
+
if (header && Object.prototype.hasOwnProperty.call(header, "glibcVersionRuntime")) {
|
|
70
|
+
// Field present but empty / null → Node was built against musl.
|
|
71
|
+
return "musl";
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
// process.report can throw on locked-down environments; fall through.
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const entries = fs.readdirSync("/lib");
|
|
78
|
+
if (entries.some((name) => /^ld-musl-.+\.so\.1$/.test(name))) {
|
|
79
|
+
return "musl";
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
// /lib may not be readable in heavily sandboxed containers; fall through.
|
|
83
|
+
}
|
|
84
|
+
return "gnu";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function platformTarget(platform = process.platform, arch = process.arch, libc = detectLibc(platform)) {
|
|
88
|
+
const key =
|
|
89
|
+
platform === "linux"
|
|
90
|
+
? `${platform}-${arch}-${libc || "gnu"}`
|
|
91
|
+
: `${platform}-${arch}`;
|
|
92
|
+
const target = TARGETS[key];
|
|
50
93
|
if (!target) {
|
|
51
|
-
throw new Error(`unsupported platform for soldr npm package: ${
|
|
94
|
+
throw new Error(`unsupported platform for soldr npm package: ${key}`);
|
|
52
95
|
}
|
|
53
96
|
return target;
|
|
54
97
|
}
|
|
@@ -128,31 +171,32 @@ function run(command, args, options = {}) {
|
|
|
128
171
|
}
|
|
129
172
|
}
|
|
130
173
|
|
|
131
|
-
function extractArchive(archivePath,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
return;
|
|
174
|
+
function extractArchive(archivePath, destination) {
|
|
175
|
+
// GNU tar 1.31+ and bsdtar (default on macOS / Windows) both support
|
|
176
|
+
// `--zstd` for zstandard. If a host's tar predates that flag, fall
|
|
177
|
+
// back to `--use-compress-program=unzstd` which only needs the
|
|
178
|
+
// `unzstd` CLI on PATH — installed alongside `zstd` on every modern
|
|
179
|
+
// package manager. As a last resort, `zstd` decompresses to a temp
|
|
180
|
+
// .tar and we extract that explicitly.
|
|
181
|
+
const attempts = [
|
|
182
|
+
["tar", ["--zstd", "-xf", archivePath, "-C", destination]],
|
|
183
|
+
["tar", ["--use-compress-program=unzstd", "-xf", archivePath, "-C", destination]],
|
|
184
|
+
];
|
|
185
|
+
for (const [cmd, args] of attempts) {
|
|
186
|
+
const result = childProcess.spawnSync(cmd, args, { stdio: "inherit" });
|
|
187
|
+
if (!result.error && result.status === 0) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (result.error && result.error.code === "ENOENT") {
|
|
191
|
+
throw result.error;
|
|
192
|
+
}
|
|
193
|
+
// status != 0 → fall through to the next strategy.
|
|
153
194
|
}
|
|
154
|
-
|
|
155
|
-
|
|
195
|
+
// Last resort: decompress to a sibling .tar, then untar.
|
|
196
|
+
const intermediate = `${archivePath}.tar`;
|
|
197
|
+
run("zstd", ["-d", "-o", intermediate, archivePath]);
|
|
198
|
+
run("tar", ["-xf", intermediate, "-C", destination]);
|
|
199
|
+
fs.rmSync(intermediate, { force: true });
|
|
156
200
|
}
|
|
157
201
|
|
|
158
202
|
function findExtractedBinary(root, binaryName) {
|
|
@@ -180,7 +224,7 @@ async function install() {
|
|
|
180
224
|
|
|
181
225
|
const version = PACKAGE_JSON.version;
|
|
182
226
|
const target = platformTarget();
|
|
183
|
-
const filename = `soldr-v${version}-${target.triple}.${
|
|
227
|
+
const filename = `soldr-v${version}-${target.triple}.${ARCHIVE_EXT}`;
|
|
184
228
|
const baseUrl = releaseBaseUrl(version);
|
|
185
229
|
const archiveUrl = `${baseUrl}/${filename}`;
|
|
186
230
|
const checksumUrl = `${baseUrl}/soldr-v${version}-SHA256SUMS.txt`;
|
|
@@ -203,24 +247,44 @@ async function install() {
|
|
|
203
247
|
const extractDir = path.join(tmp, "extract");
|
|
204
248
|
fs.writeFileSync(archivePath, archive);
|
|
205
249
|
fs.mkdirSync(extractDir, { recursive: true });
|
|
206
|
-
extractArchive(archivePath,
|
|
207
|
-
|
|
208
|
-
const extracted = findExtractedBinary(extractDir, target.binary);
|
|
209
|
-
if (!extracted) {
|
|
210
|
-
throw new Error(`release archive did not contain ${target.binary}`);
|
|
211
|
-
}
|
|
250
|
+
extractArchive(archivePath, extractDir);
|
|
212
251
|
|
|
213
252
|
const nativeDir = path.join(PACKAGE_ROOT, "bin", "native");
|
|
214
253
|
fs.rmSync(nativeDir, { recursive: true, force: true });
|
|
215
254
|
fs.mkdirSync(nativeDir, { recursive: true });
|
|
216
255
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
256
|
+
// Copy every bundled binary so soldr can find its sibling zccache
|
|
257
|
+
// via SOLDR_ZCCACHE_LOCAL_DIR and its sibling crgx via
|
|
258
|
+
// SOLDR_CRGX_LOCAL_DIR (both wired up by `bin/soldr.js` before
|
|
259
|
+
// exec). The archive layout is flat — all five binaries live at
|
|
260
|
+
// the archive root.
|
|
261
|
+
const binaryExt = target.binary.endsWith(".exe") ? ".exe" : "";
|
|
262
|
+
for (const baseName of BUNDLED_BINARIES) {
|
|
263
|
+
const fileName = `${baseName}${binaryExt}`;
|
|
264
|
+
const src = findExtractedBinary(extractDir, fileName);
|
|
265
|
+
if (!src) {
|
|
266
|
+
throw new Error(`release archive ${filename} did not contain ${fileName}`);
|
|
267
|
+
}
|
|
268
|
+
const dst = path.join(nativeDir, fileName);
|
|
269
|
+
fs.copyFileSync(src, dst);
|
|
270
|
+
if (process.platform !== "win32") {
|
|
271
|
+
fs.chmodSync(dst, 0o755);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Drop manifest.json alongside the binaries so downstream tooling
|
|
276
|
+
// (and humans reading `bin/native/`) can introspect provenance —
|
|
277
|
+
// soldr / zccache versions, target triples, build commit, sha256s.
|
|
278
|
+
// Best-effort: archives shipped before the manifest convention
|
|
279
|
+
// landed simply won't have one.
|
|
280
|
+
const manifestSrc = findExtractedBinary(extractDir, "manifest.json");
|
|
281
|
+
if (manifestSrc) {
|
|
282
|
+
fs.copyFileSync(manifestSrc, path.join(nativeDir, "manifest.json"));
|
|
221
283
|
}
|
|
222
284
|
|
|
223
|
-
console.log(
|
|
285
|
+
console.log(
|
|
286
|
+
`soldr: installed ${target.triple} (soldr + zccache trio + crgx) into ${nativeDir}`,
|
|
287
|
+
);
|
|
224
288
|
} finally {
|
|
225
289
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
226
290
|
}
|
|
@@ -234,7 +298,11 @@ if (require.main === module) {
|
|
|
234
298
|
}
|
|
235
299
|
|
|
236
300
|
module.exports = {
|
|
301
|
+
ARCHIVE_EXT,
|
|
302
|
+
BUNDLED_BINARIES,
|
|
303
|
+
TARGETS,
|
|
237
304
|
checksumFor,
|
|
305
|
+
detectLibc,
|
|
238
306
|
platformTarget,
|
|
239
307
|
releaseBaseUrl,
|
|
240
308
|
};
|
|
@@ -75,17 +75,87 @@ assert.deepStrictEqual(pkg.files, [
|
|
|
75
75
|
const bin = fs.readFileSync(path.join(root, pkg.bin.soldr), "utf8");
|
|
76
76
|
assert(bin.startsWith("#!/usr/bin/env node"), "bin/soldr.js must have a node shebang");
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
// Linux: triple selection branches on libc. `platformTarget` accepts an
|
|
79
|
+
// explicit `libc` arg so tests don't depend on the runtime detector.
|
|
80
|
+
assert.strictEqual(
|
|
81
|
+
install.platformTarget("linux", "x64", "gnu").triple,
|
|
82
|
+
"x86_64-unknown-linux-gnu",
|
|
83
|
+
);
|
|
84
|
+
assert.strictEqual(
|
|
85
|
+
install.platformTarget("linux", "x64", "musl").triple,
|
|
86
|
+
"x86_64-unknown-linux-musl",
|
|
87
|
+
);
|
|
88
|
+
assert.strictEqual(
|
|
89
|
+
install.platformTarget("linux", "arm64", "gnu").triple,
|
|
90
|
+
"aarch64-unknown-linux-gnu",
|
|
91
|
+
);
|
|
92
|
+
assert.strictEqual(
|
|
93
|
+
install.platformTarget("linux", "arm64", "musl").triple,
|
|
94
|
+
"aarch64-unknown-linux-musl",
|
|
95
|
+
);
|
|
96
|
+
// Default (no libc arg) falls back to detectLibc; on most CI hosts that's
|
|
97
|
+
// gnu. We don't assert the triple here — just that the call resolves.
|
|
98
|
+
assert.ok(install.platformTarget("linux", "x64").triple.startsWith("x86_64-unknown-linux-"));
|
|
80
99
|
assert.strictEqual(install.platformTarget("darwin", "x64").triple, "x86_64-apple-darwin");
|
|
81
100
|
assert.strictEqual(install.platformTarget("darwin", "arm64").triple, "aarch64-apple-darwin");
|
|
82
101
|
assert.strictEqual(install.platformTarget("win32", "x64").triple, "x86_64-pc-windows-msvc");
|
|
83
102
|
assert.strictEqual(install.platformTarget("win32", "arm64").triple, "aarch64-pc-windows-msvc");
|
|
84
103
|
assert.throws(() => install.platformTarget("freebsd", "x64"), /unsupported platform/);
|
|
85
104
|
|
|
105
|
+
// detectLibc must return null on non-Linux platforms so the platform key
|
|
106
|
+
// stays `<platform>-<arch>` rather than `<platform>-<arch>-gnu`.
|
|
107
|
+
assert.strictEqual(install.detectLibc("darwin"), null);
|
|
108
|
+
assert.strictEqual(install.detectLibc("win32"), null);
|
|
109
|
+
// On Linux it must always resolve to one of the two known families so
|
|
110
|
+
// `platformTarget` can build a well-formed key.
|
|
111
|
+
const linuxLibc = install.detectLibc("linux");
|
|
112
|
+
assert.ok(linuxLibc === "gnu" || linuxLibc === "musl", `unexpected libc: ${linuxLibc}`);
|
|
113
|
+
|
|
86
114
|
assert.strictEqual(
|
|
87
|
-
install.checksumFor(
|
|
115
|
+
install.checksumFor(
|
|
116
|
+
"abc123 soldr-v0.7.29-x86_64-unknown-linux-gnu.tar.zst\n",
|
|
117
|
+
"soldr-v0.7.29-x86_64-unknown-linux-gnu.tar.zst",
|
|
118
|
+
),
|
|
88
119
|
"abc123",
|
|
89
120
|
);
|
|
90
121
|
|
|
122
|
+
// Every TARGETS entry must drop the `archive` field — the combined
|
|
123
|
+
// archive format is fixed (.tar.zst) so the per-target field is dead
|
|
124
|
+
// data. Catch a regression early if anyone re-adds it.
|
|
125
|
+
for (const [key, target] of Object.entries(install.TARGETS || {})) {
|
|
126
|
+
assert.ok(
|
|
127
|
+
typeof target.triple === "string" && target.triple.length > 0,
|
|
128
|
+
`TARGETS[${key}].triple must be a non-empty string`,
|
|
129
|
+
);
|
|
130
|
+
assert.ok(
|
|
131
|
+
typeof target.binary === "string" && target.binary.length > 0,
|
|
132
|
+
`TARGETS[${key}].binary must be a non-empty string`,
|
|
133
|
+
);
|
|
134
|
+
assert.strictEqual(
|
|
135
|
+
target.archive,
|
|
136
|
+
undefined,
|
|
137
|
+
`TARGETS[${key}].archive must be removed — archive format is fixed at .tar.zst`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// BUNDLED_BINARIES must include soldr, the zccache trio, and crgx.
|
|
142
|
+
// Locks the per-archive layout contract so a future bundling
|
|
143
|
+
// refactor can't quietly drop a binary.
|
|
144
|
+
assert.ok(
|
|
145
|
+
Array.isArray(install.BUNDLED_BINARIES),
|
|
146
|
+
"install.BUNDLED_BINARIES must be exported as an array",
|
|
147
|
+
);
|
|
148
|
+
for (const required of [
|
|
149
|
+
"soldr",
|
|
150
|
+
"zccache",
|
|
151
|
+
"zccache-daemon",
|
|
152
|
+
"zccache-fp",
|
|
153
|
+
"crgx",
|
|
154
|
+
]) {
|
|
155
|
+
assert.ok(
|
|
156
|
+
install.BUNDLED_BINARIES.includes(required),
|
|
157
|
+
`BUNDLED_BINARIES must include "${required}" (got ${JSON.stringify(install.BUNDLED_BINARIES)})`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
91
161
|
console.log("npm package and PyPI version checks passed");
|