feat: complete WorkBuddy parity and Windows release workflow
This commit is contained in:
1 parent
143516b3a8
commit
d5512df549
434 files changed
+54977
-1854
No files matched your search
@@ -0,0 +1,103 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { promises as fs } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { configureSandbox } from '../src/main/sandbox/configuration'
|
||||
import { importSandboxToolchain, listSandboxToolchains } from '../src/main/sandbox/toolchains'
|
||||
import { executeSandboxCommand, sandboxStatus } from '../src/main/sandbox/service'
|
||||
import { restoreTransaction } from '../src/main/sandbox/transactions'
|
||||
import { importSandboxDependencies } from '../src/main/sandbox/dependency-cache'
|
||||
|
||||
if (process.platform !== 'win32') throw new Error('Toolchain regression requires real Windows isolation')
|
||||
await fs.mkdir(resolve('.tmp'), { recursive: true })
|
||||
const fixture = await fs.mkdtemp(resolve('.tmp/sandbox-toolchain-'))
|
||||
const workspace = join(fixture, 'workspace'), stateRoot = join(fixture, 'state'), outside = join(fixture, 'outside.txt')
|
||||
await fs.mkdir(workspace); await fs.writeFile(outside, 'host-only-fixture')
|
||||
configureSandbox({ stateRoot, resourceDirectory: resolve('resources/sandbox/win32-x64') })
|
||||
const checks: Array<{ label: string; pass: boolean }> = []
|
||||
function check(label: string, condition: unknown): void { checks.push({ label, pass: !!condition }); assert.ok(condition, label); console.log(`PASS ${label}`) }
|
||||
const context = { workspaceRoot: workspace, sessionId: 'toolchain-session', runId: 'toolchain-run', toolCallId: 'toolchain-call', approve: async () => true }
|
||||
try {
|
||||
check('Node/Python start missing instead of inheriting host PATH', (await listSandboxToolchains()).filter(item => item.kind !== 'git').every(item => item.state === 'missing'))
|
||||
await assert.rejects(executeSandboxCommand('node --version', { ...context, runtimes: ['node'] }), /尚未注册/)
|
||||
check('requesting a missing runtime fails closed', true)
|
||||
await assert.rejects(importSandboxToolchain('node', 'relative-node.exe'), /绝对路径/)
|
||||
const node = await importSandboxToolchain('node', await fs.realpath(process.execPath))
|
||||
check('explicit Node import persists content-addressed file identities and npm', node.state === 'registered' && !!node.id && !!node.files && node.features!.includes('npm'))
|
||||
process.env.YCODE_DUMMY_MODEL_TOKEN = 'not-a-real-credential'
|
||||
let result = await executeSandboxCommand('node -e "console.log(process.version); console.log(process.env.YCODE_DUMMY_MODEL_TOKEN || \'NO_TOKEN\'); console.log(process.env.NPM_CONFIG_OFFLINE)"', { ...context, runtimes: ['node'] })
|
||||
check('real Node executes inside the native sandbox with a clean environment', result.ok && result.data.stdout.includes(process.version) && result.data.stdout.includes('NO_TOKEN') && !result.data.stdout.includes('not-a-real-credential'))
|
||||
check('native execution returns the exact staged runtime identity', result.data.toolchains?.[0]?.id === node.id)
|
||||
await fs.writeFile(join(workspace, 'host-read.cjs'), `try { require('fs').readFileSync(${JSON.stringify(outside)});process.exit(9) } catch(error) { if(!['EACCES','EPERM'].includes(error.code))throw error; console.log('HOST_READ_DENIED') }`)
|
||||
result = await executeSandboxCommand('node host-read.cjs', { ...context, runtimes: ['node'] })
|
||||
check('real Node cannot read the ordinary host fixture', result.ok && result.data.stdout.includes('HOST_READ_DENIED'))
|
||||
await fs.writeFile(join(workspace, 'runtime-ro.cjs'), "const fs=require('fs'),path=require('path');const target=path.join(path.dirname(process.execPath),'node_modules/npm/package.json');fs.readFileSync(target);try {fs.appendFileSync(target,'x');process.exit(9)}catch(error){if(!['EACCES','EPERM'].includes(error.code))throw error;console.log('RUNTIME_READONLY')}")
|
||||
result = await executeSandboxCommand('node runtime-ro.cjs', { ...context, runtimes: ['node'] })
|
||||
check('native ACLs prevent modification of the staged interpreter', result.ok && result.data.stdout.includes('RUNTIME_READONLY'))
|
||||
await fs.writeFile(join(workspace, 'package.json'), JSON.stringify({ name: 'ycode-sandbox-fixture', version: '1.0.0', private: true, scripts: { build: 'node build.cjs', test: 'node test.cjs' } }))
|
||||
await fs.writeFile(join(workspace, 'build.cjs'), "require('fs').writeFileSync('artifact.txt','built-in-sandbox')")
|
||||
await fs.writeFile(join(workspace, 'test.cjs'), "require('assert').equal(require('fs').readFileSync('artifact.txt','utf8'),'built-in-sandbox');console.log('NODE_PROJECT_PASS')")
|
||||
result = await executeSandboxCommand('npm run build && npm test', { ...context, runtimes: ['node'] })
|
||||
check('npm builds and tests a real offline project in the snapshot', result.ok && result.data.stdout.includes('NODE_PROJECT_PASS') && result.data.writeback === 'committed')
|
||||
check('npm build artifact reaches the host only through approved transaction', await fs.readFile(join(workspace, 'artifact.txt'), 'utf8') === 'built-in-sandbox' && !!result.data.transactionId)
|
||||
await restoreTransaction(result.data.transactionId!, workspace)
|
||||
const pythonSource = execFileSync('where.exe', ['python'], { encoding: 'utf8', windowsHide: true }).trim().split(/\r?\n/)[0]
|
||||
const python = await importSandboxToolchain('python', await fs.realpath(pythonSource))
|
||||
check('Python standard library and pip are imported without executing source on the host', python.state === 'registered' && python.features!.includes('pip'))
|
||||
result = await executeSandboxCommand('python -c "import sys,json,ssl,sqlite3; print(\'PYTHON_STDLIB_PASS\'); print(sys.prefix)"', { ...context, runtimes: ['python'] })
|
||||
check('real Python loads its private standard library and extension modules', result.ok && result.data.stdout.includes('PYTHON_STDLIB_PASS') && !result.data.stdout.includes(pythonSource))
|
||||
result = await executeSandboxCommand('python -m pip --version', { ...context, runtimes: ['python'] })
|
||||
check('pip runs from the controlled Python runtime', result.ok && /pip \d/.test(result.data.stdout))
|
||||
await fs.writeFile(join(workspace, 'test_fixture.py'), "from pathlib import Path\nimport unittest\nclass Fixture(unittest.TestCase):\n def test_write(self):\n Path('python-artifact.txt').write_text('python-built',encoding='utf-8')\n self.assertEqual(Path('python-artifact.txt').read_text(), 'python-built')\n")
|
||||
result = await executeSandboxCommand('python -m unittest test_fixture', { ...context, runtimes: ['python'] })
|
||||
check('Python tests execute and write approved artifacts', result.ok && result.data.writeback === 'committed' && await fs.readFile(join(workspace, 'python-artifact.txt'), 'utf8') === 'python-built')
|
||||
await restoreTransaction(result.data.transactionId!, workspace)
|
||||
check('runtime status exposes separate registered Node and Python capabilities', (await sandboxStatus()).toolchains?.filter(item => item.kind !== 'git').every(item => item.state === 'registered'))
|
||||
await fs.mkdir(join(workspace, 'node_modules/fixture-package'), { recursive: true })
|
||||
await fs.writeFile(join(workspace, 'node_modules/fixture-package/package.json'), JSON.stringify({ name: 'fixture-package', version: '1.0.0', exports: { require: './index.cjs', import: './esm.mjs' } }))
|
||||
await fs.writeFile(join(workspace, 'node_modules/fixture-package/index.cjs'), "module.exports={value:'DEPENDENCY_CJS_PASS'}")
|
||||
await fs.writeFile(join(workspace, 'node_modules/fixture-package/esm.mjs'), "export const value='DEPENDENCY_ESM_PASS'")
|
||||
await fs.writeFile(join(workspace, 'node_modules/fixture-package/.env'), 'DUMMY_PACKAGE_CREDENTIAL')
|
||||
const nodeDependencies = await importSandboxDependencies(workspace, 'node')
|
||||
check('dependency import binds workspace lock inputs and runtime identity while filtering credentials', nodeDependencies.excluded === 1 && nodeDependencies.identity.runtimeId === node.id && nodeDependencies.files === 3)
|
||||
result = await executeSandboxCommand('node -e "console.log(require(\'fixture-package\').value)" && node --input-type=module -e "import {value} from \'fixture-package\';console.log(value)"', { ...context, runtimes: ['node'], dependencies: ['node'] })
|
||||
check('controlled node_modules supports real CommonJS and ESM resolution', result.ok && result.data.stdout.includes('DEPENDENCY_CJS_PASS') && result.data.stdout.includes('DEPENDENCY_ESM_PASS') && result.data.writeback === 'unchanged')
|
||||
check('execution records the exact workspace dependency layer identity', result.data.dependencies?.[0]?.id === nodeDependencies.identity.id)
|
||||
await fs.writeFile(join(workspace, 'dep-ro.cjs'), "const fs=require('fs'),p=require.resolve('fixture-package');fs.readFileSync(p);try{fs.appendFileSync(p,'x');process.exit(9)}catch(error){if(!['EACCES','EPERM'].includes(error.code))throw error;console.log('DEPENDENCY_READONLY')}")
|
||||
result = await executeSandboxCommand('node dep-ro.cjs', { ...context, runtimes: ['node'], dependencies: ['node'] })
|
||||
check('native ACLs enforce dependency-layer immutability', result.ok && result.data.stdout.includes('DEPENDENCY_READONLY'))
|
||||
await fs.writeFile(join(workspace, 'package-lock.json'), '{}')
|
||||
await assert.rejects(executeSandboxCommand('node --version', { ...context, runtimes: ['node'], dependencies: ['node'] }), /锁文件已变化/)
|
||||
check('changed project lock inputs invalidate a cached dependency layer', true)
|
||||
await fs.unlink(join(workspace, 'package-lock.json'))
|
||||
const anotherWorkspace = join(fixture, 'another-workspace'); await fs.mkdir(anotherWorkspace)
|
||||
await assert.rejects(executeSandboxCommand('node --version', { ...context, workspaceRoot: anotherWorkspace, runtimes: ['node'], dependencies: ['node'] }), /ENOENT/)
|
||||
check('one workspace cannot accidentally select another workspace dependency cache', true)
|
||||
await fs.mkdir(join(workspace, '.venv/Lib/site-packages/fixture_module'), { recursive: true })
|
||||
await fs.writeFile(join(workspace, '.venv/Lib/site-packages/fixture_module/__init__.py'), "value='PYTHON_DEPENDENCY_PASS'\n")
|
||||
const pythonDependencies = await importSandboxDependencies(workspace, 'python')
|
||||
result = await executeSandboxCommand('python -c "import fixture_module;print(fixture_module.value)"', { ...context, runtimes: ['python'], dependencies: ['python'] })
|
||||
check('Python imports a workspace-scoped controlled site-packages layer', result.ok && result.data.stdout.includes('PYTHON_DEPENDENCY_PASS') && result.data.dependencies?.[0]?.id === pythonDependencies.identity.id)
|
||||
const cacheKey = (await fs.readdir(join(stateRoot, 'dependencies')))[0]
|
||||
const cacheFile = join(stateRoot, 'dependencies', cacheKey, 'objects', nodeDependencies.identity.id, 'files/fixture-package/index.cjs')
|
||||
const cacheBytes = await fs.readFile(cacheFile)
|
||||
await fs.appendFile(cacheFile, 'tampered')
|
||||
await assert.rejects(executeSandboxCommand('node --version', { ...context, runtimes: ['node'], dependencies: ['node'] }), /依赖文件摘要不匹配/)
|
||||
check('a tampered dependency layer fails closed before execution', true)
|
||||
await fs.writeFile(cacheFile, cacheBytes)
|
||||
const alias = JSON.parse(await fs.readFile(join(stateRoot, 'toolchains/node.json'), 'utf8'))
|
||||
const executable = join(stateRoot, 'toolchains/objects', alias.id, 'files/node.exe')
|
||||
const bytes = await fs.readFile(executable)
|
||||
await fs.appendFile(executable, 'tampered')
|
||||
await assert.rejects(executeSandboxCommand('node --version', { ...context, runtimes: ['node'] }), /摘要不匹配/)
|
||||
check('a tampered registered interpreter is rejected before native execution', true)
|
||||
await fs.writeFile(executable, bytes)
|
||||
await assert.rejects(executeSandboxCommand('echo bad', { ...context, runtimes: ['node', 'node'] }), /选择无效/)
|
||||
await assert.rejects(executeSandboxCommand('echo bad', { ...context, runtimes: ['../host'] }), /只支持/)
|
||||
check('runtime IDs cannot be duplicated or used as arbitrary source paths', true)
|
||||
console.log(`SANDBOX_TOOLCHAIN_PASS ${checks.length}/${checks.length}`)
|
||||
} finally {
|
||||
delete process.env.YCODE_DUMMY_MODEL_TOKEN
|
||||
await fs.writeFile(join(fixture, 'report.json'), JSON.stringify({ checks, fixture, limitations: ['This suite covers Node/npm, Python/pip and immutable dependency layers; private Git repository coverage is in test:sandbox:git. Host Git metadata integration, network installs, persistent mutable workspace, pnpm link layouts and full native-addon compatibility remain pending.'] }, null, 2) + '\n')
|
||||
console.log(`SANDBOX_TOOLCHAIN_REPORT ${join(fixture, 'report.json')}`)
|
||||
}
|
||||
Reference in new issue
Block a user