fix(ime): actually ship @xterm/xterm 6.1.0-beta.304 (v1.0.21 built from stale node_modules with 6.0.0); add predist dependency-consistency gate (scripts/verify-deps.cjs) and rename-retry shim for the AV scan EPERM race
This commit is contained in:
1 parent
ac43844f8a
commit
40336d014d
9 files changed
+128
-4
No files matched your search
@@ -0,0 +1,30 @@
|
||||
// 构建垫片:Windows 上安全软件实时扫描新解压的文件时会短暂持有目录句柄(实测 ~4s),
|
||||
// electron-builder 解包后立即 rename 会撞 EPERM。对 fs.promises 的 rename/rm/unlink/copyFile
|
||||
// 加 EPERM/EBUSY/ENOTEMPTY 重试(30 次 × 1s),stderr 会打印每次重试。
|
||||
// 用法(命中 EPERM 时):NODE_OPTIONS="--require $(pwd -W)/scripts/rename-retry-shim.cjs" npm run dist
|
||||
const fs = require('node:fs');
|
||||
|
||||
const RETRY_CODES = new Set(['EPERM', 'EBUSY', 'ENOTEMPTY']);
|
||||
const MAX_TRIES = 30;
|
||||
const DELAY_MS = 1000;
|
||||
|
||||
function wrap(name) {
|
||||
const orig = fs.promises[name];
|
||||
if (typeof orig !== 'function') return;
|
||||
fs.promises[name] = async function (...args) {
|
||||
for (let i = 0; ; i++) {
|
||||
try {
|
||||
return await orig.apply(fs.promises, args);
|
||||
} catch (err) {
|
||||
if (!RETRY_CODES.has(err && err.code) || i >= MAX_TRIES - 1) throw err;
|
||||
process.stderr.write(`[rename-retry-shim] ${name} ${args[0]} -> ${err.code}, retry ${i + 1}/${MAX_TRIES}\n`);
|
||||
await new Promise(r => setTimeout(r, DELAY_MS));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
wrap('rename');
|
||||
wrap('rm');
|
||||
wrap('unlink');
|
||||
wrap('copyFile');
|
||||
@@ -0,0 +1,59 @@
|
||||
/* Dependency consistency gate: node_modules must match package-lock.json.
|
||||
|
||||
Usage: node scripts/verify-deps.cjs (runs first in `predist`)
|
||||
|
||||
Why it exists: v1.0.21 shipped an input-method fix that never reached the
|
||||
build. @xterm/xterm had been pinned to 6.1.0-beta.304 in package.json, but
|
||||
node_modules still held 6.0.0 from an older install — `npm install
|
||||
--package-lock-only` (the last step of predist) rewrites only the lockfile,
|
||||
so the lockfile agreed with package.json while the tree on disk stayed
|
||||
stale, and the bundler took the stale tree. An install that never happened
|
||||
is invisible to every other check, so it gets its own gate here.
|
||||
|
||||
Scope: version equality only, for every entry the lockfile lists under
|
||||
node_modules. The root entry ("") is deliberately excluded — bumping
|
||||
package.json makes the lockfile root version lag by design until the
|
||||
`npm install --package-lock-only` at the end of predist rewrites it. */
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
|
||||
const ROOT = path.join(__dirname, '..')
|
||||
const lock = JSON.parse(fs.readFileSync(path.join(ROOT, 'package-lock.json'), 'utf8'))
|
||||
const packages = lock.packages ?? {}
|
||||
|
||||
const mismatched = []
|
||||
let checked = 0
|
||||
// Optional entries are the cross-platform variants (darwin/linux/arm64 …) that
|
||||
// npm records in the lockfile but never installs on this machine. Their absence
|
||||
// is expected, not drift.
|
||||
let skippedOptional = 0
|
||||
|
||||
for (const [key, entry] of Object.entries(packages)) {
|
||||
if (key === '' || !key.startsWith('node_modules/')) continue
|
||||
// The key is the path under node_modules, so it also resolves nested
|
||||
// ("node_modules/a/node_modules/b") and scoped ("node_modules/@scope/pkg")
|
||||
// entries without any name reconstruction.
|
||||
let installed
|
||||
try {
|
||||
installed = JSON.parse(fs.readFileSync(path.join(ROOT, key, 'package.json'), 'utf8')).version
|
||||
} catch {
|
||||
installed = undefined
|
||||
}
|
||||
if (installed === undefined) {
|
||||
if (entry.optional) skippedOptional++
|
||||
else mismatched.push([key, entry.version, 'MISSING'])
|
||||
continue
|
||||
}
|
||||
checked++
|
||||
if (installed !== entry.version) mismatched.push([key, entry.version, installed])
|
||||
}
|
||||
|
||||
if (mismatched.length) {
|
||||
console.error('node_modules does not match package-lock.json:')
|
||||
for (const [name, expected, actual] of mismatched) {
|
||||
console.error(` ${name}: lockfile expects ${expected}, installed ${actual}`)
|
||||
}
|
||||
console.error('\nnode_modules 与 package-lock.json 不一致,请运行 npm ci 重装依赖')
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`dependencies ok: ${checked} packages verified, ${skippedOptional} optional entries skipped`)
|
||||
Reference in new issue
Block a user