fix: validate optional Python resources and isolate downloads

This commit is contained in:
yudshj committed 2026-10-09 15:52:59 +08:00
1 parent 305021fcd6
commit 4fd85be130
19 files changed
+335 -149

No files matched your search

+39 -50
View File
@@ -1,10 +1,12 @@
import { chmod, mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises'
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { join } from 'node:path'
import { afterEach, expect, it } from 'vitest'
import { createHash } from 'node:crypto'
import { c as tar } from 'tar'
import { copyOfficeSidecar, downloadOfficeSidecar } from './build-exe-for-python-sdk-office.ts'
import { downloadOfficeSidecar, runtimeNpmArchive } from './build-exe-for-python-sdk-office.ts'
import { officePackageDirectories } from './libreoffice-packages.mjs'
const temporaryDirectories: string[] = []
@@ -26,33 +28,8 @@ async function fixture() {
return { root, staging, destination, packageAt }
}
it('copies package-owned data, licenses, helper permissions, and nested dependencies', async () => {
const { staging, destination, packageAt } = await fixture()
const entry = await packageAt('@deepseek-ai/libreoffice-kit', { optionalDependencies: { '@deepseek-ai/libreoffice-kit-wasm': '0.0.1' }, dependencies: { decoder: '1' } })
const engine = await packageAt('@deepseek-ai/libreoffice-kit-wasm')
const decoder = await packageAt('decoder', { dependencies: { codec: '1' } }, entry)
await packageAt('codec', {}, decoder)
await packageAt('codec', { version: '2.0.0' })
const assets = ['assets/soffice.data', 'prebuilds.json', 'licenses/LICENSE', 'bin/helper']
for (const name of assets) {
const path = join(engine, name)
await mkdir(dirname(path), { recursive: true })
await writeFile(path, name)
}
await chmod(join(engine, 'bin/helper'), 0o755)
const packages = await copyOfficeSidecar(staging, destination, { platform: 'linux', arch: 'x64' })
expect(packages).toHaveLength(4)
expect(await readFile(join(destination, 'node_modules/@deepseek-ai/libreoffice-kit-wasm/assets/soffice.data'), 'utf8')).toBe('assets/soffice.data')
expect(await readFile(join(destination, 'node_modules/@deepseek-ai/libreoffice-kit-wasm/licenses/LICENSE'), 'utf8')).toBe('licenses/LICENSE')
expect(await readFile(join(destination, 'node_modules/@deepseek-ai/libreoffice-kit/node_modules/decoder/node_modules/codec/package.json'), 'utf8')).toContain('codec')
await expect(stat(join(destination, 'node_modules/codec'))).rejects.toMatchObject({ code: 'ENOENT' })
if (process.platform !== 'win32') expect((await stat(join(destination, 'node_modules/@deepseek-ai/libreoffice-kit-wasm/bin/helper'))).mode & 0o111).toBe(0o111)
})
it('copies installed target optionals and leaves other platforms and absent optionals out', async () => {
const { staging, destination, packageAt } = await fixture()
it('locates installed target optionals and leaves other platforms and absent optionals out', async () => {
const { staging, packageAt } = await fixture()
await packageAt('@deepseek-ai/libreoffice-kit', {
optionalDependencies: { native: '1', foreign: '1', absent: '1' },
})
@@ -60,77 +37,74 @@ it('copies installed target optionals and leaves other platforms and absent opti
await packageAt('native', { os: ['linux'], cpu: ['x64'] })
await packageAt('foreign', { os: ['darwin'], cpu: ['arm64'] })
const packages = await copyOfficeSidecar(staging, destination, { platform: 'linux', arch: 'x64' })
const packages = await officePackageDirectories(staging, { platform: 'linux', arch: 'x64' })
expect(packages.map(path => path.replaceAll('\\', '/'))).toEqual([
expect(packages.map(path => path.slice(staging.length + 1).replaceAll('\\', '/'))).toEqual([
'node_modules/@deepseek-ai/libreoffice-kit',
'node_modules/@deepseek-ai/libreoffice-kit-wasm',
'node_modules/native',
])
})
it('rejects a missing required dependency before producing a sidecar', async () => {
const { staging, destination, packageAt } = await fixture()
it('rejects a missing required dependency in the deployed closure', async () => {
const { staging, packageAt } = await fixture()
await packageAt('@deepseek-ai/libreoffice-kit', { dependencies: { 'dsh-missing-office-fixture': '1' } })
await expect(copyOfficeSidecar(staging, destination, { platform: 'linux', arch: 'x64' }))
await expect(officePackageDirectories(staging, { platform: 'linux', arch: 'x64' }))
.rejects.toThrow('dsh-missing-office-fixture required by @deepseek-ai/libreoffice-kit is missing')
await expect(stat(destination)).rejects.toMatchObject({ code: 'ENOENT' })
})
it('rejects an incomplete installed optional instead of omitting it', async () => {
const { staging, destination, packageAt } = await fixture()
const { staging, packageAt } = await fixture()
await packageAt('@deepseek-ai/libreoffice-kit', { optionalDependencies: { broken: '1' } })
await mkdir(join(staging, 'node_modules/broken'), { recursive: true })
await expect(copyOfficeSidecar(staging, destination, { platform: 'linux', arch: 'x64' }))
await expect(officePackageDirectories(staging, { platform: 'linux', arch: 'x64' }))
.rejects.toMatchObject({ code: 'ENOENT' })
})
it('rejects an ancestor dependency outside the deployed closure', async () => {
const { root, staging, destination, packageAt } = await fixture()
const { root, staging, packageAt } = await fixture()
await packageAt('@deepseek-ai/libreoffice-kit', { dependencies: { 'ancestor-office-fixture': '1' } })
await packageAt('ancestor-office-fixture', {}, root)
await expect(copyOfficeSidecar(staging, destination, { platform: 'linux', arch: 'x64' }))
await expect(officePackageDirectories(staging, { platform: 'linux', arch: 'x64' }))
.rejects.toThrow('outside the deployed closure')
})
it.each([true, false])('requires a staged WASM engine when declared=%s even when an ancestor has one', async (declared) => {
const { root, staging, destination, packageAt } = await fixture()
const { root, staging, packageAt } = await fixture()
await packageAt('@deepseek-ai/libreoffice-kit', declared ? { optionalDependencies: { '@deepseek-ai/libreoffice-kit-wasm': '0.0.1' } } : {})
await packageAt('@deepseek-ai/libreoffice-kit-wasm', {}, root)
await expect(copyOfficeSidecar(staging, destination, { platform: 'linux', arch: 'x64' }))
await expect(officePackageDirectories(staging, { platform: 'linux', arch: 'x64' }))
.rejects.toThrow('Office engine @deepseek-ai/libreoffice-kit-wasm required for linux/x64 is missing.')
await expect(stat(destination)).rejects.toMatchObject({ code: 'ENOENT' })
})
it.each([
['darwin', 'arm64', 'darwin-arm64'], ['darwin', 'x64', 'darwin-x64'],
['win32', 'arm64', 'win32-arm64'], ['win32', 'x64', 'win32-x64'],
['linux', 'x64', 'linux-x64'], ['linux', 'arm64', 'wasm'], ['freebsd', 'x64', 'wasm'],
])('copies only the %s/%s engine even when other engines are staged', async (platform, arch, selected) => {
const { staging, destination, packageAt } = await fixture()
])('locates only the %s/%s engine even when other engines are staged', async (platform, arch, selected) => {
const { staging, packageAt } = await fixture()
const targets = ['darwin-arm64', 'darwin-x64', 'win32-arm64', 'win32-x64', 'linux-x64', 'wasm']
const names = targets.map(target => `@deepseek-ai/libreoffice-kit-${target}`)
await packageAt('@deepseek-ai/libreoffice-kit', { optionalDependencies: Object.fromEntries(names.map(name => [name, '0.0.1'])) })
for (const name of names) await packageAt(name)
const expected = `@deepseek-ai/libreoffice-kit-${selected}`
const packages = await copyOfficeSidecar(staging, destination, { platform, arch })
expect(packages.map(path => path.replaceAll('\\', '/'))).toEqual([
const packages = await officePackageDirectories(staging, { platform, arch })
expect(packages.map(path => path.slice(staging.length + 1).replaceAll('\\', '/'))).toEqual([
'node_modules/@deepseek-ai/libreoffice-kit', `node_modules/${expected}`,
])
})
it.each(['darwin', 'win32', 'linux'])('%s requires its native package even when WASM is staged', async (platform) => {
const { staging, destination, packageAt } = await fixture()
const { staging, packageAt } = await fixture()
await packageAt('@deepseek-ai/libreoffice-kit', { optionalDependencies: {
'@deepseek-ai/libreoffice-kit-wasm': '0.0.1', [`@deepseek-ai/libreoffice-kit-${platform}-arm64`]: '0.0.1',
} })
await packageAt('@deepseek-ai/libreoffice-kit-wasm')
await expect(copyOfficeSidecar(staging, destination, { platform, arch: 'arm64' }))
await expect(officePackageDirectories(staging, { platform, arch: 'arm64' }))
.rejects.toThrow(`Office engine @deepseek-ai/libreoffice-kit-${platform}-arm64 required for ${platform}/arm64 is missing.`)
await expect(stat(destination)).rejects.toMatchObject({ code: 'ENOENT' })
})
it('downloads pinned npm packages into the sidecar without resolving or executing package scripts', async () => {
@@ -157,3 +131,18 @@ it('downloads pinned npm packages into the sidecar without resolving or executin
await writeFile(join(cache, checksum.toString('hex')), 'corrupted archive')
await expect(downloadOfficeSidecar([artifact], destination, cache)).rejects.toThrow('checksum mismatch')
})
it('takes npm integrity from the workspace lock without querying registry metadata', async () => {
const { root } = await fixture()
const lockfile = join(root, 'pnpm-lock.yaml')
const integrity = `sha512-${Buffer.alloc(64, 7).toString('base64')}`
await writeFile(lockfile, JSON.stringify({ packages: { '@scope/[email protected]': { resolution: { integrity } } } }))
expect(await runtimeNpmArchive('@scope/fixture', '1.2.3', lockfile)).toEqual({
name: '@scope/fixture', version: '1.2.3', integrity,
url: 'https://registry.npmjs.org/@scope/fixture/-/fixture-1.2.3.tgz',
})
await expect(runtimeNpmArchive('@scope/fixture', '1.2.4', lockfile)).rejects.toThrow('missing SHA-512 integrity')
await writeFile(lockfile, JSON.stringify({ packages: { '@scope/[email protected]': { resolution: { integrity: 'sha256-invalid' } } } }))
await expect(runtimeNpmArchive('@scope/fixture', '1.2.3', lockfile)).rejects.toThrow('missing SHA-512 integrity')
})
+11 -37
View File
@@ -1,7 +1,8 @@
/** Keep LibreOffice workers, prebuilt engines, and their dependencies on the real filesystem. */
import { cp, mkdir, readFile, rm } from 'node:fs/promises'
import { readFile } from 'node:fs/promises'
import { isAbsolute, join, relative, sep } from 'node:path'
import { downloadRuntimeNpmPackage, type RuntimeNpmArchive } from './primary-runtime/prepare.ts'
import { load as parseYaml } from 'js-yaml'
import { officePackageDirectories } from './libreoffice-packages.mjs'
/** pkg applies these exclusions to dependency `files` as well as root asset globs. */
@@ -10,33 +11,6 @@ export const OFFICE_ASSET_IGNORES = [
'**/node_modules/@deepseek-ai/libreoffice-kit-*/**',
]
/**
* Copy the installed Office dependency tree without changing package contents or executable modes.
* Harness sidecars require the kit's declared target native engine, or WASM for other targets.
* Missing target engines fail with their package name; missing required dependencies or paths outside the deployed closure also fail.
* @param staging - Symlink-free deployed Node closure.
* @param destination - Target-specific Office directory beside the executable; replaced when present.
* @param target - Node platform and CPU of the executable.
* @returns Relative package directories included in the sidecar.
*/
export async function copyOfficeSidecar(
staging: string,
destination: string,
target: { platform: string; arch: string },
): Promise<string[]> {
const directories = await officePackageDirectories(staging, target)
await rm(destination, { recursive: true, force: true })
await mkdir(destination, { recursive: true })
for (const source of directories) {
const nestedModules = join(source, 'node_modules')
await cp(source, join(destination, relative(staging, source)), {
recursive: true,
filter: path => path !== nestedModules && !path.startsWith(nestedModules + sep),
})
}
return directories.map(directory => relative(staging, directory))
}
/** One npm package at its existing sidecar dependency location. */
export interface OfficePackageArchive extends RuntimeNpmArchive {
/** Relative package directory inside the sidecar. */
@@ -44,19 +18,19 @@ export interface OfficePackageArchive extends RuntimeNpmArchive {
}
/**
* Read exact npm tarball metadata for a version already selected by the workspace lock.
* Resolve one installed npm version against its reviewed workspace lock integrity.
* @param name - Published package name.
* @param version - Exact installed version.
* @returns Immutable npm tarball identity.
* @param lockfile - Workspace lock recording the exact published archive.
* @returns Locked npm tarball identity; rejects versions without SHA-512 integrity.
*/
export async function runtimeNpmArchive(name: string, version: string): Promise<RuntimeNpmArchive> {
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(name)}/${encodeURIComponent(version)}`)
if (!response.ok) throw new Error(`runtime npm metadata: HTTP ${String(response.status)} for ${name}@${version}`)
const metadata = await response.json() as { name: string; version: string; dist: { tarball: string; integrity: string } }
if (metadata.name !== name || metadata.version !== version || !metadata.dist.integrity.startsWith('sha512-')) {
throw new Error(`runtime npm metadata: invalid identity for ${name}@${version}`)
export async function runtimeNpmArchive(name: string, version: string, lockfile = join(import.meta.dirname, '../pnpm-lock.yaml')): Promise<RuntimeNpmArchive> {
const lock = parseYaml(await readFile(lockfile, 'utf8')) as { packages?: Record<string, { resolution?: { integrity?: string } }> }
const integrity = lock.packages?.[`${name}@${version}`]?.resolution?.integrity
if (typeof integrity !== 'string' || !/^sha512-[A-Za-z0-9+/]+={0,2}$/u.test(integrity)) {
throw new Error(`runtime npm lock: missing SHA-512 integrity for ${name}@${version}`)
}
return { name, version, url: metadata.dist.tarball, integrity: metadata.dist.integrity }
return { name, version, url: `https://registry.npmjs.org/${name}/-/${name.split('/').at(-1)}-${version}.tgz`, integrity }
}
/**
+20 -5
View File
@@ -342,6 +342,8 @@ class SingleExeBuild {
const patch = { bin: ENTRY_BIN, pkg: { assets: ASSET_GLOBS, ignore: [
...OFFICE_ASSET_IGNORES,
`${sourceModules}/**`, `${sourceModules}/.pnpm/**`,
...['packages', 'apps', 'vendor', 'native', 'scripts'].map(directory => `${join(root, directory).replaceAll('\\', '/')}/**`),
`${root.replaceAll('\\', '/')}/tsconfig*.json`,
'**/*.map', '**/*.d.ts', '**/*.d.mts', '**/*.d.cts',
] } }
const manifestPath = join(this.staging, 'package.json')
@@ -355,11 +357,24 @@ class SingleExeBuild {
if (!existsSync(join(this.staging, ENTRY_BIN))) {
throw new Error(`build-exe-for-python-sdk: staged bootstrap ${join(this.staging, ENTRY_BIN)} is missing.`)
}
await bundle({ config: false, tsconfig: false, inputOptions: { tsconfig: false }, target: 'es2024', entry: { 'primary-runtime': join(root, 'scripts/primary-runtime/prepare.ts'),
'office-sidecar': join(root, 'scripts/build-exe-for-python-sdk-office.ts') },
outDir: this.staging, clean: false, format: 'esm', platform: 'node', dts: false,
deps: { neverBundle: [/^@deepseek-ai\//u], alwaysBundle: [/.*/u] }, shims: true, outExtensions: () => ({ js: '.mjs' }),
define: { 'import.meta.main': 'false' } })
const helpers = [
{ name: 'primary-runtime', source: 'scripts/primary-runtime/prepare.ts', exports: 'downloadNodeRuntime, preparePrimaryRuntime' },
{ name: 'office-sidecar', source: 'scripts/build-exe-for-python-sdk-office.ts', exports: 'downloadOfficeSidecar' },
]
const entries: Record<string, string> = {}
for (const helper of helpers) {
const entry = join(this.staging, `.${helper.name}-entry.ts`)
entries[helper.name] = entry
await writeFile(entry, `export { ${helper.exports} } from ${JSON.stringify(join(root, helper.source).replaceAll('\\', '/'))}\n`)
}
try {
await bundle({ config: false, tsconfig: false, inputOptions: { tsconfig: false }, target: 'es2024', entry: entries,
outDir: this.staging, clean: false, format: 'esm', platform: 'node', dts: false,
deps: { neverBundle: [/^@deepseek-ai\//u], alwaysBundle: [/.*/u] }, shims: true, outExtensions: () => ({ js: '.mjs' }),
define: { 'import.meta.main': 'false' } })
} finally {
for (const entry of Object.values(entries)) await rm(entry, { force: true })
}
const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) as Record<string, unknown>
await writeFile(manifestPath, `${JSON.stringify({ ...manifest, ...patch }, null, 2)}\n`)
await this.run('resource helper imports', process.execPath, ['--input-type=module', '--eval',
+5 -1
View File
@@ -1,6 +1,6 @@
import { readFileSync } from 'node:fs'
import { createHash } from 'node:crypto'
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { basename, join } from 'node:path'
import { zipSync } from 'fflate'
@@ -61,7 +61,11 @@ it('extracts a hash-verified cached library without a Python installer or networ
const hash = createHash('sha256').update(libraryWheel).digest('hex')
const archive = join(root, hash)
await writeFile(archive, libraryWheel)
const original = await stat(archive)
expect(await downloadPrimaryRuntimeAsset('https://unused.invalid/library.whl', hash, root)).toBe(archive)
const reused = await stat(archive)
expect(reused.ino).toBe(original.ino)
expect(reused.mtimeMs).toBe(original.mtimeMs)
await unpackPrimaryRuntimeWheel(archive, join(root, 'site-packages'))
expect(await readFile(join(root, 'site-packages/sample.py'), 'utf8')).toBe('sample = 42\n')
await writeFile(archive, 'corrupt archive')
+3
View File
@@ -25,8 +25,10 @@ export async function downloadPrimaryRuntimeAsset(url: string, sha256: string, c
mkdirSync(cache, { recursive: true })
const destination = join(cache, sha256)
let bytes: Buffer
let cached = true
try { bytes = readFileSync(destination) } catch (error) {
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
cached = false
const timeout = Number(process.env.DSH_RESOURCE_DOWNLOAD_TIMEOUT_MS ?? '300000')
if (!Number.isSafeInteger(timeout) || timeout < 1 || timeout > 2_147_483_647) throw new Error('DSH_RESOURCE_DOWNLOAD_TIMEOUT_MS must be an integer from 1 to 2147483647')
const response = await fetch(url, { signal: AbortSignal.timeout(timeout) })
@@ -37,6 +39,7 @@ export async function downloadPrimaryRuntimeAsset(url: string, sha256: string, c
rmSync(destination, { force: true })
throw new Error(`primary runtime download: checksum mismatch for ${url}`)
}
if (cached) return destination
const temporary = join(cache, `.${randomUUID()}`)
try {
writeFileSync(temporary, bytes)
+26 -10
View File
@@ -23,15 +23,17 @@ async function fixture() {
await file(join(root, 'node/node_modules/@deepseek-ai/dsh-sandbox-windows-acl/package.json'), '{"type":"module","exports":{"./runner":"./runner.mjs"}}')
await file(join(root, 'node/node_modules/@deepseek-ai/dsh-sandbox-windows-acl/runner.mjs'), '')
await file(join(root, 'node/node_modules/@deepseek-ai/dsh/package.json'), '{"type":"module"}')
await file(join(root, 'node/node_modules/@deepseek-ai/dsh/lib/bin.js'), 'export async function runCli() { console.log(JSON.stringify({ primary: process.env.DSH_BUNDLED_PRIMARY_RUNTIME, office: process.env.DSH_BUNDLED_OFFICE_CLI })) }')
await file(join(root, 'node/node_modules/@deepseek-ai/dsh/lib/bin.js'), 'export async function runCli() { if (process.argv[2]) { console.log(JSON.stringify({ argv: process.argv.slice(2) })); return } console.log(JSON.stringify({ primary: process.env.DSH_BUNDLED_PRIMARY_RUNTIME, office: process.env.DSH_BUNDLED_OFFICE_CLI })) }')
await file(join(root, 'node/primary-runtime.mjs'), `
import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
export async function downloadNodeRuntime(target, destination) {
if (process.env.DSH_RUNTIME_DOWNLOAD !== undefined) throw new Error("private selector leaked");
await mkdir(join(destination, 'bin'), { recursive: true });
await writeFile(join(destination, 'bin', process.platform === 'win32' ? 'node.exe' : 'node'), 'node');
}
export async function preparePrimaryRuntime({ output }) {
if (process.env.DSH_RUNTIME_DOWNLOAD !== undefined) throw new Error("private selector leaked");
await mkdir(join(output, 'primary-runtime'), { recursive: true });
await writeFile(join(output, 'primary-runtime/runtime.json'), '{}');
}
@@ -51,9 +53,9 @@ async function fixture() {
}
}
`)
function run(flag?: string, extra: NodeJS.ProcessEnv = {}) {
const child = spawn(process.execPath, [bootstrap, ...flag === undefined ? [] : [flag]], {
env: { ...process.env, DSH_RESOURCE_CACHE: cache, DSH_BUNDLED_PRIMARY_RUNTIME: undefined,
function run(resource?: string, extra: NodeJS.ProcessEnv = {}, args: string[] = []) {
const child = spawn(process.execPath, [bootstrap, ...args], {
env: { ...process.env, DSH_RESOURCE_CACHE: cache, DSH_RUNTIME_DOWNLOAD: resource, DSH_BUNDLED_PRIMARY_RUNTIME: undefined,
DSH_OFFICE_SIDECAR: undefined, DSH_BUNDLED_OFFICE_CLI: undefined, ...extra },
stdio: ['pipe', 'pipe', 'pipe'],
})
@@ -78,32 +80,32 @@ it('starts without downloading, independently downloads either resource, and reu
try {
expect(await run().done).toMatchObject({ code: 0, stdout: '{}\n' })
await expect(readdir(cache)).rejects.toMatchObject({ code: 'ENOENT' })
const primary = await run('--download-primary-runtime').done
const primary = await run('primary').done
expect(primary.code, primary.stderr).toBe(0)
expect(JSON.parse(primary.stdout)).toBe(join(cache, 'a'.repeat(64), 'primary', 'primary-runtime'))
await expect(readdir(join(cache, 'a'.repeat(64), 'office'))).rejects.toMatchObject({ code: 'ENOENT' })
const office = await run('--download-office').done
const office = await run('office').done
expect(office.code, office.stderr).toBe(0)
const location = JSON.parse(office.stdout) as string
expect(await readFile(join(location, 'node_modules/@deepseek-ai/libreoffice-kit/lib/cli.js'), 'utf8')).toBe('cli')
expect(await run('--download-office', { DSH_FIXTURE_FAIL: '1' }).done).toMatchObject({ code: 0, stdout: office.stdout })
expect(await run('office', { DSH_FIXTURE_FAIL: '1' }).done).toMatchObject({ code: 0, stdout: office.stdout })
} finally { await rm(root, { recursive: true, force: true }) }
})
it('cleans failed staging and allows an explicit retry', async () => {
const { root, cache, run } = await fixture()
try {
const failed = await run('--download-office', { DSH_FIXTURE_FAIL: '1' }).done
const failed = await run('office', { DSH_FIXTURE_FAIL: '1' }).done
expect(failed.code).toBe(1)
expect(failed.stderr).toContain('fixture download failed')
expect(await readdir(join(cache, 'a'.repeat(64)))).toEqual([])
expect((await run('--download-office').done).code).toBe(0)
expect((await run('office').done).code).toBe(0)
} finally { await rm(root, { recursive: true, force: true }) }
})
it('publishes one complete cache when two processes download concurrently', async () => {
const { root, cache, run } = await fixture()
const jobs = [run('--download-office', { DSH_FIXTURE_BARRIER: '1' }), run('--download-office', { DSH_FIXTURE_BARRIER: '1' })]
const jobs = [run('office', { DSH_FIXTURE_BARRIER: '1' }), run('office', { DSH_FIXTURE_BARRIER: '1' })]
try {
await Promise.all(jobs.map(job => job.ready))
for (const job of jobs) job.child.stdin.end('continue')
@@ -117,3 +119,17 @@ it('publishes one complete cache when two processes download concurrently', asyn
await rm(root, { recursive: true, force: true })
}
})
it('leaves ordinary CLI arguments to the CLI and rejects an invalid private download selector', async () => {
const { root, cache, run } = await fixture()
try {
expect(await run(undefined, {}, ['--download-office']).done).toMatchObject({
code: 0, stdout: JSON.stringify({ argv: ['--download-office'] }) + '\n',
})
const rejected = await run('invalid').done
expect(rejected.code).toBe(1)
expect(rejected.stderr).toContain('DSH_RUNTIME_DOWNLOAD must select office or primary')
await expect(readdir(cache)).rejects.toMatchObject({ code: 'ENOENT' })
} finally { await rm(root, { recursive: true, force: true }) }
})
+11 -4
View File
@@ -884,9 +884,16 @@ def main() -> None:
def smoke_sdk_authoring(base_url: str, executable: Path, update_snapshots: bool) -> None:
"""Query the bundled Python and switch skills without replacing that environment."""
with tempfile.TemporaryDirectory(prefix="dsh-sdk-authoring-cache-") as cache_directory:
_smoke_sdk_authoring_cached(base_url, executable, update_snapshots, Path(cache_directory))
def _smoke_sdk_authoring_cached(base_url: str, executable: Path, update_snapshots: bool, cache: Path) -> None:
from deepseek_harness import DeepSeekHarness
primary = Path(json.loads(subprocess.check_output([str(executable), "--download-primary-runtime"], text=True)))
cache_environment = {**os.environ, "DSH_RESOURCE_CACHE": str(cache), "DSH_RUNTIME_DOWNLOAD": "primary"}
primary = Path(json.loads(subprocess.check_output([str(executable)], encoding="utf-8", env=cache_environment)))
resources = primary.parent
manifest = json.loads((primary / "runtime.json").read_text())
for mode in ("default", "replacement", "disabled"):
@@ -904,7 +911,7 @@ def smoke_sdk_authoring(base_url: str, executable: Path, update_snapshots: bool)
provider="deepseek-official", model="smoke-model", cwd=str(root),
dsh_bin=str(executable), dsh_home=str(home), patches=(str(patch),),
api_key="sk-keyless-smoke", base_url=base_url,
env={"DSH_PERMISSION_MODE": "danger-full-access", "DSH_TELEMETRY_DISABLED": "1"},
env={"DSH_PERMISSION_MODE": "danger-full-access", "DSH_TELEMETRY_DISABLED": "1", "DSH_RESOURCE_CACHE": str(cache)},
request_timeout_seconds=60,
) as harness:
result = harness.run(AUTHORING_PROMPT, session_id="authoring")
@@ -960,7 +967,7 @@ def smoke_sdk_office(executable: Path) -> None:
else:
shutil.copy2(source, destination)
cache_environment = {**os.environ, "DSH_RESOURCE_CACHE": str(root / "cache")}
office = Path(json.loads(subprocess.check_output([str(relocated), "--download-office"], text=True, env=cache_environment)))
office = Path(json.loads(subprocess.check_output([str(relocated)], encoding="utf-8", env={**cache_environment, "DSH_RUNTIME_DOWNLOAD": "office"})))
assert not (office.parent / "primary").exists(), "Office download unexpectedly prepared the authoring environment"
adapter = office / "node_modules/@deepseek-ai/libreoffice-kit/package.json"
native = stem.removeprefix("deepseek-harness-sdk-runtime-").replace("win-", "win32-").replace("macos-", "darwin-")
@@ -1034,7 +1041,7 @@ recalculate(root / 'input.xlsx', root / 'calculated.xlsx')
with zipfile.ZipFile(root / 'calculated.xlsx') as archive:
sheet = ElementTree.fromstring(archive.read('xl/worksheets/sheet1.xml'))
assert sheet.find('.//{*}c[@r="A1"]/{*}v').text == '3'
""", str(root), expected_backend], env={**cache_environment, "DSH_OFFICE_SIDECAR": str(office)}, check=True, timeout=180)
""", str(root), expected_backend], env={name: value for name, value in cache_environment.items() if name != "DSH_OFFICE_SIDECAR"}, check=True, timeout=180)
assert not (office.parent / "primary").exists(), "Python Office API unexpectedly downloaded authoring resources"
print(f"smoke-python-runtime: relocated Office {result['backend']} DOCX produced {len(pdf)} PDF bytes")