@zackees/soldr 0.7.38 → 0.7.40

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
@@ -4,6 +4,7 @@
4
4
  const childProcess = require("child_process");
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
+ const zccacheContract = require("../scripts/zccache-contract");
7
8
 
8
9
  const binaryName = process.platform === "win32" ? "soldr.exe" : "soldr";
9
10
  const nativeDir = path.join(__dirname, "native");
@@ -23,20 +24,21 @@ if (!fs.existsSync(binaryPath)) {
23
24
  // Wire the bundled zccache trio (zccache, zccache-daemon, zccache-fp,
24
25
  // shipped alongside soldr in `bin/native/` since the combined-archive
25
26
  // 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
27
+ // Soldr's ZccacheResolver checks SOLDR_ZCCACHE_LOCAL_DIR ahead of the
28
+ // pinned and managed-download chain, so this turns the bundled
28
29
  // binaries into the active zccache automatically — no manual env
29
30
  // setup, no managed fetch over the network. Users who explicitly set
30
31
  // the env var themselves keep their override.
31
32
  const exeExt = process.platform === "win32" ? ".exe" : "";
32
33
  const childEnv = { ...process.env };
34
+ const zccacheLocalDirEnv = zccacheContract.CONTRACT.zccache.local_dir_env;
33
35
  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}`))
36
+ !childEnv[zccacheLocalDirEnv] &&
37
+ zccacheContract.ZCCACHE_BUNDLED_BINARIES.every((baseName) =>
38
+ fs.existsSync(path.join(nativeDir, `${baseName}${exeExt}`)),
39
+ )
38
40
  ) {
39
- childEnv.SOLDR_ZCCACHE_LOCAL_DIR = nativeDir;
41
+ childEnv[zccacheLocalDirEnv] = nativeDir;
40
42
  }
41
43
 
42
44
  // Same wiring for the bundled crgx binary (shipped alongside soldr
@@ -45,11 +47,12 @@ if (
45
47
  // SOLDR_CRGX_LOCAL_DIR ahead of the GitHub Releases / crates.io
46
48
  // fetch chain, so `soldr crgx ...` runs the bundled binary with no
47
49
  // network round trip. Caller-set overrides win.
50
+ const crgxLocalDirEnv = zccacheContract.CONTRACT.crgx.local_dir_env;
48
51
  if (
49
- !childEnv.SOLDR_CRGX_LOCAL_DIR &&
50
- fs.existsSync(path.join(nativeDir, `crgx${exeExt}`))
52
+ !childEnv[crgxLocalDirEnv] &&
53
+ fs.existsSync(path.join(nativeDir, `${zccacheContract.CRGX_BUNDLED_BINARY}${exeExt}`))
51
54
  ) {
52
- childEnv.SOLDR_CRGX_LOCAL_DIR = nativeDir;
55
+ childEnv[crgxLocalDirEnv] = nativeDir;
53
56
  }
54
57
 
55
58
  const child = childProcess.spawn(binaryPath, process.argv.slice(2), {
@@ -0,0 +1,334 @@
1
+ {
2
+ "schema_version": 1,
3
+ "name": "soldr-zccache-integration-guardrails",
4
+ "parent_issue": "https://github.com/zackees/soldr/issues/543",
5
+ "wave_issue": "https://github.com/zackees/soldr/issues/548",
6
+ "policy": {
7
+ "hard_gate": "Must pass before zccache integration refactor waves merge.",
8
+ "report_only": "Must remain runnable and documented, but does not block every PR unless the refactor touches that path.",
9
+ "discovery_rule": "If implementation finds a new soldr/zccache refactor area, file a child issue and add it to the parent issue ledger before or alongside the implementation PR."
10
+ },
11
+ "validation_commands": [
12
+ {
13
+ "id": "rust-contract-matrix",
14
+ "gate": "hard",
15
+ "command": "soldr --no-cache cargo test -p soldr-cli --test cli_zccache_contract_matrix --locked"
16
+ },
17
+ {
18
+ "id": "rust-source-resolution",
19
+ "gate": "hard",
20
+ "command": "soldr --no-cache cargo test -p soldr-cli --test cli_install_zccache_resolution --locked"
21
+ },
22
+ {
23
+ "id": "rust-wrapper-env",
24
+ "gate": "hard",
25
+ "command": "soldr --no-cache cargo test -p soldr-cli --test cli_cargo_wrappers --locked"
26
+ },
27
+ {
28
+ "id": "rust-native-cache-unit",
29
+ "gate": "hard",
30
+ "command": "soldr --no-cache cargo test -p soldr-cli --bin soldr --locked native_cc::tests"
31
+ },
32
+ {
33
+ "id": "rust-unknown-session-retry",
34
+ "gate": "hard",
35
+ "command": "soldr --no-cache cargo test -p soldr-cli --test cli_unknown_session_retry --locked"
36
+ },
37
+ {
38
+ "id": "rust-native-cache-integration",
39
+ "gate": "hard",
40
+ "command": "soldr --no-cache cargo test -p soldr-cli --test cli_cargo_native_cc --locked"
41
+ },
42
+ {
43
+ "id": "rust-wrapper-perf",
44
+ "gate": "hard",
45
+ "command": "soldr --no-cache cargo test -p soldr-cli --test cli_wrapper_perf --locked"
46
+ },
47
+ {
48
+ "id": "rust-watchdog-lint",
49
+ "gate": "hard",
50
+ "command": "soldr --no-cache cargo test -p soldr-cli --test timed_test_lint --locked"
51
+ },
52
+ {
53
+ "id": "python-setup-contracts",
54
+ "gate": "hard",
55
+ "command": "uv run pytest tests/test_zccache_integration_guardrails.py tests/test_zccache_runtime_contract.py tests/test_setup_soldr_action.py tests/test_setup_soldr_exporter.py tests/test_setup_soldr_ensure_soldr.py -q"
56
+ },
57
+ {
58
+ "id": "node-npm-contract",
59
+ "gate": "hard",
60
+ "command": "node scripts/test-npm-package.js"
61
+ },
62
+ {
63
+ "id": "perf-cold-warm",
64
+ "gate": "report-only",
65
+ "command": "gh workflow run perf-cold-warm.yml -f run_mode='Purge cache and run cold build before warm build' -f fixture=medium"
66
+ },
67
+ {
68
+ "id": "perf-matrix-worktree-touch",
69
+ "gate": "report-only",
70
+ "command": "gh workflow run perf-matrix.yml -f platforms=linux -f fixtures=medium -f scenarios=all"
71
+ }
72
+ ],
73
+ "guardrails": [
74
+ {
75
+ "id": "source-precedence",
76
+ "axis": "source resolution",
77
+ "gate": "hard",
78
+ "covers": [
79
+ "SOLDR_TEST_ZCCACHE_BIN override",
80
+ "SOLDR_ZCCACHE_LOCAL_DIR override",
81
+ "pinned managed runtime",
82
+ "managed cached/runtime fallback",
83
+ "system fallback diagnostics"
84
+ ],
85
+ "test_files": [
86
+ "crates/soldr-cli/tests/cli_install_zccache_resolution.rs",
87
+ "crates/soldr-cli/src/fetch/zccache_contract_tests.rs",
88
+ "tests/test_zccache_runtime_contract.py"
89
+ ],
90
+ "validation_command_ids": [
91
+ "rust-source-resolution",
92
+ "python-setup-contracts"
93
+ ]
94
+ },
95
+ {
96
+ "id": "managed-session-env",
97
+ "axis": "session lifecycle and child env",
98
+ "gate": "hard",
99
+ "covers": [
100
+ "zccache start",
101
+ "session-start",
102
+ "ZCCACHE_SESSION_ID propagation",
103
+ "ZCCACHE_CACHE_DIR propagation",
104
+ "ZCCACHE_PATH_REMAP propagation",
105
+ "ZCCACHE_WORKTREE_ROOT propagation",
106
+ "session-end stats"
107
+ ],
108
+ "test_files": [
109
+ "crates/soldr-cli/tests/cli_zccache_contract_matrix.rs",
110
+ "crates/soldr-cli/tests/cli_cargo_basic.rs",
111
+ "crates/soldr-cli/src/cargo_front_door/cache_plan.rs"
112
+ ],
113
+ "validation_command_ids": [
114
+ "rust-contract-matrix",
115
+ "rust-wrapper-env"
116
+ ]
117
+ },
118
+ {
119
+ "id": "rust-plan-cache",
120
+ "axis": "rust artifact plan",
121
+ "gate": "hard",
122
+ "covers": [
123
+ "restore before cargo",
124
+ "save after cargo",
125
+ "managed daemon cache dir",
126
+ "artifact bundle cache dir",
127
+ "partial restore warning"
128
+ ],
129
+ "test_files": [
130
+ "crates/soldr-cli/tests/cli_zccache_contract_matrix.rs",
131
+ "crates/soldr-cli/tests/cli_rust_plan.rs",
132
+ "crates/soldr-cli/src/rust_plan_tests"
133
+ ],
134
+ "validation_command_ids": [
135
+ "rust-contract-matrix"
136
+ ]
137
+ },
138
+ {
139
+ "id": "disabled-and-non-build",
140
+ "axis": "cache scoping",
141
+ "gate": "hard",
142
+ "covers": [
143
+ "--no-cache build bypasses managed zccache",
144
+ "non-build cargo commands bypass managed zccache",
145
+ "SOLDR_CACHE_ENABLED=0 reaches cargo"
146
+ ],
147
+ "test_files": [
148
+ "crates/soldr-cli/tests/cli_zccache_contract_matrix.rs",
149
+ "crates/soldr-cli/tests/cli_cargo_wrappers.rs"
150
+ ],
151
+ "validation_command_ids": [
152
+ "rust-contract-matrix",
153
+ "rust-wrapper-env"
154
+ ]
155
+ },
156
+ {
157
+ "id": "unknown-session-retry",
158
+ "axis": "wrapper recovery",
159
+ "gate": "hard",
160
+ "platforms": [
161
+ "windows"
162
+ ],
163
+ "covers": [
164
+ "one retry after zccache unknown session",
165
+ "fresh session allocation for retry",
166
+ "non-session errors do not retry"
167
+ ],
168
+ "test_files": [
169
+ "crates/soldr-cli/tests/cli_unknown_session_retry.rs",
170
+ "crates/soldr-cli/src/wrapper.rs",
171
+ "crates/soldr-cli/src/zccache_lifecycle.rs"
172
+ ],
173
+ "validation_command_ids": [
174
+ "rust-unknown-session-retry",
175
+ "rust-watchdog-lint"
176
+ ]
177
+ },
178
+ {
179
+ "id": "shutdown-scoping",
180
+ "axis": "daemon shutdown",
181
+ "gate": "hard",
182
+ "covers": [
183
+ "command-lifetime stop after session-end",
184
+ "failed cargo still shuts down",
185
+ "shutdown uses soldr-owned cache root",
186
+ "daemon-down diagnostics"
187
+ ],
188
+ "test_files": [
189
+ "crates/soldr-cli/tests/cli_zccache_contract_matrix.rs",
190
+ "crates/soldr-cli/tests/cli_cache.rs",
191
+ "crates/soldr-cli/tests/cli_cargo_basic.rs"
192
+ ],
193
+ "validation_command_ids": [
194
+ "rust-contract-matrix"
195
+ ]
196
+ },
197
+ {
198
+ "id": "setup-action-outputs",
199
+ "axis": "setup-soldr action",
200
+ "gate": "hard",
201
+ "covers": [
202
+ "cache-hit output",
203
+ "build-cache-hit output",
204
+ "target-cache-hit output",
205
+ "native-cache-enabled output",
206
+ "target-cache mode/key policy"
207
+ ],
208
+ "test_files": [
209
+ "action.yml",
210
+ ".github/actions/setup-soldr/resolve_setup.py",
211
+ "tests/test_setup_soldr_action.py",
212
+ "tests/test_setup_soldr_exporter.py"
213
+ ],
214
+ "validation_command_ids": [
215
+ "python-setup-contracts"
216
+ ]
217
+ },
218
+ {
219
+ "id": "release-npm-staging",
220
+ "axis": "release and npm staging",
221
+ "gate": "hard",
222
+ "covers": [
223
+ "release archive manifest",
224
+ "zccache binary trio",
225
+ "crgx binary",
226
+ "setup-soldr manifest validation",
227
+ "npm bundled contract files"
228
+ ],
229
+ "test_files": [
230
+ "contracts/zccache-runtime.v1.json",
231
+ "contracts/zccache-integration-guardrails.v1.json",
232
+ ".github/workflows/release-auto.yml",
233
+ "scripts/zccache-contract.js",
234
+ "scripts/test-npm-package.js",
235
+ "tests/test_zccache_runtime_contract.py"
236
+ ],
237
+ "validation_command_ids": [
238
+ "python-setup-contracts",
239
+ "node-npm-contract"
240
+ ]
241
+ },
242
+ {
243
+ "id": "perf-cold-warm",
244
+ "axis": "performance",
245
+ "gate": "report-only",
246
+ "covers": [
247
+ "cold build",
248
+ "warm build",
249
+ "flush before save",
250
+ "hit-rate reporting"
251
+ ],
252
+ "test_files": [
253
+ ".github/workflows/perf-cold-warm.yml",
254
+ "crates/soldr-cli/tests/cli_cargo_wrappers.rs"
255
+ ],
256
+ "validation_command_ids": [
257
+ "rust-wrapper-env",
258
+ "perf-cold-warm"
259
+ ]
260
+ },
261
+ {
262
+ "id": "perf-worktree-share",
263
+ "axis": "performance",
264
+ "gate": "report-only",
265
+ "covers": [
266
+ "shared cache across worktrees",
267
+ "branch/worktree target reuse"
268
+ ],
269
+ "test_files": [
270
+ ".github/workflows/perf-matrix.yml",
271
+ "perf/scenarios/worktree-share/run.sh"
272
+ ],
273
+ "validation_command_ids": [
274
+ "perf-matrix-worktree-touch"
275
+ ]
276
+ },
277
+ {
278
+ "id": "perf-touch-no-change",
279
+ "axis": "performance",
280
+ "gate": "report-only",
281
+ "covers": [
282
+ "touch without source change",
283
+ "Cargo fingerprint churn canary"
284
+ ],
285
+ "test_files": [
286
+ ".github/workflows/perf-matrix.yml",
287
+ "perf/scenarios/touch-no-change/run.sh"
288
+ ],
289
+ "validation_command_ids": [
290
+ "perf-matrix-worktree-touch"
291
+ ]
292
+ },
293
+ {
294
+ "id": "native-cc-cache",
295
+ "axis": "native C/C++ cache",
296
+ "gate": "hard",
297
+ "covers": [
298
+ "CC wrapper injection",
299
+ "CXX wrapper injection",
300
+ "CC_KNOWN_WRAPPER_CUSTOM",
301
+ "SOLDR_NATIVE_CACHE=0 opt-out",
302
+ "--no-cache disables native cache"
303
+ ],
304
+ "test_files": [
305
+ "crates/soldr-cli/tests/cli_cargo_native_cc.rs",
306
+ "crates/soldr-cli/src/native_cc_tests.rs",
307
+ "crates/soldr-cli/src/cargo_front_door/cache_plan.rs"
308
+ ],
309
+ "validation_command_ids": [
310
+ "rust-native-cache-unit",
311
+ "rust-native-cache-integration"
312
+ ]
313
+ },
314
+ {
315
+ "id": "monolith-migration-ratchet",
316
+ "axis": "test ownership",
317
+ "gate": "hard",
318
+ "covers": [
319
+ "new zccache integration tests live outside monolithic source modules",
320
+ "new tests use timed_test watchdog",
321
+ "wave-specific contract tests remain discoverable"
322
+ ],
323
+ "test_files": [
324
+ "crates/soldr-cli/tests/timed_test_lint.rs",
325
+ "crates/soldr-cli/src/fetch/zccache_contract_tests.rs",
326
+ "crates/soldr-cli/src/cargo_front_door/cache_plan.rs",
327
+ "crates/soldr-cli/src/zccache_lifecycle.rs"
328
+ ],
329
+ "validation_command_ids": [
330
+ "rust-watchdog-lint"
331
+ ]
332
+ }
333
+ ]
334
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "schema_version": 1,
3
+ "name": "soldr-zccache-runtime",
4
+ "release_archive": {
5
+ "extension": "tar.zst",
6
+ "compression_level": 19,
7
+ "manifest_name": "manifest.json",
8
+ "manifest_min_schema_version": 2,
9
+ "sha256_algorithm": "sha256",
10
+ "required_binaries": [
11
+ "soldr",
12
+ "zccache",
13
+ "zccache-daemon",
14
+ "zccache-fp",
15
+ "crgx"
16
+ ],
17
+ "linux_zccache_target_libc": "musl"
18
+ },
19
+ "zccache": {
20
+ "managed_version": "1.11.6",
21
+ "local_dir_env": "SOLDR_ZCCACHE_LOCAL_DIR",
22
+ "cache_dir_env": "ZCCACHE_CACHE_DIR",
23
+ "required_binaries": [
24
+ "zccache",
25
+ "zccache-daemon",
26
+ "zccache-fp"
27
+ ],
28
+ "manifest_block": "zccache"
29
+ },
30
+ "crgx": {
31
+ "managed_version": "0.1.0",
32
+ "local_dir_env": "SOLDR_CRGX_LOCAL_DIR",
33
+ "required_binaries": [
34
+ "crgx"
35
+ ],
36
+ "manifest_block": "crgx"
37
+ },
38
+ "setup_action": {
39
+ "cache_root_env": "SOLDR_CACHE_DIR",
40
+ "native_cache_env": "SOLDR_NATIVE_CACHE",
41
+ "contract_module": ".github/actions/setup-soldr/zccache_contract.py",
42
+ "exported_contract": "contracts/zccache-runtime.v1.json"
43
+ },
44
+ "npm": {
45
+ "native_dir": "bin/native",
46
+ "launcher": "bin/soldr.js",
47
+ "installer": "scripts/install.js",
48
+ "contract_loader": "scripts/zccache-contract.js"
49
+ }
50
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackees/soldr",
3
- "version": "0.7.38",
3
+ "version": "0.7.40",
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",
@@ -16,7 +16,10 @@
16
16
  },
17
17
  "files": [
18
18
  "bin/soldr.js",
19
+ "contracts/zccache-integration-guardrails.v1.json",
20
+ "contracts/zccache-runtime.v1.json",
19
21
  "scripts/install.js",
22
+ "scripts/zccache-contract.js",
20
23
  "scripts/test-npm-package.js",
21
24
  "README.md",
22
25
  "LICENSE"
@@ -8,6 +8,7 @@ const http = require("http");
8
8
  const https = require("https");
9
9
  const os = require("os");
10
10
  const path = require("path");
11
+ const zccacheContract = require("./zccache-contract");
11
12
 
12
13
  const PACKAGE_ROOT = path.resolve(__dirname, "..");
13
14
  const PACKAGE_JSON = require(path.join(PACKAGE_ROOT, "package.json"));
@@ -16,9 +17,9 @@ const PACKAGE_JSON = require(path.join(PACKAGE_ROOT, "package.json"));
16
17
  // alongside its matching-target zccache trio (zccache, zccache-daemon,
17
18
  // zccache-fp) and a same-target crgx. One fetch installs everything,
18
19
  // 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";
20
+ // to the install dir so soldr's runtime resolver finds the sibling binaries
21
+ // without going through the managed-download path.
22
+ const ARCHIVE_EXT = zccacheContract.ARCHIVE_EXT;
22
23
 
23
24
  const TARGETS = {
24
25
  "linux-x64-gnu": { triple: "x86_64-unknown-linux-gnu", binary: "soldr" },
@@ -37,13 +38,7 @@ const TARGETS = {
37
38
  // `Build crgx from pinned source` steps drop into `dist/package/`
38
39
  // before the tar.zst is built. `.exe` suffix is appended at install
39
40
  // time based on `target.binary`.
40
- const BUNDLED_BINARIES = [
41
- "soldr",
42
- "zccache",
43
- "zccache-daemon",
44
- "zccache-fp",
45
- "crgx",
46
- ];
41
+ const BUNDLED_BINARIES = zccacheContract.RELEASE_BUNDLED_BINARIES;
47
42
 
48
43
  // Detect whether the running Linux uses musl or glibc. Three layered probes:
49
44
  // 1. process.report.header.glibcVersionRuntime is the documented Node
@@ -253,12 +248,28 @@ async function install() {
253
248
  fs.rmSync(nativeDir, { recursive: true, force: true });
254
249
  fs.mkdirSync(nativeDir, { recursive: true });
255
250
 
256
- // Copy every bundled binary so soldr can find its sibling zccache
257
- // via SOLDR_ZCCACHE_LOCAL_DIR and its sibling crgx via
251
+ // Copy every bundled binary so soldr's runtime resolver can find
252
+ // its sibling zccache via SOLDR_ZCCACHE_LOCAL_DIR and crgx via
258
253
  // SOLDR_CRGX_LOCAL_DIR (both wired up by `bin/soldr.js` before
259
254
  // exec). The archive layout is flat — all five binaries live at
260
255
  // the archive root.
261
256
  const binaryExt = target.binary.endsWith(".exe") ? ".exe" : "";
257
+ const manifestSrc = findExtractedBinary(extractDir, zccacheContract.MANIFEST_NAME);
258
+ if (!manifestSrc) {
259
+ throw new Error(`release archive ${filename} did not contain ${zccacheContract.MANIFEST_NAME}`);
260
+ }
261
+ const manifest = JSON.parse(fs.readFileSync(manifestSrc, "utf8"));
262
+ zccacheContract.validateReleaseManifest(manifest, {
263
+ soldrTarget: target.triple,
264
+ platform: process.platform,
265
+ findFile: (name) => {
266
+ const filePath = findExtractedBinary(extractDir, name);
267
+ if (!filePath) {
268
+ throw new Error(`release archive ${filename} did not contain ${name}`);
269
+ }
270
+ return filePath;
271
+ },
272
+ });
262
273
  for (const baseName of BUNDLED_BINARIES) {
263
274
  const fileName = `${baseName}${binaryExt}`;
264
275
  const src = findExtractedBinary(extractDir, fileName);
@@ -275,12 +286,7 @@ async function install() {
275
286
  // Drop manifest.json alongside the binaries so downstream tooling
276
287
  // (and humans reading `bin/native/`) can introspect provenance —
277
288
  // 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"));
283
- }
289
+ fs.copyFileSync(manifestSrc, path.join(nativeDir, zccacheContract.MANIFEST_NAME));
284
290
 
285
291
  console.log(
286
292
  `soldr: installed ${target.triple} (soldr + zccache trio + crgx) into ${nativeDir}`,
@@ -8,6 +8,7 @@ const assert = require("assert");
8
8
  const root = path.resolve(__dirname, "..");
9
9
  const pkg = require(path.join(root, "package.json"));
10
10
  const install = require(path.join(root, "scripts", "install.js"));
11
+ const zccacheContract = require(path.join(root, "scripts", "zccache-contract.js"));
11
12
 
12
13
  function tomlSection(toml, sectionName) {
13
14
  const header = `[${sectionName}]`;
@@ -66,7 +67,10 @@ assert.strictEqual(pkg.bin.soldr, "bin/soldr.js");
66
67
  assert.strictEqual(pkg.repository.url, "git+https://github.com/zackees/soldr.git");
67
68
  assert.deepStrictEqual(pkg.files, [
68
69
  "bin/soldr.js",
70
+ "contracts/zccache-integration-guardrails.v1.json",
71
+ "contracts/zccache-runtime.v1.json",
69
72
  "scripts/install.js",
73
+ "scripts/zccache-contract.js",
70
74
  "scripts/test-npm-package.js",
71
75
  "README.md",
72
76
  "LICENSE",
@@ -119,6 +123,21 @@ assert.strictEqual(
119
123
  "abc123",
120
124
  );
121
125
 
126
+ assert.strictEqual(install.ARCHIVE_EXT, "tar.zst");
127
+ assert.deepStrictEqual(
128
+ install.BUNDLED_BINARIES,
129
+ zccacheContract.RELEASE_BUNDLED_BINARIES,
130
+ );
131
+ assert.deepStrictEqual(zccacheContract.ZCCACHE_BUNDLED_BINARIES, [
132
+ "zccache",
133
+ "zccache-daemon",
134
+ "zccache-fp",
135
+ ]);
136
+ assert.ok(
137
+ fs.existsSync(path.join(root, "contracts", "zccache-integration-guardrails.v1.json")),
138
+ "npm package must include the zccache integration guardrail contract",
139
+ );
140
+
122
141
  // Every TARGETS entry must drop the `archive` field — the combined
123
142
  // archive format is fixed (.tar.zst) so the per-target field is dead
124
143
  // data. Catch a regression early if anyone re-adds it.
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const crypto = require("crypto");
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+
8
+ const PACKAGE_ROOT = path.resolve(__dirname, "..");
9
+ const CONTRACT_PATH = path.join(PACKAGE_ROOT, "contracts", "zccache-runtime.v1.json");
10
+ const CONTRACT = JSON.parse(fs.readFileSync(CONTRACT_PATH, "utf8"));
11
+
12
+ const ARCHIVE_EXT = CONTRACT.release_archive.extension;
13
+ const MANIFEST_NAME = CONTRACT.release_archive.manifest_name;
14
+ const MANIFEST_MIN_SCHEMA_VERSION = CONTRACT.release_archive.manifest_min_schema_version;
15
+ const RELEASE_BUNDLED_BINARIES = Object.freeze([...CONTRACT.release_archive.required_binaries]);
16
+ const ZCCACHE_BUNDLED_BINARIES = Object.freeze([...CONTRACT.zccache.required_binaries]);
17
+ const CRGX_BUNDLED_BINARY = CONTRACT.crgx.required_binaries[0];
18
+
19
+ function binaryName(baseName, platform = process.platform) {
20
+ return `${baseName}${platform === "win32" ? ".exe" : ""}`;
21
+ }
22
+
23
+ function releaseBinaryNames(platform = process.platform) {
24
+ return RELEASE_BUNDLED_BINARIES.map((baseName) => binaryName(baseName, platform));
25
+ }
26
+
27
+ function zccacheTargetForSoldrTarget(soldrTarget) {
28
+ if (!soldrTarget.includes("-unknown-linux-")) {
29
+ return soldrTarget;
30
+ }
31
+ const arch = soldrTarget.split("-unknown-linux-", 1)[0];
32
+ return `${arch}-unknown-linux-${CONTRACT.release_archive.linux_zccache_target_libc}`;
33
+ }
34
+
35
+ function sha256File(filePath) {
36
+ return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
37
+ }
38
+
39
+ function collectManifestBinaries(manifest) {
40
+ const binaries = new Map();
41
+ if (manifest.soldr && manifest.soldr.binary && manifest.soldr.sha256) {
42
+ binaries.set(manifest.soldr.binary, manifest.soldr.sha256);
43
+ }
44
+ const zccacheBinaries = (manifest.zccache && manifest.zccache.binaries) || [];
45
+ for (const entry of zccacheBinaries) {
46
+ if (entry && entry.name && entry.sha256) {
47
+ binaries.set(entry.name, entry.sha256);
48
+ }
49
+ }
50
+ if (manifest.crgx && manifest.crgx.binary && manifest.crgx.sha256) {
51
+ binaries.set(manifest.crgx.binary, manifest.crgx.sha256);
52
+ }
53
+ return binaries;
54
+ }
55
+
56
+ function validateReleaseManifest(manifest, options) {
57
+ const { soldrTarget, platform = process.platform, findFile } = options;
58
+ if (!Number.isInteger(manifest.schema_version) || manifest.schema_version < MANIFEST_MIN_SCHEMA_VERSION) {
59
+ throw new Error(`release manifest schema_version must be >= ${MANIFEST_MIN_SCHEMA_VERSION}`);
60
+ }
61
+ if (!manifest.archive || manifest.archive.format !== ARCHIVE_EXT) {
62
+ throw new Error(`release manifest archive.format must be ${ARCHIVE_EXT}`);
63
+ }
64
+ if (!manifest.soldr || manifest.soldr.target !== soldrTarget) {
65
+ throw new Error(`release manifest soldr.target must be ${soldrTarget}`);
66
+ }
67
+ const expectedZccacheTarget = zccacheTargetForSoldrTarget(soldrTarget);
68
+ if (!manifest.zccache || manifest.zccache.target !== expectedZccacheTarget) {
69
+ throw new Error(`release manifest zccache.target must be ${expectedZccacheTarget}`);
70
+ }
71
+ if (!manifest.crgx || manifest.crgx.target !== soldrTarget) {
72
+ throw new Error(`release manifest crgx.target must be ${soldrTarget}`);
73
+ }
74
+
75
+ const expectedNames = releaseBinaryNames(platform);
76
+ const manifestBinaries = collectManifestBinaries(manifest);
77
+ for (const name of expectedNames) {
78
+ if (!manifestBinaries.has(name)) {
79
+ throw new Error(`release manifest is missing bundled binary record for ${name}`);
80
+ }
81
+ const expectedSha = String(manifestBinaries.get(name)).toLowerCase();
82
+ if (!/^[0-9a-f]{64}$/.test(expectedSha)) {
83
+ throw new Error(`release manifest sha256 for ${name} is not lowercase hex`);
84
+ }
85
+ const filePath = findFile(name);
86
+ const actualSha = sha256File(filePath);
87
+ if (actualSha !== expectedSha) {
88
+ throw new Error(`release manifest sha256 mismatch for ${name}: expected ${expectedSha}, got ${actualSha}`);
89
+ }
90
+ }
91
+ }
92
+
93
+ module.exports = {
94
+ ARCHIVE_EXT,
95
+ CONTRACT,
96
+ CONTRACT_PATH,
97
+ CRGX_BUNDLED_BINARY,
98
+ MANIFEST_NAME,
99
+ RELEASE_BUNDLED_BINARIES,
100
+ ZCCACHE_BUNDLED_BINARIES,
101
+ binaryName,
102
+ releaseBinaryNames,
103
+ validateReleaseManifest,
104
+ zccacheTargetForSoldrTarget,
105
+ };