mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(ci): make local Wine gate portable on macOS
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-27-wine-windows-gates-experiment.md
|
||||
2026-07-27-wine-windows-gates-experiment.md: fd36a3322b0d63a0fa605ecfcec95c7522b43b25
|
||||
2026-07-27-wine-windows-gates-experiment.zh.md: 97a54161bb851785a9108cc45d59617e1a449e68
|
||||
2026-07-27-wine-windows-gates-experiment.md: 640c8e455b1a35ea4ac83454227147b9979316dc
|
||||
2026-07-27-wine-windows-gates-experiment.zh.md: f30e09ca7411ef83d02faf012ce54d6c6c65dff1
|
||||
|
||||
@@ -20,7 +20,7 @@ The lane holds the wall clock of the Linux CI jobs through four levers: the mast
|
||||
|
||||
The gate logic lives in one script, [scripts/wine-windows-gates.sh](../../../../scripts/wine-windows-gates.sh): the ci.yml job provisions runner state (caches, apt Wine) and calls it, and the optional local gate `pnpm run check:windows-wine` runs the identical script on a developer machine that has Wine installed — one implementation, so local reproduction of a red CI lane needs no translation between environments. The local gate is a diagnosis tool, not a routine check: run it only when investigating a known Windows-related failure; CI owns the everyday win32 signal, and [dsh-pre-push-checks](../../../skills/dsh-pre-push-checks/SKILL.md) never selects it. The script never mutates the working tree: it snapshots tracked plus untracked-unignored files into a scratch directory, applies the Wine-specific pnpm overrides to the snapshot only, and installs there against the shared store; the Wine prefix and the checksum-verified Windows Node zip persist under `.cache/wine-windows/` so local reruns skip provisioning, with an offline fallback to the newest cached zip when nodejs.org is unreachable.
|
||||
|
||||
Four environment constraints shape the job, each found as a red run: Ubuntu's `wine64` package alone puts nothing on PATH (install `wine`, the dispatcher); Node under Wine cannot attach stdio to the Actions runner's pipes (`Socket open EBADF` at bootstrap — every invocation routes stdio through a file); Wine does not realpath pnpm's isolated-layout Unix symlinks (the hoisted layout above); and Wine cannot create Windows symlinks (`ENOTSUP` from VitePress's `linkVue` — the `vue` link is laid down host-side before the gate).
|
||||
Five environment constraints shape CI and local execution, each found as a red run: Ubuntu's `wine64` package alone puts nothing on PATH (install `wine`, the dispatcher); Node under Wine cannot attach stdio to the caller's pipes (`Socket open EBADF` at bootstrap — every invocation routes stdio through a file); Wine does not realpath pnpm's isolated-layout Unix symlinks (the hoisted layout above); macOS Wine also exposes hoisted workspace links as ordinary directories, so the client test aggregate includes every package-local CSS module declaration instead of relying on project-reference realpaths; and Wine cannot create Windows symlinks (`ENOTSUP` from VitePress's `linkVue` — the `vue` link is laid down host-side before the gate).
|
||||
|
||||
## Measured results
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Pull request 的 Windows 通道存在的意义是证明两个阻断性 win32 表
|
||||
|
||||
门禁逻辑集中在一个脚本里,[scripts/wine-windows-gates.sh](../../../../scripts/wine-windows-gates.sh):ci.yml 作业只供给 runner 状态(缓存、apt Wine)然后调用它,可选的本地门禁 `pnpm run check:windows-wine` 在装有 Wine 的开发机上运行同一个脚本——单一实现,因此本地复现红色 CI 通道不需要在环境之间做任何转译。该本地门禁是诊断工具而非例行检查:仅在排查已知的 Windows 相关失败时运行;日常 win32 信号归 CI 所有,[dsh-pre-push-checks](../../../skills/dsh-pre-push-checks/SKILL.md) 也从不选择它。脚本从不改动工作树:把被跟踪加未跟踪未忽略的文件快照进一个临时目录,只对快照施加 Wine 特有的 pnpm 覆盖,并在那里对着共享 store 安装;Wine prefix 与校验和验证过的 Windows Node zip 持久存放在 `.cache/wine-windows/` 下,本地重跑跳过供给,nodejs.org 不可达时回退到最新的已缓存 zip。
|
||||
|
||||
四条环境约束塑造了该作业,每条都以一次红色运行被发现:Ubuntu 的 `wine64` 包本身不往 PATH 放任何东西(要装 `wine` 调度器);Wine 下的 Node 无法把 stdio 接到 Actions runner 的管道上(引导期 `Socket open EBADF`——所有调用都经文件中转 stdio);Wine 不对 pnpm isolated 布局的 Unix 符号链接做 realpath(即上文的 hoisted 布局);Wine 无法创建 Windows 符号链接(VitePress 的 `linkVue` 报 `ENOTSUP`——`vue` 链接在门禁前由宿主侧铺好)。
|
||||
五条环境约束塑造了 CI 与本地执行,每条都以一次红色运行被发现:Ubuntu 的 `wine64` 包本身不往 PATH 放任何东西(要装 `wine` 调度器);Wine 下的 Node 无法把 stdio 接到调用方的管道上(引导期 `Socket open EBADF`——所有调用都经文件中转 stdio);Wine 不对 pnpm isolated 布局的 Unix 符号链接做 realpath(即上文的 hoisted 布局);macOS Wine 也会把 hoisted workspace 链接暴露为普通目录,因此 client 测试聚合会纳入每个包自己的 CSS 模块声明,而不依赖 project-reference realpath;Wine 无法创建 Windows 符号链接(VitePress 的 `linkVue` 报 `ENOTSUP`——`vue` 链接在门禁前由宿主侧铺好)。
|
||||
|
||||
## 实测结果
|
||||
|
||||
|
||||
33
scripts/client-tsconfig.spec.ts
Normal file
33
scripts/client-tsconfig.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/** Regression coverage for source declarations owned by the client test aggregate. */
|
||||
|
||||
import { existsSync, readdirSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import ts from 'typescript'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const root = fileURLToPath(new URL('..', import.meta.url))
|
||||
|
||||
function clientCssDeclarations(): string[] {
|
||||
const clientRoot = resolve(root, 'packages/client')
|
||||
return readdirSync(clientRoot, { withFileTypes: true })
|
||||
.filter(entry => entry.isDirectory())
|
||||
.map(entry => resolve(clientRoot, entry.name, 'src/css-modules.d.ts'))
|
||||
.filter(existsSync)
|
||||
.sort()
|
||||
}
|
||||
|
||||
describe('client TypeScript aggregate', () => {
|
||||
it('loads package CSS declarations without relying on workspace-link realpaths', () => {
|
||||
const configPath = resolve(root, 'tsconfig.client.json')
|
||||
const read = ts.readConfigFile(configPath, file => ts.sys.readFile(file))
|
||||
if (read.error !== undefined) {
|
||||
throw new Error(ts.flattenDiagnosticMessageText(read.error.messageText, '\n'))
|
||||
}
|
||||
const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, root)
|
||||
const loaded = parsed.fileNames
|
||||
.filter(file => file.endsWith('/src/css-modules.d.ts'))
|
||||
.sort()
|
||||
expect(loaded).toEqual(clientCssDeclarations())
|
||||
})
|
||||
})
|
||||
@@ -14,6 +14,9 @@
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": [
|
||||
// Source-subpath test imports can arrive through workspace links whose
|
||||
// realpath semantics vary by host. Load package CSS declarations directly.
|
||||
"packages/client/*/src/css-modules.d.ts",
|
||||
"packages/client/*/tests/**/*.ts",
|
||||
"packages/client/*/tests/**/*.tsx",
|
||||
"packages/client/tsdown.client.ts",
|
||||
|
||||
Reference in New Issue
Block a user