Files
deepseek-harness/scripts/executable-packaging.ts
T
fz 67e2d1e506 fix(python): preserve deployed dependency validation
(cherry picked from commit e18abca88589347975a5866925c590a16f1b2aed)
2026-11-02 16:07:29 +08:00

83 lines
3.5 KiB
TypeScript

/** Shared production-deploy preparation for the SDK and SSH executable carriers. */
import { existsSync, statSync } from 'node:fs'
import { cp, mkdir, readFile } from 'node:fs/promises'
import { dirname, extname, join, resolve, sep } from 'node:path'
/**
* Resolve pnpm without invoking a command shell, including Windows shims.
* @param args - pnpm arguments.
* @returns the executable and its argument vector.
*/
export function pnpmInvocation(args: string[]): [command: string, args: string[]] {
const entrypoint = process.env.npm_execpath?.trim()
if (entrypoint !== undefined && entrypoint !== '') {
const extension = extname(entrypoint).toLowerCase()
if (extension === '.js' || extension === '.cjs' || extension === '.mjs') {
return [process.execPath, [entrypoint, ...args]]
}
if (extension !== '.cmd') return [entrypoint, args]
}
const home = process.env.PNPM_HOME?.trim()
if (home !== undefined && home !== '') {
const packageBin = resolve(home, '..', 'pnpm', 'bin')
for (const filename of ['pnpm.mjs', 'pnpm.cjs']) {
const candidate = resolve(packageBin, filename)
if (existsSync(candidate)) return [process.execPath, [candidate, ...args]]
}
}
if (process.platform === 'win32') {
throw new Error('executable-packaging: pnpm must expose a JavaScript entrypoint through npm_execpath or PNPM_HOME on Windows.')
}
return ['pnpm', args]
}
/**
* Restore direct dependencies omitted by pnpm's legacy hoister, excluding nested module trees.
* @param staging - deployed runtime root.
* @param sourceNodeModules - deploy source's installed dependencies.
* @returns names restored from the source installation.
*/
export async function restoreLegacyHoists(staging: string, sourceNodeModules: string): Promise<string[]> {
const manifestPath = join(staging, 'package.json')
const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) as {
dependencies?: Record<string, string>
}
const restored: string[] = []
for (const dependency of Object.keys(manifest.dependencies ?? {}).sort()) {
const destination = join(staging, 'node_modules', dependency)
if (existsSync(destination)) continue
const source = join(sourceNodeModules, dependency)
if (!existsSync(source)) {
throw new Error(
`executable-packaging: deployed dependency ${dependency} is absent from both ${destination} and ${source}.`,
)
}
await mkdir(dirname(destination), { recursive: true })
const nestedNodeModules = join(source, 'node_modules')
await cp(source, destination, {
recursive: true,
dereference: true,
filter: path => path !== nestedNodeModules && !path.startsWith(nestedNodeModules + sep),
})
restored.push(dependency)
}
await verifyStagedDependencies(staging)
return restored
}
/**
* Reject a deployed payload whose manifest names missing direct dependency directories.
* @param staging - Deployed payload root containing package.json and node_modules.
* @returns Resolves when every declared direct dependency has a directory.
*/
export async function verifyStagedDependencies(staging: string): Promise<void> {
const manifest = JSON.parse(await readFile(join(staging, 'package.json'), 'utf8')) as {
dependencies?: Record<string, string>
}
const missing = Object.keys(manifest.dependencies ?? {}).sort()
.filter(dependency => !statSync(join(staging, 'node_modules', dependency), { throwIfNoEntry: false })?.isDirectory())
if (missing.length > 0) {
throw new Error(`executable-packaging: staged dependencies remain missing: ${missing.join(', ')}.`)
}
}