mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-20 15:33:54 +00:00
fix(desktop): prevent staging wrong-platform node-pty binary for cross targets
stageNodePty received electron-builder's { platform, arch } but unconditionally
copied host build/Release, staging a host binary (e.g. macOS Mach-O) for a
foreign target (e.g. linux-arm64). The fallback rebuild also didn't pass the
target arch to electron-rebuild.
Now:
- build/Release is only staged when target platform+arch match the host
- cross-platform targets with no matching prebuild fail closed
- same-platform different-arch rebuild passes --arch to electron-rebuild
- post-staging validation reads .node magic bytes (ELF/Mach-O/PE) and rejects
any binary whose platform doesn't match the target
Adds stageNodePtyInto() (testable core) and classifyNativeBinary() (pure),
plus 11 regression tests covering the cross-target scenarios.
This commit is contained in:
parent
47d56b802a
commit
7a44a8fdec
2 changed files with 396 additions and 20 deletions
|
|
@ -18,6 +18,7 @@ import {
|
|||
existsSync,
|
||||
mkdirSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
rmSync
|
||||
} from 'node:fs'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
|
|
@ -79,9 +80,115 @@ function copyBuildRelease(srcDir, destDir) {
|
|||
}
|
||||
}
|
||||
|
||||
export function stageNodePty({ platform = process.platform, arch = process.arch } = {}) {
|
||||
const srcRoot = resolveNodePtyRoot()
|
||||
const destRoot = resolve(projectRoot, 'dist/node_modules/node-pty')
|
||||
// ─── binary classification ───────────────────────────────────────────
|
||||
//
|
||||
// .node files are shared libraries in the target platform's native binary
|
||||
// format. By reading the first few bytes (magic) we can determine which
|
||||
// platform a given .node was compiled for, without shelling out to `file`.
|
||||
//
|
||||
// ELF (\x7fELF) → linux
|
||||
// Mach-O 32-bit (feedface) → darwin
|
||||
// Mach-O 64-bit (feedfacf) → darwin
|
||||
// Fat/Universal (cafebabe) → darwin
|
||||
// PE (MZ DOS header) → win32
|
||||
//
|
||||
// Exported for unit testing.
|
||||
|
||||
/**
|
||||
* Classify a native binary's target platform from its magic bytes.
|
||||
* Returns `'linux'`, `'darwin'`, `'win32'`, or `null` if unrecognized
|
||||
* or the file cannot be read.
|
||||
*/
|
||||
export function classifyNativeBinary(filePath) {
|
||||
let buf
|
||||
try {
|
||||
buf = readFileSync(filePath, { start: 0, end: 63 }) // first 64 bytes
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
if (buf.length < 4) return null
|
||||
|
||||
// ELF: \x7f E L F
|
||||
if (buf[0] === 0x7f && buf[1] === 0x45 && buf[2] === 0x4c && buf[3] === 0x46) {
|
||||
return 'linux'
|
||||
}
|
||||
// Mach-O 32-bit: feedface
|
||||
if (buf[0] === 0xfe && buf[1] === 0xed && buf[2] === 0xfa && buf[3] === 0xce) {
|
||||
return 'darwin'
|
||||
}
|
||||
// Mach-O 64-bit: feedfacf
|
||||
if (buf[0] === 0xfe && buf[1] === 0xed && buf[2] === 0xfa && buf[3] === 0xcf) {
|
||||
return 'darwin'
|
||||
}
|
||||
// Fat/Universal binary: cafebabe
|
||||
if (buf[0] === 0xca && buf[1] === 0xfe && buf[2] === 0xba && buf[3] === 0xbe) {
|
||||
return 'darwin'
|
||||
}
|
||||
// PE: MZ DOS header
|
||||
if (buf[0] === 0x4d && buf[1] === 0x5a) {
|
||||
return 'win32'
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the staged destination tree for .node files and verify each one's
|
||||
* binary platform matches the requested target. Throws on any mismatch.
|
||||
*
|
||||
* This is the fail-closed safety net: even if a prebuild or build/Release
|
||||
* somehow slipped through with the wrong platform, this catches it before
|
||||
* the package ships a broken native binary to users.
|
||||
*/
|
||||
function validateStagedBinaries(destRoot, targetPlatform) {
|
||||
const mismatches = []
|
||||
function scan(dir, relPrefix) {
|
||||
if (!existsSync(dir)) return
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
if (entry.isDirectory()) {
|
||||
scan(join(dir, entry.name), `${relPrefix}${entry.name}/`)
|
||||
continue
|
||||
}
|
||||
if (!entry.name.endsWith('.node')) continue
|
||||
const fullPath = join(dir, entry.name)
|
||||
const classified = classifyNativeBinary(fullPath)
|
||||
if (classified !== targetPlatform) {
|
||||
mismatches.push({ file: `${relPrefix}${entry.name}`, classified, expected: targetPlatform })
|
||||
}
|
||||
}
|
||||
}
|
||||
scan(join(destRoot, 'prebuilds'), 'prebuilds/')
|
||||
scan(join(destRoot, 'build', 'Release'), 'build/Release/')
|
||||
if (mismatches.length > 0) {
|
||||
throw new Error(
|
||||
`[stage-native-deps] native binary platform mismatch (target=${targetPlatform}):\n` +
|
||||
mismatches
|
||||
.map((m) => ` ${m.file}: expected ${m.expected}, got ${m.classified ?? 'unknown'}`)
|
||||
.join('\n') +
|
||||
`\nRefusing to stage a binary compiled for the wrong platform.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stage node-pty's native runtime dependencies into `destRoot`.
|
||||
*
|
||||
* Exported separately from `stageNodePty` so tests can supply a fake
|
||||
* node-pty source tree without going through real module resolution.
|
||||
*
|
||||
* Strategy (fail-closed):
|
||||
*
|
||||
* 1. Copy the matching prebuild (`prebuilds/<platform>-<arch>/`) if present.
|
||||
* 2. Copy `build/Release/` **only when the target matches the host** —
|
||||
* build/Release contains a binary compiled for the host's platform/arch,
|
||||
* so staging it for a different target ships a broken app.
|
||||
* 3. If no native binary was staged:
|
||||
* - Same platform as host, different arch → run `electron-rebuild --arch`.
|
||||
* - Different platform from host → throw (cannot cross-compile native
|
||||
* modules; build on the target platform or provide a prebuild).
|
||||
* 4. Validate every staged `.node` file's binary platform matches the target.
|
||||
*/
|
||||
export function stageNodePtyInto(srcRoot, destRoot, { platform = process.platform, arch = process.arch } = {}) {
|
||||
const hostMatch = platform === process.platform && arch === process.arch
|
||||
|
||||
rmSync(destRoot, { recursive: true, force: true })
|
||||
mkdirSync(destRoot, { recursive: true })
|
||||
|
|
@ -119,47 +226,77 @@ export function stageNodePty({ platform = process.platform, arch = process.arch
|
|||
|
||||
// build/Release/* — present when node-pty was compiled locally
|
||||
// (e.g. no prebuild available for this Electron ABI/platform combo).
|
||||
// Some installs won't have this at all if prebuild-install succeeded.
|
||||
const buildReleaseDir = join(srcRoot, 'build/Release')
|
||||
copyBuildRelease(buildReleaseDir, join(destRoot, 'build/Release'))
|
||||
// Only stage this when the target matches the host, because
|
||||
// build/Release contains a binary compiled for the *host's* platform
|
||||
// and architecture. Staging a host binary for a different target (e.g.
|
||||
// a macOS Mach-O .node staged for a linux-arm64 target) ships a broken
|
||||
// app that crashes the first time a terminal is spawned.
|
||||
if (hostMatch) {
|
||||
const buildReleaseDir = join(srcRoot, 'build/Release')
|
||||
copyBuildRelease(buildReleaseDir, join(destRoot, 'build/Release'))
|
||||
}
|
||||
|
||||
// If neither a prebuild nor build/Release produced a .node binary for this
|
||||
// target, run electron-rebuild to compile one from source. This happens on
|
||||
// CI (npm ci --ignore-scripts skips postinstall) and on platforms where
|
||||
// node-pty doesn't publish prebuilds (e.g. linux-x64).
|
||||
// Check whether a native binary for this target was staged.
|
||||
const stagedDirs = [
|
||||
join(destRoot, 'prebuilds', `${platform}-${arch}`),
|
||||
join(destRoot, 'build/Release')
|
||||
]
|
||||
const hasNativeBinary = stagedDirs.some(dir => {
|
||||
const hasNativeBinary = stagedDirs.some((dir) => {
|
||||
if (!existsSync(dir)) return false
|
||||
return readdirSync(dir, { recursive: true }).some(name => String(name).endsWith('.node'))
|
||||
return readdirSync(dir, { recursive: true }).some((name) => String(name).endsWith('.node'))
|
||||
})
|
||||
|
||||
if (!hasNativeBinary) {
|
||||
if (platform !== process.platform) {
|
||||
throw new Error(
|
||||
`[stage-native-deps] no prebuilt binary for ${platform}-${arch} and ` +
|
||||
`cannot cross-compile native modules from ${process.platform}-${process.arch}. ` +
|
||||
`Build on the target platform or provide a prebuild.`
|
||||
)
|
||||
}
|
||||
// Same platform, possibly different arch — rebuild from source with
|
||||
// the target architecture so electron-rebuild produces the correct
|
||||
// binary rather than defaulting to the host's arch.
|
||||
console.log(
|
||||
`[stage-native-deps] no prebuilt or compiled native binary for ${platform}-${arch}; ` +
|
||||
`running electron-rebuild to compile from source...`
|
||||
)
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
['../../node_modules/.bin/electron-rebuild', '-f', '-w', 'node-pty'],
|
||||
{ cwd: projectRoot, stdio: 'inherit' }
|
||||
`[stage-native-deps] no native binary for ${platform}-${arch}; ` +
|
||||
`running electron-rebuild (target arch: ${arch})...`
|
||||
)
|
||||
const rebuildArgs = [
|
||||
'../../node_modules/.bin/electron-rebuild',
|
||||
'-f',
|
||||
'-w',
|
||||
'node-pty',
|
||||
'--arch',
|
||||
arch
|
||||
]
|
||||
const result = spawnSync(process.execPath, rebuildArgs, {
|
||||
cwd: projectRoot,
|
||||
stdio: 'inherit'
|
||||
})
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
`electron-rebuild failed for ${platform}-${arch} (exit ${result.status}). ` +
|
||||
`Cannot stage node-pty without a native binary.`
|
||||
`Cannot stage node-pty without a native binary.`
|
||||
)
|
||||
}
|
||||
// Re-copy build/Release after electron-rebuild populated it.
|
||||
const buildReleaseDir = join(srcRoot, 'build/Release')
|
||||
copyBuildRelease(buildReleaseDir, join(destRoot, 'build/Release'))
|
||||
}
|
||||
|
||||
// Validate every staged .node binary matches the target platform.
|
||||
validateStagedBinaries(destRoot, platform)
|
||||
|
||||
console.log(`[stage-native-deps] staged node-pty (${platform}-${arch}) -> ${destRoot}`)
|
||||
return destRoot
|
||||
}
|
||||
|
||||
export function stageNodePty({ platform = process.platform, arch = process.arch } = {}) {
|
||||
const srcRoot = resolveNodePtyRoot()
|
||||
const destRoot = resolve(projectRoot, 'dist/node_modules/node-pty')
|
||||
return stageNodePtyInto(srcRoot, destRoot, { platform, arch })
|
||||
}
|
||||
|
||||
// Allow direct CLI invocation: node scripts/stage-native-deps.mjs [platform] [arch]
|
||||
if (isMain(import.meta.url)) {
|
||||
const [platform, arch] = process.argv.slice(2)
|
||||
|
|
|
|||
239
apps/desktop/scripts/stage-native-deps.test.mjs
Normal file
239
apps/desktop/scripts/stage-native-deps.test.mjs
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import fs, { existsSync } from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { test } from 'vitest'
|
||||
|
||||
import {
|
||||
stageNodePtyInto,
|
||||
classifyNativeBinary
|
||||
} from '../scripts/stage-native-deps.mjs'
|
||||
|
||||
const { join } = path
|
||||
|
||||
// ─── fixtures ──────────────────────────────────────────────────────
|
||||
//
|
||||
// Create minimal fake .node files with correct magic bytes so the
|
||||
// binary classifier and the staging validator exercise real code paths
|
||||
// without needing actual native modules.
|
||||
|
||||
/** Write a fake .node file with the given platform's magic bytes. */
|
||||
function makeFakeNode(filePath, platform) {
|
||||
const headers = {
|
||||
linux: Buffer.from([0x7f, 0x45, 0x4c, 0x46, 0x00, 0x00, 0x00, 0x00]), // ELF
|
||||
darwin: Buffer.from([0xfe, 0xed, 0xfa, 0xcf, 0x00, 0x00, 0x00, 0x00]), // Mach-O 64-bit
|
||||
win32: Buffer.from([0x4d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), // MZ (PE)
|
||||
}
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
||||
fs.writeFileSync(filePath, headers[platform] ?? headers.linux)
|
||||
}
|
||||
|
||||
/** Create a minimal fake node-pty source tree in a temp dir. */
|
||||
function makeFakeNodePty(srcRoot, { prebuildPlatform, prebuildArch } = {}) {
|
||||
fs.mkdirSync(srcRoot, { recursive: true })
|
||||
fs.writeFileSync(join(srcRoot, 'package.json'), JSON.stringify({ name: 'node-pty', main: 'lib/index.js' }))
|
||||
fs.mkdirSync(join(srcRoot, 'lib'), { recursive: true })
|
||||
fs.writeFileSync(join(srcRoot, 'lib', 'index.js'), 'module.exports = {};')
|
||||
|
||||
if (prebuildPlatform && prebuildArch) {
|
||||
const prebuildDir = join(srcRoot, 'prebuilds', `${prebuildPlatform}-${prebuildArch}`)
|
||||
makeFakeNode(join(prebuildDir, 'pty.node'), prebuildPlatform)
|
||||
}
|
||||
}
|
||||
|
||||
// ─── classifyNativeBinary tests ─────────────────────────────────────
|
||||
|
||||
test('classifyNativeBinary detects ELF as linux', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const f = join(tmp, 'test.node')
|
||||
fs.writeFileSync(f, Buffer.from([0x7f, 0x45, 0x4c, 0x46, 0x00, 0x00]))
|
||||
assert.equal(classifyNativeBinary(f), 'linux')
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('classifyNativeBinary detects Mach-O 64-bit as darwin', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const f = join(tmp, 'test.node')
|
||||
fs.writeFileSync(f, Buffer.from([0xfe, 0xed, 0xfa, 0xcf, 0x00, 0x00]))
|
||||
assert.equal(classifyNativeBinary(f), 'darwin')
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('classifyNativeBinary detects Mach-O 32-bit as darwin', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const f = join(tmp, 'test.node')
|
||||
fs.writeFileSync(f, Buffer.from([0xfe, 0xed, 0xfa, 0xce, 0x00, 0x00]))
|
||||
assert.equal(classifyNativeBinary(f), 'darwin')
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('classifyNativeBinary detects PE (MZ) as win32', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const f = join(tmp, 'test.node')
|
||||
fs.writeFileSync(f, Buffer.from([0x4d, 0x5a, 0x00, 0x00, 0x00, 0x00]))
|
||||
assert.equal(classifyNativeBinary(f), 'win32')
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('classifyNativeBinary returns null for unrecognized magic', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const f = join(tmp, 'test.node')
|
||||
fs.writeFileSync(f, Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
|
||||
assert.equal(classifyNativeBinary(f), null)
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('classifyNativeBinary returns null for a missing file', () => {
|
||||
assert.equal(classifyNativeBinary('/nonexistent/path/to/thing.node'), null)
|
||||
})
|
||||
|
||||
// ─── cross-target regression tests ──────────────────────────────────
|
||||
//
|
||||
// The core bug: stageNodePty receives { platform, arch } from
|
||||
// electron-builder but unconditionally copies host build/Release, staging
|
||||
// a host binary for a foreign target. These tests prove the fix:
|
||||
//
|
||||
// 1. A host build/Release must NOT be staged for a foreign platform.
|
||||
// 2. A matching prebuild IS staged for a foreign target.
|
||||
// 3. A foreign target with no prebuild throws (fail closed).
|
||||
// 4. A host build/Release IS staged for a matching target.
|
||||
// 5. Validation rejects a binary whose magic bytes don't match the target.
|
||||
|
||||
test('cross-target: host build/Release is NOT staged for a foreign platform', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const srcRoot = join(tmp, 'node-pty')
|
||||
const destRoot = join(tmp, 'dest')
|
||||
|
||||
// Create a node-pty tree with ONLY a host build/Release (no prebuild).
|
||||
makeFakeNodePty(srcRoot)
|
||||
const buildReleaseDir = join(srcRoot, 'build', 'Release')
|
||||
makeFakeNode(join(buildReleaseDir, 'pty.node'), process.platform)
|
||||
|
||||
// Request a foreign platform (different from the host).
|
||||
const foreignPlatform = process.platform === 'linux' ? 'darwin' : 'linux'
|
||||
|
||||
assert.throws(
|
||||
() => stageNodePtyInto(srcRoot, destRoot, { platform: foreignPlatform, arch: 'x64' }),
|
||||
/cannot cross-compile/i
|
||||
)
|
||||
|
||||
// build/Release must NOT have been copied to the dest tree.
|
||||
assert.equal(
|
||||
existsSync(join(destRoot, 'build', 'Release', 'pty.node')),
|
||||
false,
|
||||
'host build/Release .node must not be staged for a foreign target'
|
||||
)
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('cross-target: matching prebuild IS staged for a foreign target', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const srcRoot = join(tmp, 'node-pty')
|
||||
const destRoot = join(tmp, 'dest')
|
||||
|
||||
// Host is (say) darwin. Request linux-x64, which has a prebuild.
|
||||
const foreignPlatform = process.platform === 'linux' ? 'darwin' : 'linux'
|
||||
makeFakeNodePty(srcRoot, { prebuildPlatform: foreignPlatform, prebuildArch: 'x64' })
|
||||
|
||||
// Also create a host build/Release that should NOT be staged.
|
||||
makeFakeNode(join(srcRoot, 'build', 'Release', 'pty.node'), process.platform)
|
||||
|
||||
stageNodePtyInto(srcRoot, destRoot, { platform: foreignPlatform, arch: 'x64' })
|
||||
|
||||
// The foreign prebuild must be staged.
|
||||
const stagedPrebuild = join(destRoot, 'prebuilds', `${foreignPlatform}-x64`, 'pty.node')
|
||||
assert.equal(existsSync(stagedPrebuild), true, 'foreign prebuild must be staged')
|
||||
|
||||
// The host build/Release must NOT be staged.
|
||||
assert.equal(
|
||||
existsSync(join(destRoot, 'build', 'Release', 'pty.node')),
|
||||
false,
|
||||
'host build/Release must not be staged for a foreign target'
|
||||
)
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('cross-target: foreign target with no prebuild throws (fail closed)', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const srcRoot = join(tmp, 'node-pty')
|
||||
const destRoot = join(tmp, 'dest')
|
||||
|
||||
// Create a tree with a host build/Release but no foreign prebuild.
|
||||
makeFakeNodePty(srcRoot)
|
||||
makeFakeNode(join(srcRoot, 'build', 'Release', 'pty.node'), process.platform)
|
||||
|
||||
const foreignPlatform = process.platform === 'linux' ? 'darwin' : 'linux'
|
||||
|
||||
assert.throws(
|
||||
() => stageNodePtyInto(srcRoot, destRoot, { platform: foreignPlatform, arch: 'x64' }),
|
||||
/cannot cross-compile/i
|
||||
)
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('host-target: host build/Release IS staged for a matching target', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const srcRoot = join(tmp, 'node-pty')
|
||||
const destRoot = join(tmp, 'dest')
|
||||
|
||||
makeFakeNodePty(srcRoot)
|
||||
makeFakeNode(join(srcRoot, 'build', 'Release', 'pty.node'), process.platform)
|
||||
|
||||
stageNodePtyInto(srcRoot, destRoot, { platform: process.platform, arch: process.arch })
|
||||
|
||||
assert.equal(
|
||||
existsSync(join(destRoot, 'build', 'Release', 'pty.node')),
|
||||
true,
|
||||
'host build/Release must be staged for a matching target'
|
||||
)
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('validation rejects a staged binary with the wrong platform magic', () => {
|
||||
const tmp = fs.mkdtempSync(join(os.tmpdir(), 'hermes-stage-'))
|
||||
try {
|
||||
const srcRoot = join(tmp, 'node-pty')
|
||||
const destRoot = join(tmp, 'dest')
|
||||
|
||||
// Create a prebuild dir that claims to be linux-x64 but contains
|
||||
// a darwin (Mach-O) binary. This simulates the original bug where
|
||||
// a host binary ends up in a foreign target's prebuild slot.
|
||||
makeFakeNodePty(srcRoot, { prebuildPlatform: 'linux', prebuildArch: 'x64' })
|
||||
// Overwrite the prebuild .node with the WRONG platform magic.
|
||||
makeFakeNode(join(srcRoot, 'prebuilds', 'linux-x64', 'pty.node'), 'darwin')
|
||||
|
||||
assert.throws(
|
||||
() => stageNodePtyInto(srcRoot, destRoot, { platform: 'linux', arch: 'x64' }),
|
||||
/platform mismatch/i
|
||||
)
|
||||
} finally {
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue