feat: complete WorkBuddy parity and Windows release workflow

This commit is contained in:
xiaohan.yuan committed 2026-10-18 12:45:21 +08:00
1 parent 143516b3a8
commit d5512df549
434 files changed
+54977 -1854

No files matched your search

+122
View File
@@ -0,0 +1,122 @@
import { promises as fs } from 'node:fs'
import { spawn } from 'node:child_process'
import { join } from 'node:path'
import { dirname, resolve } from 'node:path'
import { randomUUID } from 'node:crypto'
import { sandboxConfiguration } from './configuration'
import { stageSandboxToolchains } from './toolchains'
import { verifiedBinary } from './native-component'
import { sealWorkspace, saveWorkspaceRecord, workspaceRecord } from './workspace'
import { assertActive } from './policy'
import { assertPolicyCurrent } from './policy-store'
import type { SandboxEffectivePolicy } from '@shared/sandbox'
export interface GitControl {
root: string
repository: string
run(args: string[], options?: { input?: Buffer; allowFailure?: boolean; network?: boolean; config?: Record<string, string>; environment?: Record<string, string>; timeoutMs?: number }): Promise<{ code: number; stdout: Buffer; stderr: string }>
close(): Promise<void>
}
const activeControls = new Set<() => void>()
export function disposeGitControls(): void { for (const cancel of [...activeControls]) cancel() }
/** Fixed, verified Git in a parent-monitored native Job; never a model shell. */
export async function createGitControl(policy: SandboxEffectivePolicy, signal?: AbortSignal): Promise<GitControl> {
assertActive(signal)
const parent = join(sandboxConfiguration().stateRoot, 'git-control')
await fs.mkdir(parent, { recursive: true })
const root = await fs.mkdtemp(join(parent, 'operation-'))
await sealWorkspace(root)
const owner = { version: 1, root, pid: process.pid, nonce: randomUUID(), createdAt: Date.now(), state: 'active' }
await saveWorkspaceRecord(join(root, 'owner.json'), owner, false)
for (const name of ['workspace', 'repository', 'home', 'tmp']) await fs.mkdir(join(root, name))
try { await stageSandboxToolchains(root, ['git'], signal) }
catch (error) { await fs.rm(root, { recursive: true, force: true }).catch(() => undefined); throw error }
const binary = await verifiedBinary(true, true, true), tool = join(root, 'tools/git')
let running = false
return {
root, repository: join(root, 'repository'),
async run(args, options = {}) {
if (running) throw new Error('Git control operations must be sequential')
if (args.length > 128 || args.some(arg => typeof arg !== 'string' || arg.includes('\0'))) throw new Error('Invalid Git control argv')
assertActive(signal); await assertPolicyCurrent(policy)
const config: Record<string, string> = {
'core.hooksPath': join(tool, 'empty-hooks'), 'core.fsmonitor': 'false', 'credential.helper': '',
'commit.gpgSign': 'false', 'tag.gpgSign': 'false', 'maintenance.auto': 'false', 'gc.auto': '0',
'diff.autoRefreshIndex': 'false', 'core.attributesFile': 'NUL', 'core.excludesFile': 'NUL',
'protocol.allow': 'never', 'http.followRedirects': 'false', 'http.sslVerify': 'true',
...options.config
}
const win = process.env.SystemRoot ?? 'C:\\Windows'
const environment: Record<string, string> = {
SYSTEMROOT: win, WINDIR: win, SYSTEMDRIVE: win.slice(0, 2), COMSPEC: join(win, 'System32/cmd.exe'),
PATH: `${tool};${join(win, 'System32')}`, HOME: join(root, 'home'), USERPROFILE: join(root, 'home'),
TEMP: join(root, 'tmp'), TMP: join(root, 'tmp'), LC_ALL: 'C.UTF-8',
GIT_DIR: join(root, 'repository'), GIT_WORK_TREE: join(root, 'workspace'),
GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: 'NUL', GIT_CONFIG_SYSTEM: 'NUL',
GIT_TERMINAL_PROMPT: '0', GIT_OPTIONAL_LOCKS: '0', GIT_PAGER: '', GIT_EDITOR: '', GIT_SEQUENCE_EDITOR: '',
GIT_EXEC_PATH: tool, GIT_TEMPLATE_DIR: join(tool, 'empty-template'), GIT_NO_LAZY_FETCH: '1',
GIT_ALLOW_PROTOCOL: options.network ? 'https:http:ssh' : '',
GIT_CONFIG_COUNT: String(Object.keys(config).length), ...options.environment
}
Object.entries(config).forEach(([key, value], index) => { environment[`GIT_CONFIG_KEY_${index}`] = key; environment[`GIT_CONFIG_VALUE_${index}`] = value })
const fields = (values: string[]): string => values.map(value => Buffer.from(value).toString('hex')).join('\n') + '\n'
await fs.writeFile(join(root, 'arguments'), fields(args))
await fs.writeFile(join(root, 'environment'), fields(Object.entries(environment).map(([name, value]) => `${name}=${value}`)))
await fs.writeFile(join(root, 'input'), options.input ?? Buffer.alloc(0))
await fs.unlink(join(root, 'native-lease.json')).catch(error => { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error })
const timeout = options.timeoutMs ?? 120000
running = true
const child = spawn(binary, ['--git-control', root, String(timeout)], { windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] })
const chunks: Buffer[] = []; let length = 0, stderr = '', failure: Error | undefined, checking = false
const fail = (error: Error): void => { failure ??= error; child.kill() }
const revoke = (): void => fail(Object.assign(new Error('Git control cancelled or policy revoked'), { name: 'AbortError' }))
activeControls.add(revoke)
child.stdout.on('data', (bytes: Buffer) => { length += bytes.length; if (length > 32 * 1024 * 1024) fail(new Error('Git control output exceeds limit')); else chunks.push(bytes) })
child.stderr.on('data', (bytes: Buffer) => { stderr = (stderr + bytes.toString('utf8')).slice(-2 * 1024 * 1024) })
const abort = (): void => fail(Object.assign(new Error('Git operation cancelled'), { name: 'AbortError' }))
signal?.addEventListener('abort', abort, { once: true }); if (signal?.aborted) abort()
const timer = setTimeout(() => fail(new Error('Git control timeout')), timeout + 5000)
const watch = setInterval(() => {
if (checking) return
checking = true
void assertPolicyCurrent(policy).catch(error => fail(error as Error)).finally(() => { checking = false })
}, 250)
try {
const code = await new Promise<number>((done, reject) => { child.once('error', reject); child.once('close', code => done(code ?? 130)) })
if (failure) throw failure
if (code !== 0 && !options.allowFailure) throw new Error(`Git control failed (${code}): ${stderr.slice(-4096)}`)
return { code, stdout: Buffer.concat(chunks), stderr }
} finally { clearTimeout(timer); clearInterval(watch); signal?.removeEventListener('abort', abort); activeControls.delete(revoke); running = false }
},
async close() {
if (running) throw new Error('Cannot clean an active Git control Job')
await saveWorkspaceRecord(join(root, 'owner.json'), { ...owner, state: 'closed' }).catch(() => undefined)
await fs.rm(root, { recursive: true, force: true }).catch(async () => {
await saveWorkspaceRecord(join(root, 'owner.json'), { ...owner, state: 'cleanup-required' }).catch(() => undefined)
})
}
}
}
export async function cleanupGitControls(): Promise<Array<{ id: string; state: 'removed' | 'active' | 'invalid'; reason?: string }>> {
const parent = resolve(sandboxConfiguration().stateRoot, 'git-control'), entries = await fs.readdir(parent, { withFileTypes: true }).catch(error => { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return []; throw error })
if (entries.length > 10000) throw new Error('Too many Git control records')
const alive = (pid: number): boolean => { try { process.kill(pid, 0); return true } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ESRCH') return false; throw error } }
const result: Array<{ id: string; state: 'removed' | 'active' | 'invalid'; reason?: string }> = []
for (const entry of entries) {
if (!/^operation-[a-zA-Z0-9_-]{6,}$/.test(entry.name) || !entry.isDirectory() || entry.isSymbolicLink()) { result.push({ id: entry.name, state: 'invalid' }); continue }
const root = resolve(parent, entry.name)
try {
if ((await fs.realpath(dirname(root))).toLowerCase() !== (await fs.realpath(parent)).toLowerCase() || (await fs.realpath(root)).toLowerCase() !== root.toLowerCase()) throw new Error('Git cleanup root changed or escaped')
const owner = await workspaceRecord<{ version: number; root: string; pid: number; nonce: string; state: string }>(join(root, 'owner.json'), 4096)
if (owner.version !== 1 || owner.root.toLowerCase() !== root.toLowerCase() || !Number.isSafeInteger(owner.pid) || owner.pid < 1 || !/^[a-f0-9-]{36}$/.test(owner.nonce)) throw new Error('Invalid Git control owner')
if (owner.state === 'active' && alive(owner.pid)) { result.push({ id: entry.name, state: 'active' }); continue }
const lease = await workspaceRecord<{ version: number; root: string; pid: number }>(join(root, 'native-lease.json'), 4096).catch(error => { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return null; throw error })
if (lease && (lease.version !== 1 || lease.root.toLowerCase() !== root.toLowerCase() || !Number.isSafeInteger(lease.pid) || lease.pid < 1)) throw new Error('Invalid native Git control lease')
if (lease && alive(lease.pid)) { result.push({ id: entry.name, state: 'active' }); continue }
await fs.rm(root, { recursive: true, force: false }); result.push({ id: entry.name, state: 'removed' })
} catch (error) { result.push({ id: entry.name, state: 'invalid', reason: (error as Error).message }) }
}
return result
}