@zackees/soldr 0.7.28 → 0.7.29

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 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 binaryPath = path.join(__dirname, "native", binaryName);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackees/soldr",
3
- "version": "0.7.28",
3
+ "version": "0.7.29",
4
4
  "description": "Instant Rust tools and builds from one command.",
5
5
  "license": "BSD-3-Clause",
6
6
  "homepage": "https://github.com/zackees/soldr",
@@ -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
- triple: "x86_64-unknown-linux-gnu",
18
- archive: "tar.gz",
19
- binary: "soldr",
20
- },
21
- "linux-arm64": {
22
- triple: "aarch64-unknown-linux-gnu",
23
- archive: "tar.gz",
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
- function platformTarget(platform = process.platform, arch = process.arch) {
49
- const target = TARGETS[`${platform}-${arch}`];
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: ${platform}-${arch}`);
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, archiveType, destination) {
132
- if (archiveType === "tar.gz") {
133
- run("tar", ["-xzf", archivePath, "-C", destination]);
134
- return;
135
- }
136
-
137
- if (archiveType === "zip" && process.platform === "win32") {
138
- run("powershell", [
139
- "-NoProfile",
140
- "-NonInteractive",
141
- "-ExecutionPolicy",
142
- "Bypass",
143
- "-Command",
144
- "Expand-Archive -LiteralPath $env:SOLDR_NPM_ARCHIVE -DestinationPath $env:SOLDR_NPM_EXTRACT -Force",
145
- ], {
146
- env: {
147
- ...process.env,
148
- SOLDR_NPM_ARCHIVE: archivePath,
149
- SOLDR_NPM_EXTRACT: destination,
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
- run("tar", ["-xf", archivePath, "-C", destination]);
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}.${target.archive}`;
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, target.archive, extractDir);
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
- const destination = path.join(nativeDir, target.binary);
218
- fs.copyFileSync(extracted, destination);
219
- if (process.platform !== "win32") {
220
- fs.chmodSync(destination, 0o755);
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(`soldr: installed ${target.triple} binary`);
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
- assert.strictEqual(install.platformTarget("linux", "x64").triple, "x86_64-unknown-linux-gnu");
79
- assert.strictEqual(install.platformTarget("linux", "arm64").triple, "aarch64-unknown-linux-gnu");
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("abc123 soldr-v0.7.5-x86_64-unknown-linux-gnu.tar.gz\n", "soldr-v0.7.5-x86_64-unknown-linux-gnu.tar.gz"),
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");