From d0810c63dd1392a451e76b4d0df17276281f8a80 Mon Sep 17 00:00:00 2001 From: yilsem Date: Sun, 11 Oct 2026 17:39:23 +0300 Subject: [PATCH] fix(desktop): fetch Windows window binding in Linux packaging --- .gitea/workflows/ci.yml | 2 ++ apps/desktop/scripts/stage-native-deps.mjs | 16 ++++++++++------ apps/desktop/scripts/stage-native-deps.test.mjs | 16 +++++++++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index a974304..f85e701 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -54,6 +54,8 @@ jobs: github-server-url: https://gitea.turkservis.online - name: Install locked source dependencies run: npm ci --no-audit --no-fund + - name: Native dependency staging regression tests + run: npx vitest run --root apps/desktop scripts/stage-native-deps.test.mjs --maxWorkers=2 - name: Build and package Windows x64 run: | set -eu diff --git a/apps/desktop/scripts/stage-native-deps.mjs b/apps/desktop/scripts/stage-native-deps.mjs index 385d031..fb7daa1 100644 --- a/apps/desktop/scripts/stage-native-deps.mjs +++ b/apps/desktop/scripts/stage-native-deps.mjs @@ -546,7 +546,7 @@ export function stageGetWindowsInto( export function installGetWindowsNativeBinding( srcRoot, - { resolveInstaller, spawn = spawnSync } = {} + { resolveInstaller, spawn = spawnSync, platform = process.platform, arch = process.arch } = {} ) { let installerPath try { @@ -562,7 +562,11 @@ export function installGetWindowsNativeBinding( throw new Error(`[stage-native-deps] cannot resolve get-windows native installer: ${detail}`) } - const result = spawn(process.execPath, [installerPath, 'install', '--fallback-to-build'], { + const crossTarget = platform !== process.platform || arch !== process.arch + const installerArgs = crossTarget + ? ['install', `--target_platform=${platform}`, `--target_arch=${arch}`, '--fallback-to-build=false'] + : ['install', '--fallback-to-build'] + const result = spawn(process.execPath, [installerPath, ...installerArgs], { cwd: srcRoot, stdio: 'inherit' }) @@ -606,11 +610,11 @@ export function stageGetWindows( ) } - // Only a win32 host can produce the win32 binding, so a cross-platform pack - // has nothing to gain from the native installer. + // Download the target prebuild even on Linux/Wine; cross-target installs + // never fall back to compiling a host binary. const install = - platform === 'win32' && process.platform === 'win32' - ? () => installGetWindowsNativeBinding(srcRoot) + platform === 'win32' && arch !== 'arm64' + ? () => installGetWindowsNativeBinding(srcRoot, { platform, arch }) : undefined return stageGetWindowsInto(srcRoot, destRoot, { platform, arch, install }) } diff --git a/apps/desktop/scripts/stage-native-deps.test.mjs b/apps/desktop/scripts/stage-native-deps.test.mjs index 9d20a03..96e44da 100644 --- a/apps/desktop/scripts/stage-native-deps.test.mjs +++ b/apps/desktop/scripts/stage-native-deps.test.mjs @@ -594,7 +594,9 @@ test('darwin staging ships the Swift helper executable and the rewritten windows stageGetWindowsInto(srcRoot, destRoot, { platform: 'darwin' }) - assert.equal(fs.statSync(join(destRoot, 'main')).mode & 0o777, 0o755) + if (process.platform !== 'win32') { + assert.equal(fs.statSync(join(destRoot, 'main')).mode & 0o777, 0o755) + } const staged = fs.readFileSync(join(destRoot, 'lib', 'windows.js'), 'utf8') assert.match(staged, /Rewritten by stage-native-deps\.mjs/) assert.ok(!staged.includes('node-pre-gyp'), 'pre-gyp loader must not survive staging') @@ -635,3 +637,15 @@ test('win32-x64 staging fails when get-windows is absent', () => { /get-windows is not installed/ ) }) + +// Linux/Wine packaging must fetch the Windows payload, never compile for Linux. +test('get-windows cross-target install requests the target prebuild without compilation fallback', () => { + const calls = [] + const platform = process.platform === 'win32' ? 'linux' : 'win32' + installGetWindowsNativeBinding('/fake/get-windows', { + platform, arch: 'x64', resolveInstaller: () => '/fake/node-pre-gyp', + spawn: (command, args) => { calls.push(args); return { status: 0 } } + }) + assert.deepEqual(calls[0], ['/fake/node-pre-gyp', 'install', + `--target_platform=${platform}`, '--target_arch=x64', '--fallback-to-build=false']) +})