fix: Windows-native CI findings on latest master

Local run of check:ci:windows-complete (the windows-native gate) on
latest master surfaced five Windows-only failures, all unreachable by
current CI because the native windows job is disabled and the wine gate
only covers build+site.

- install-lefthook/translation-pairing-merge specs junctioned the real
  scripts/ and tsx package into fixtures; Windows recursive deletion
  (Node rmSync and git worktree remove) follows MOUNT_POINT junctions and
  deleted the repository's own directories mid-run. Fixtures now unlink
  their reparse points before any recursive removal (shared helper in
  scripts/test-fixture-cleanup.ts).
- workflow-workerthread spawned its worker with an empty env; on Windows
  os.tmpdir() then degrades to the literal relative path undefined\temp,
  so tsx wrote its transform cache into a cwd-relative undefined/
  directory inside the repo. The worker env now injects the host temp
  path on win32 (workerSpawnEnv, platform-parameterized and unit-tested
  on both arms).
- workspace-context spec did not stub USERPROFILE (win32 homedir) or a
  set DSH_HOME, leaking the developer machine's real ~/.dsh/AGENTS.md
  into discovery.
- ui-trajectory client-bundle spec mounted the built artifact without the
  remote/settingsScope provides the locale plugin needs, so the plugin
  never activated and no view registered.
- subagent temp-fixture cleanup lacked the maxRetries Windows handle
  release needs under load (EPERM); added retries to the three affected
  specs and the fixture-cleanup helper.
This commit is contained in:
Huanqi Cao
2026-08-12 01:11:46 +08:00
committed by Chinesezjc
parent 54cc6033a6
commit 4ed036da1c
9 changed files with 137 additions and 18 deletions

View File

@@ -6,6 +6,7 @@
* @module @deepseek-ai/dsh-workflow-worker-thread/host
*/
import { tmpdir } from 'node:os'
import { Worker } from 'node:worker_threads'
import type { WorkerOptions } from 'node:worker_threads'
import { fileURLToPath } from 'node:url'
@@ -28,18 +29,42 @@ interface ChildRecord {
disposal?: Promise<void>
}
/**
* The scrubbed worker environment: no ambient credentials, no loader flags.
* Windows derives `os.tmpdir()` from `TMP`/`TEMP` and falls back to the
* literal relative path `undefined\temp` when the environment is empty, so
* tsx's transform cache would land in a cwd-relative `undefined/temp`
* directory; the host's real temp path (not a credential) is injected there.
* The unbuilt shape additionally forwards `TSX_TSCONFIG_PATH` for path
* resolution.
* @param platform - host platform; overridable so tests exercise both peer arms.
* @returns the scrubbed worker environment object.
*/
export function workerSpawnEnv(platform: NodeJS.Platform = process.platform): NodeJS.ProcessEnv {
const env: NodeJS.ProcessEnv = {}
if (platform === 'win32') {
const tmp = tmpdir()
env.TMP = tmp
env.TEMP = tmp
}
if (process.env.TSX_TSCONFIG_PATH !== undefined) {
env.TSX_TSCONFIG_PATH = process.env.TSX_TSCONFIG_PATH
}
return env
}
/**
* Resolve a built worker bundle or an unbuilt bootstrap that installs both tsx
* transforms inside the worker. Both shapes clear `execArgv` and the ambient
* environment; the unbuilt shape forwards only `TSX_TSCONFIG_PATH` for path
* resolution.
* environment (the worker only sees the platform temp path and, unbuilt,
* `TSX_TSCONFIG_PATH`).
* @param init - the run payload, passed as `workerData`.
* @returns the entry path or URL and the Worker options to spawn it with.
*/
function resolveWorkerSpawn(init: WorkerInit): { entry: string | URL; options: WorkerOptions } {
/* v8 ignore next 3 -- the built-output arm: tests always run unbuilt (src/); the built-worker e2e exercises this shape for real */
if (!import.meta.url.endsWith('.ts')) {
return { entry: fileURLToPath(new URL('./worker.cjs', import.meta.url)), options: { workerData: init, env: {}, execArgv: [] } }
return { entry: fileURLToPath(new URL('./worker.cjs', import.meta.url)), options: { workerData: init, env: workerSpawnEnv(), execArgv: [] } }
}
// Resolve tsx only for unbuilt consumers and install it before importing TS.
const workerEntry = new URL('./worker.ts', import.meta.url)
@@ -56,7 +81,7 @@ function resolveWorkerSpawn(init: WorkerInit): { entry: string | URL; options: W
entry: new URL(`data:text/javascript,${encodeURIComponent(bootstrap)}`),
options: {
workerData: init,
env: process.env.TSX_TSCONFIG_PATH === undefined ? {} : { TSX_TSCONFIG_PATH: process.env.TSX_TSCONFIG_PATH },
env: workerSpawnEnv(),
execArgv: [],
},
}