Skip to content

fix(dsh): package the bridge profile on Windows - #2296

Merged
wgqqqqq merged 1 commit into
GCWing:mainfrom
wgqqqqq:fix/dsh-profile-windows-vendor
Aug 14, 2026
Merged

wgqqqqq merged 1 commit into
GCWing:mainfrom
wgqqqqq:fix/dsh-profile-windows-vendor

Conversation

@wgqqqqq

@wgqqqqq wgqqqqq commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

What broke

The 0.2.18 Desktop Package run failed in Package (windows-x64) — run 31824204681. packages/dsh-acp/scripts/build-profile.mjs died with spawnSync npm ENOENT, prepare:dsh-profile reported profile packaging failed, and that took frontend:build-all — and so the whole Tauri build — down with it. The other four platforms were unaffected.

Three Windows assumptions in one script

npm is a .cmd shim. Node has refused to spawn .bat/.cmd without a shell since CVE-2024-27980, which is the ENOENT. The parent script (scripts/prepare-dsh-profile.mjs) already passes shell: process.platform === 'win32'; this one never did. Adding a shell is only half the fix, because a shell then re-splits every argument and the old call passed an absolute --pack-destination that would break on its first space. So npm pack and tar now both run in the staging directory: their arguments are bare package names and one tarball filename, there is nothing to quote, and no drive letter reaches tar -f — which GNU tar (the one Git for Windows puts on PATH) reads as a remote hostname.

copyTree sliced a basename on '/'. On a '\'-separated path lastIndexOf('/') is -1, so base became the entire absolute path and the node_modules / .git filter matched nothing. Now basename().

hashTree recorded native separators, so identical sources produced a different content stamp per build host. Paths are now normalized to /. Digests are unchanged on Unix — verified byte-for-byte by running the previous script side by side (same 56833147…, diff -r of the two trees clean).

The reason this reached a release build

prepare:dsh-profile runs only inside frontend:build-all, and no CI job invokes that — Rust Build Check just mkdirs an empty dist-profile to satisfy Tauri, and Frontend Build calls build:web / build:mobile-web directly. So the packaging script's first execution on Windows, ever, was a release build.

This adds a DSH Profile Packaging (windows-latest) job: it runs the real packaging and then asserts the profile is complete, stamped, and carries nothing it must not ship (no node_modules or source maps under lib/ or presets/, which is exactly what the separator bug would have produced). ~2 minutes, no Rust, no pnpm.

Verification

  • Full packaging on macOS: npm ci → tsc → profile packaging, both pinned packages vendored at the right versions (@agentclientprotocol/sdk@0.25.1, @deepseek-ai/dsh-agent-spine-demo@0.1.0-rc.6), stamp written.
  • Digest and output tree identical to the pre-fix script (diff -r clean).
  • packages/dsh-acp: 30 vitest tests, tsc --noEmit clean.
  • node --test scripts/check-github-config.test.mjs (14) and pnpm run check:repo-hygiene pass with the new job in place.
  • Windows itself is validated by the new CI job on this PR — that is the check that failed in the release build.

🤖 Generated with Claude Code

The 0.2.18 Desktop Package run failed in `Package (windows-x64)` with
`spawnSync npm ENOENT` out of `build-profile.mjs`, taking the whole
`frontend:build-all` down with it. Three separate Windows assumptions:

- `npm` is a `.cmd` shim there, and Node has refused to spawn one without
  a shell since CVE-2024-27980. A shell then re-splits every argument, so
  passing an absolute `--pack-destination` would break on its first space.
  Both `npm pack` and `tar` now run *in* the staging directory, which
  leaves their arguments as bare package names and one filename — no
  quoting to get wrong, and no drive letter reaching `tar -f`, which GNU
  tar would read as a remote host.
- `copyTree`'s filter derived a basename by slicing on '/', which on a
  '\'-separated path yields the whole path and therefore matched nothing.
  A vendored tree would have dragged `node_modules` along.
- `hashTree` recorded native separators, so the same sources produced a
  different content stamp per build host. Digests are unchanged on Unix
  (verified byte-for-byte against the previous script).

`prepare:dsh-profile` runs only inside `frontend:build-all`, which no CI
job invokes, so its first Windows execution ever was a release build.
Add a small `windows-latest` job that runs the packaging and asserts the
profile is complete, stamped, and carries nothing it must not ship.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@wgqqqqq
wgqqqqq merged commit 48d43c3 into GCWing:main Aug 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant