Files
deepseek-harness/scripts/build-exe-for-python-sdk-staging.ts
T
Tianyi Cui 4260fd2451 fix(scripts): keep the Python runtime payload out of its own deploy root
pnpm's legacy deploy links the deployed project into the target's node_modules
when the target sits inside that project. Materializing that link copies the
payload into itself and fails with ERR_FS_CP_EINVAL, which broke the
release-shaped Python runtime matrix on Linux and Windows. The staged payload
never carries the project source, so the staging tree's owner drops the entry
and leaves the deploy root's files alone.
2026-10-07 12:58:25 +08:00

59 lines
2.5 KiB
TypeScript

/** Turn the deploy-time package links of the Python runtime payload into real files. */
import { cp, lstat, readdir, realpath, rm } from 'node:fs/promises'
import { join, sep } from 'node:path'
/** Return the first symbolic link below a directory, if one exists. */
async function findSymlink(directory: string): Promise<string | undefined> {
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = join(directory, entry.name)
const metadata = await lstat(path)
if (metadata.isSymbolicLink()) return path
if (metadata.isDirectory()) {
const nested = await findSymlink(path)
if (nested !== undefined) return nested
}
}
return undefined
}
/**
* Replace every deploy-time package link below the staged payload with real files.
*
* Legacy deploy links workspace packages into the target's `node_modules`, and
* those links must survive packaging as files. One link is not a package: when
* the deploy target sits inside the deployed project, pnpm links that project
* into its own `node_modules`. Dereferencing it would copy the payload into
* itself, and the packaged payload never carries the project source, so the
* entry is dropped instead.
* @param staging - Deployed payload root whose `node_modules` becomes symlink-free.
*/
export async function materializeStagedLinks(staging: string): Promise<void> {
const payload = await realpath(staging)
const nodeModules = join(staging, 'node_modules')
let remaining = await findSymlink(nodeModules)
while (remaining !== undefined) {
const segments = remaining.slice(nodeModules.length + 1).split(sep)
const binIndex = segments.lastIndexOf('.bin')
if (binIndex >= 0) {
await rm(join(nodeModules, ...segments.slice(0, binIndex + 1)), { recursive: true, force: true })
remaining = await findSymlink(nodeModules)
continue
}
const destination = remaining
const source = await realpath(destination)
if (payload === source || payload.startsWith(source + sep)) {
await rm(destination, { recursive: true, force: true })
remaining = await findSymlink(nodeModules)
continue
}
const nestedNodeModules = join(source, 'node_modules')
await rm(destination, { recursive: true, force: true })
await cp(source, destination, {
recursive: true,
dereference: true,
filter: path => path !== nestedNodeModules && !path.startsWith(nestedNodeModules + sep),
})
remaining = await findSymlink(nodeModules)
}
}