dsh 0.2.0-rc.2: in-container update (experimental channel), path-agnostic compose, upstream patches adapted to 0.1.x/0.2.x
This commit is contained in:
commit
e6dcef9bf5
302 files changed
+75178
No files matched your search
@@ -0,0 +1,245 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { cp, mkdir, mkdtemp, readFile, readdir, readlink, rm, symlink, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import test from 'node:test'
|
||||
import {
|
||||
buildRuntime,
|
||||
RuntimeSlots,
|
||||
verifyNpmIntegrity,
|
||||
verifyPatchSet,
|
||||
verifyRuntimePatches,
|
||||
} from '../../control-plane/modules/patch-manager/index.mjs'
|
||||
import { verifyDshCompatibility } from '../lib/dsh-compatibility.mjs'
|
||||
|
||||
const containerRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url))))
|
||||
|
||||
async function pristine() {
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-pristine-'))
|
||||
await mkdir(join(root, 'lib'), { recursive: true })
|
||||
await writeFile(join(root, 'lib', 'bin.js'), `#!/usr/bin/env node
|
||||
const invocation = parseDshArgs(process.argv.slice(2), readVersion());
|
||||
switch (invocation.mode) {
|
||||
case "profile": {
|
||||
const { runProfile } = await import("./profile-boot-fixture.js");
|
||||
}
|
||||
}
|
||||
|
||||
`)
|
||||
await writeFile(join(root, 'lib', 'profile-boot-fixture.js'), 'import { r as runProfile } from "./profile-boot-implementation.js";\nexport { runProfile };\n')
|
||||
await writeFile(join(root, 'lib', 'profile-boot-implementation.js'), `async function runProfile(options) {
|
||||
\tprocess.on("SIGTERM", () => {
|
||||
\t\tinterrupt(0);
|
||||
\t});
|
||||
\tprocess.on("SIGINT", () => { interrupt(130); });
|
||||
\tconst ctx = await boot(NAME, rootConfig, patches, (hostCtx) => {
|
||||
\t\thostCtx.provide(DSH_LAUNCH_ENVIRONMENT_KEY, options.environment);
|
||||
\t});
|
||||
\treturn {
|
||||
\t\tctx,
|
||||
\t\tshutdown
|
||||
\t};
|
||||
}
|
||||
export { runProfile as r };
|
||||
`)
|
||||
const picker = join(root, 'node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib')
|
||||
const connection = join(root, 'node_modules/@deepseek-ai/dsh-client-connection/lib')
|
||||
const theme = join(root, 'node_modules/@deepseek-ai/dsh-client-ui-theme/lib')
|
||||
await mkdir(picker, { recursive: true })
|
||||
await mkdir(connection, { recursive: true })
|
||||
await mkdir(theme, { recursive: true })
|
||||
await writeFile(join(picker, 'index.js'), 'const target = resolve(path ?? home);\n')
|
||||
await writeFile(join(connection, 'client.js'), 'isLoopback: pageLocation === void 0 || isLoopbackHostname(pageLocation.hostname),\n')
|
||||
await writeFile(join(theme, 'client.js'), 'var corner_shape_css_default = "@supports (corner-shape:superellipse(1.5)){:root{--dsw-corner-shape:superellipse(1.5)}*,:before,:after{corner-shape:var(--dsw-corner-shape)}}";\n')
|
||||
await mkdir(join(root, 'node_modules/.bin'), { recursive: true })
|
||||
await symlink('../tool/bin.js', join(root, 'node_modules/.bin/tool'))
|
||||
return root
|
||||
}
|
||||
|
||||
async function transportOwnedPristine() {
|
||||
const root = await pristine()
|
||||
await writeFile(
|
||||
join(root, 'node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'),
|
||||
'isLoopback: transport?.ownsHost === true || pageLocation === void 0 || isLoopbackHostname(pageLocation.hostname),\n',
|
||||
)
|
||||
return root
|
||||
}
|
||||
|
||||
async function environment(root, patches) {
|
||||
const environmentRoot = join(root, 'environment')
|
||||
await mkdir(join(environmentRoot, 'artifacts'), { recursive: true })
|
||||
const artifacts = []
|
||||
const references = []
|
||||
for (const [index, path] of patches.entries()) {
|
||||
const bytes = await readFile(path)
|
||||
const sha256 = createHash('sha256').update(bytes).digest('hex')
|
||||
const id = `patch-${String(index + 1)}`
|
||||
await cp(path, join(environmentRoot, 'artifacts', id))
|
||||
artifacts.push({
|
||||
id,
|
||||
mediaType: 'text/javascript',
|
||||
sha256,
|
||||
size: bytes.byteLength,
|
||||
url: `https://example.invalid/${id}`,
|
||||
})
|
||||
references.push({ id, sha256 })
|
||||
}
|
||||
await writeFile(join(environmentRoot, 'environment.manifest.json'), JSON.stringify({
|
||||
schema: 1,
|
||||
manifestType: 'environment',
|
||||
version: '1.0.0',
|
||||
keyringGeneration: 1,
|
||||
targetSequence: 1,
|
||||
issuedAt: '2026-08-20T00:00:00.000Z',
|
||||
artifacts,
|
||||
bootstrapApi: 1,
|
||||
components: [],
|
||||
patches: references,
|
||||
systemPlugins: [],
|
||||
}))
|
||||
return environmentRoot
|
||||
}
|
||||
|
||||
test('rebuilds each Runtime from unchanged Pristine with the complete ordered Patch set', async () => {
|
||||
const source = await pristine()
|
||||
const originalPicker = await readFile(join(source, 'node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib/index.js'))
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-'))
|
||||
const patches = [
|
||||
join(containerRoot, 'environment/resources/patches/directory-picker.mjs'),
|
||||
join(containerRoot, 'environment/resources/patches/browser-loopback.mjs'),
|
||||
join(containerRoot, 'environment/resources/patches/managed-lifecycle.mjs'),
|
||||
join(containerRoot, 'environment/resources/patches/native-corners.mjs'),
|
||||
]
|
||||
const first = await buildRuntime({ pristineRoot: source, versionsRoot: join(root, 'versions'), runtimeId: 'one', patchPaths: patches })
|
||||
const second = await buildRuntime({ pristineRoot: source, versionsRoot: join(root, 'versions'), runtimeId: 'two', patchPaths: patches })
|
||||
assert.deepEqual(await readFile(join(source, 'node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib/index.js')), originalPicker)
|
||||
for (const runtime of [first, second]) {
|
||||
assert.match(await readFile(join(runtime, 'package/node_modules/@deepseek-ai/dsh-host-directory-picker-browse/lib/index.js'), 'utf8'), /DSH_DEFAULT_WORKSPACE/)
|
||||
assert.match(await readFile(join(runtime, 'package/node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'), 'utf8'), /isLoopback: true/)
|
||||
assert.match(await readFile(join(runtime, 'package/lib/bin.js'), 'utf8'), /prepareManagedInvocation/)
|
||||
assert.match(await readFile(join(runtime, 'package/lib/profile-boot-implementation.js'), 'utf8'), /managedSigtermHandler/)
|
||||
assert.match(await readFile(join(runtime, 'package/node_modules/@deepseek-ai/dsh-client-ui-theme/lib/client.js'), 'utf8'), /corner_shape_css_default = ""/)
|
||||
assert.equal(await readlink(join(runtime, 'package/node_modules/.bin/tool')), '../tool/bin.js')
|
||||
}
|
||||
})
|
||||
|
||||
test('rejects a Patch mismatch without publishing a partial Runtime', async () => {
|
||||
const source = await pristine()
|
||||
await writeFile(join(source, 'node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'), 'changed upstream\n')
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-failure-'))
|
||||
await assert.rejects(buildRuntime({
|
||||
pristineRoot: source,
|
||||
versionsRoot: join(root, 'versions'),
|
||||
runtimeId: 'broken',
|
||||
patchPaths: [join(containerRoot, 'environment/resources/patches/browser-loopback.mjs')],
|
||||
}), /found 0/)
|
||||
await assert.rejects(readFile(join(root, 'versions/broken/package/lib/bin.js')), { code: 'ENOENT' })
|
||||
})
|
||||
|
||||
test('builds a Runtime from the transport-owned browser classification', async () => {
|
||||
const source = await transportOwnedPristine()
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-transport-loopback-'))
|
||||
const runtime = await buildRuntime({
|
||||
pristineRoot: source,
|
||||
versionsRoot: join(root, 'versions'),
|
||||
runtimeId: 'transport-loopback',
|
||||
patchPaths: [join(containerRoot, 'environment/resources/patches/browser-loopback.mjs')],
|
||||
})
|
||||
assert.equal(
|
||||
await readFile(join(runtime, 'package/node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'), 'utf8'),
|
||||
'isLoopback: true,\n',
|
||||
)
|
||||
})
|
||||
|
||||
test('verifies mandatory Patch Artifacts and their applied Runtime effects before startup', async () => {
|
||||
const source = await pristine()
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-verification-'))
|
||||
const patches = [
|
||||
join(containerRoot, 'environment/resources/patches/directory-picker.mjs'),
|
||||
join(containerRoot, 'environment/resources/patches/browser-loopback.mjs'),
|
||||
join(containerRoot, 'environment/resources/patches/managed-lifecycle.mjs'),
|
||||
join(containerRoot, 'environment/resources/patches/native-corners.mjs'),
|
||||
]
|
||||
const runtimeRoot = await buildRuntime({
|
||||
pristineRoot: source,
|
||||
versionsRoot: join(root, 'versions'),
|
||||
runtimeId: 'verified',
|
||||
patchPaths: patches,
|
||||
})
|
||||
const environmentRoot = await environment(root, patches)
|
||||
assert.deepEqual(await verifyRuntimePatches({ runtimeRoot, environmentRoot }), ['patch-1', 'patch-2', 'patch-3', 'patch-4'])
|
||||
|
||||
await writeFile(
|
||||
join(runtimeRoot, 'package/node_modules/@deepseek-ai/dsh-client-connection/lib/client.js'),
|
||||
'isLoopback: false,\n',
|
||||
)
|
||||
await assert.rejects(verifyRuntimePatches({ runtimeRoot, environmentRoot }), /Patch verification failed/)
|
||||
await writeFile(join(environmentRoot, 'artifacts', 'patch-2'), 'modified artifact\n')
|
||||
await assert.rejects(verifyRuntimePatches({ runtimeRoot, environmentRoot }), /differs from the Environment Manifest/)
|
||||
})
|
||||
|
||||
test('checks npm SHA-512 integrity and tracks Runtime current/previous slots', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-slots-'))
|
||||
const tarball = join(root, 'dsh.tgz')
|
||||
const bytes = Buffer.from('tarball fixture')
|
||||
await writeFile(tarball, bytes)
|
||||
await verifyNpmIntegrity(tarball, `sha512-${createHash('sha512').update(bytes).digest('base64')}`)
|
||||
await assert.rejects(verifyNpmIntegrity(tarball, `sha512-${Buffer.alloc(64).toString('base64')}`), { code: 'TRUST_ARTIFACT_MISMATCH' })
|
||||
await mkdir(join(root, 'runtime/versions/one'), { recursive: true })
|
||||
await mkdir(join(root, 'runtime/versions/two'), { recursive: true })
|
||||
const slots = new RuntimeSlots(join(root, 'runtime'))
|
||||
await slots.promote('one')
|
||||
await slots.promote('two')
|
||||
assert.deepEqual(await slots.state(), { current: 'two', previous: 'one' })
|
||||
await slots.rollback()
|
||||
assert.deepEqual(await slots.state(), { current: 'one', previous: 'two' })
|
||||
})
|
||||
|
||||
test('prunes only Runtime versions outside current and previous slots', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-runtime-prune-'))
|
||||
const slots = new RuntimeSlots(root)
|
||||
for (const version of ['one', 'two', 'old']) await mkdir(join(root, 'versions', version), { recursive: true })
|
||||
const imageSeed = await mkdtemp(join(tmpdir(), 'dsh-runtime-image-seed-'))
|
||||
await writeFile(join(imageSeed, 'sentinel'), 'keep')
|
||||
await symlink(imageSeed, join(root, 'versions/image-old'), 'dir')
|
||||
await slots.promote('one')
|
||||
await slots.promote('two')
|
||||
assert.deepEqual(await slots.prune(), ['image-old', 'old'])
|
||||
assert.deepEqual((await readdir(join(root, 'versions'))).sort(), ['one', 'two'])
|
||||
assert.equal(await readFile(join(imageSeed, 'sentinel'), 'utf8'), 'keep')
|
||||
})
|
||||
|
||||
test('builds and verifies the real DSH 0.1.5 Runtime when its npm tree is provided', {
|
||||
skip: process.env.DSH_COMPAT_PACKAGE_ROOT === undefined,
|
||||
}, async t => {
|
||||
const source = resolve(process.env.DSH_COMPAT_PACKAGE_ROOT)
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-real-runtime-'))
|
||||
t.after(() => rm(root, { recursive: true, force: true }))
|
||||
const patches = [
|
||||
join(containerRoot, 'environment/resources/patches/directory-picker.mjs'),
|
||||
join(containerRoot, 'environment/resources/patches/browser-loopback.mjs'),
|
||||
join(containerRoot, 'environment/resources/patches/managed-lifecycle.mjs'),
|
||||
join(containerRoot, 'environment/resources/patches/native-corners.mjs'),
|
||||
]
|
||||
const runtime = await buildRuntime({
|
||||
pristineRoot: source,
|
||||
versionsRoot: join(root, 'versions'),
|
||||
runtimeId: 'dsh-0.1.5-rc.1',
|
||||
patchPaths: patches,
|
||||
})
|
||||
const packageRoot = join(runtime, 'package')
|
||||
await verifyPatchSet(packageRoot, patches)
|
||||
assert.deepEqual(await verifyDshCompatibility({
|
||||
packageRoot,
|
||||
expectedVersion: '0.1.5-rc.1',
|
||||
home: join(root, 'home'),
|
||||
}), {
|
||||
name: '@deepseek-ai/dsh',
|
||||
version: '0.1.5-rc.1',
|
||||
packages: 10,
|
||||
remoteMethods: 3,
|
||||
webRows: 5,
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user