From 8db8b13a8cc8ae893a0c31fb2438b4ecc867d27f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 21:00:59 +0000 Subject: [PATCH 1/2] test: pin the node flow's build prerequisite to a shared helper (RED) `node_flow_test.py`'s skip guard restates the staged-artifact layout instead of asking the gate, and the layout it restates was deleted in #739 -- so the guard raises `AttributeError: 'Platform' object has no attribute 'bin_name'` before it can skip, and the node flow's only real-subprocess coverage has not run since. Assert the contract that removes the restating: `run()`'s prerequisite is exactly `staged_addon_path()`'s output. `staged_addon_path` does not exist yet, so this collection-errors. Refs #996 --- internals/distcheck/src/distcheck/node_flow/gate_test.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internals/distcheck/src/distcheck/node_flow/gate_test.py b/internals/distcheck/src/distcheck/node_flow/gate_test.py index 9cf3e45c1..51e3d30e0 100644 --- a/internals/distcheck/src/distcheck/node_flow/gate_test.py +++ b/internals/distcheck/src/distcheck/node_flow/gate_test.py @@ -14,6 +14,7 @@ _require_zero, run, select_tarball, + staged_addon_path, ) @@ -143,10 +144,17 @@ def test_run_success_executes_the_full_sequence(): ] +def test_staged_addon_path_builds_the_pnpm_build_output_path(): + assert staged_addon_path("/ts", _HOST) == _STAGED + + def test_run_missing_staged_binary_raises_prereq(): fs = _fs(exists=lambda p: False) with pytest.raises(DistcheckError, match="prerequisite missing"): run("/ts", _HOST, runner=mock.Mock(), fs=fs) + # The prerequisite `run` demands IS `staged_addon_path`'s output -- the + # contract other callers guard on. + fs.exists.assert_called_once_with(staged_addon_path("/ts", _HOST)) fs.copy.assert_not_called() From cc768a143e2762afd909c1a616e724700835e3b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 21:08:08 +0000 Subject: [PATCH 2/2] fix(distcheck): guard the node flow on the path the gate actually requires The node flow's integration guard restated the staged-artifact layout, and #739 deleted the layout it restated -- `bundled-cli-` and `Platform.bin_name` -- so it raised `AttributeError` before reaching the skip it feeds. Re-deriving the current layout in a second place would be the same drift again, one rename later, so export the path instead: `staged_addon_path()` is the flow's one build prerequisite, and `run()` and the guard both call it. Also correct the two places still describing the retired standalone-CLI sub-package: `gate.py`'s module docstring and README's `node` bullet now say `@dirsql/lib-` / `loadNativeCore()`, matching what `run()` packs. Fixes #996 --- internals/distcheck/README.md | 5 +++-- .../distcheck/src/distcheck/node_flow/gate.py | 19 ++++++++++++++----- .../tests/integration/node_flow_test.py | 11 ++++------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/internals/distcheck/README.md b/internals/distcheck/README.md index 45cbb2aea..0bfbef911 100644 --- a/internals/distcheck/README.md +++ b/internals/distcheck/README.md @@ -31,9 +31,10 @@ tooling, so one tested home matters more than language purity of the harness. `import dirsql`. Nothing is staged: since #738 the wheel's extension module carries the CLI and the console script calls it in-process. - **`node`** -- packs the main `dirsql` package and a reconstructed host - `@dirsql/cli-` sub-package, `npm install`s both into a fresh dir, and + `@dirsql/lib-` sub-package, `npm install`s both into a fresh dir, and runs `node_modules/.bin/dirsql --version`, cross-checking the installed - sub-package layout. + sub-package layout. Nothing separate is staged for the CLI: since #739 the + napi addon carries it and the launcher calls it in-process. **Caveat (host triple only):** each flow tests the host triple/interpreter. Cross-target coverage lives in the release pipeline's install matrix (one runner diff --git a/internals/distcheck/src/distcheck/node_flow/gate.py b/internals/distcheck/src/distcheck/node_flow/gate.py index cfc3e06ea..6605d3dd9 100644 --- a/internals/distcheck/src/distcheck/node_flow/gate.py +++ b/internals/distcheck/src/distcheck/node_flow/gate.py @@ -2,7 +2,7 @@ the per-package `build.test.ts`). build -> pack -> install -> run: pack the main `dirsql` package and a -reconstructed host `@dirsql/cli-` sub-package the way the release pipeline +reconstructed host `@dirsql/lib-` sub-package the way the release pipeline would, `npm install` both into a fresh dir, and run `node_modules/.bin/dirsql --version`. No mocks -- exactly what `npx dirsql` does for an end user. @@ -11,8 +11,9 @@ - Tests only the host triple. Cross-triple coverage lives in the release matrix (one runner per target). - The per-platform `package.json` shape is reconstructed here (it is normally - generated by the release tool), in the shape `src/cli/resolve-binary.ts` - consumes, so the real launcher resolution path is still exercised. + generated by the release tool), in the shape `loadNativeCore()` consumes, so + the real addon resolution path -- which since #739 serves the CLI too -- is + still exercised. Effects funnel through an injected `runner` (subprocess.run) and `fs` (FileSystem); the detected host `Platform` is passed in so each stage is @@ -56,6 +57,15 @@ def select_tarball(names, prefix: str, exclude: Optional[str] = None) -> str: return only +def staged_addon_path(ts_pkg: str, host: Platform) -> str: + """Where `pnpm build` stages the host addon -- the flow's one prerequisite. + + Exported so other callers guard on the path `run` actually requires, + rather than restating the staging layout in a second place. + """ + return os.path.join(ts_pkg, "build", host.slug, host.addon_name) + + def run( ts_pkg: str, host: Platform, @@ -65,8 +75,7 @@ def run( ) -> int: # pnpm build (napi:build + tsc + stage:platform) is a CI prerequisite; the # staged host addon is its output. Sanity-check rather than rebuild. - staged_dir = os.path.join(ts_pkg, "build", host.slug) - staged_addon = os.path.join(staged_dir, host.addon_name) + staged_addon = staged_addon_path(ts_pkg, host) if not fs.exists(staged_addon): raise DistcheckError( f"prerequisite missing: {staged_addon}. " diff --git a/internals/distcheck/tests/integration/node_flow_test.py b/internals/distcheck/tests/integration/node_flow_test.py index c4cca6854..911e64101 100644 --- a/internals/distcheck/tests/integration/node_flow_test.py +++ b/internals/distcheck/tests/integration/node_flow_test.py @@ -2,7 +2,7 @@ Runs `gate.run` with its default `subprocess.run` / real `FileSystem` -- the build -> pack -> install -> run behavior the old per-package packaging -suite exercised. Skips when the build prerequisites (the staged host CLI binary +suite exercised. Skips when the build prerequisites (the staged host addon from `pnpm build`, `npm`/`pnpm`) are absent, since the flow's actual CI execution is the `dirsql-distcheck node` job, which builds them first. """ @@ -15,7 +15,7 @@ import pytest -from distcheck.node_flow.gate import run +from distcheck.node_flow.gate import run, staged_addon_path from distcheck.node_flow.platforms import detect_host _REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) @@ -24,11 +24,8 @@ def test_npm_installed_cli_packs_installs_and_runs(): host = detect_host(sys.platform, platform.machine()) - staged = os.path.join( - _TS, "build", f"bundled-cli-{host.slug}", host.bin_name - ) - if not os.path.exists(staged): - pytest.skip("staged CLI binary absent (pnpm build in packages/ts)") + if not os.path.exists(staged_addon_path(_TS, host)): + pytest.skip("staged addon absent (pnpm build in packages/ts)") if shutil.which("npm") is None or shutil.which("pnpm") is None: pytest.skip("npm/pnpm not on PATH")