Files
workdsh/scripts/sync-vendored-runtime.mjs
T

215 lines
9.6 KiB
JavaScript

import { createHash } from 'node:crypto'
import {
copyFileSync,
existsSync,
mkdirSync,
readFileSync,
readdirSync,
statSync,
unlinkSync,
writeFileSync,
} from 'node:fs'
import { basename, join, relative, resolve } from 'node:path'
const root = resolve(import.meta.dirname, '..')
const upstreamPath = join(root, 'upstream.json')
const workspacePath = join(root, 'package.json')
const mode = process.argv[2]
const channelFlag = process.argv.indexOf('--channel')
const requestedChannel = channelFlag === -1 ? undefined : process.argv[channelFlag + 1]
if (mode !== '--write' && mode !== '--check') {
throw new Error('usage: node scripts/sync-vendored-runtime.mjs <--write|--check> [--channel stable|beta]')
}
const readJson = path => JSON.parse(readFileSync(path, 'utf8'))
const writeJson = (path, value) => writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`)
const sha256 = path => createHash('sha256').update(readFileSync(path)).digest('hex')
const isDshPackage = name => name === '@deepseek-ai/dsh' || name.startsWith('@deepseek-ai/dsh-')
const isDshResolution = selector => selector === '@deepseek-ai/dsh'
|| selector.startsWith('@deepseek-ai/dsh@')
|| selector.startsWith('@deepseek-ai/dsh-')
const fail = message => { throw new Error(`sync-vendored-runtime: ${message}`) }
const upstreamDocument = readJson(upstreamPath)
const channel = requestedChannel ?? upstreamDocument.activeChannel
if (channel !== 'stable' && channel !== 'beta') fail(`unknown release channel ${JSON.stringify(channel)}`)
const upstream = upstreamDocument.channels?.[channel]
if (upstream === undefined || typeof upstream !== 'object') fail(`missing upstream metadata for ${channel}`)
const otherChannel = channel === 'stable' ? 'beta' : 'stable'
const otherVersion = upstreamDocument.channels?.[otherChannel]?.sourceVersion
if (typeof otherVersion !== 'string' || !/^[0-9A-Za-z][0-9A-Za-z.-]*$/u.test(otherVersion)) {
fail(`unsafe ${otherChannel} source version ${JSON.stringify(otherVersion)}`)
}
const pluginPaths = [
join(root, upstream.package, 'package.json'),
...(channel === 'beta' ? [join(root, 'dsh-community-market', 'package.json')] : []),
]
const version = upstream.sourceVersion
if (typeof version !== 'string' || !/^[0-9A-Za-z][0-9A-Za-z.-]*$/u.test(version)) {
fail(`unsafe source version ${JSON.stringify(version)}`)
}
const vendorRelative = `vendor/dsh-runtime/${version}`
const vendorDirectory = join(root, ...vendorRelative.split('/'))
const manifestPath = join(vendorDirectory, 'manifest.json')
const resolutionSelector = (name, range = version) => `${name}@npm:${range}`
const isChannelResolution = selector => selector.endsWith(`@npm:${version}`)
|| selector.endsWith(`@npm:^${version}`)
const isOtherChannelResolution = selector => selector.endsWith(`@npm:${otherVersion}`)
|| selector.endsWith(`@npm:^${otherVersion}`)
function packageName(filename) {
const suffix = `-${version}.tgz`
if (!filename.startsWith('deepseek-ai-') || !filename.endsWith(suffix)) {
fail(`unexpected upstream tarball name ${JSON.stringify(filename)}`)
}
const unscoped = filename.slice('deepseek-ai-'.length, -suffix.length)
if (unscoped.length === 0) fail(`empty package name in ${JSON.stringify(filename)}`)
return `@deepseek-ai/${unscoped}`
}
function expectedResolution(entry) {
const source = `file:${vendorRelative}/${entry.filename}`
const unscoped = entry.name.slice('@deepseek-ai/'.length)
const patchRelative = `patches/${unscoped}@${version}.patch`
return existsSync(join(root, patchRelative))
? `patch:${entry.name}@${source.replace(':', '%3A')}#./${patchRelative}`
: source
}
function writeVendor() {
const packedDirectory = join(root, 'deepseek-harness', 'dist', 'npm')
const orderPath = join(packedDirectory, 'publish-order.txt')
if (!existsSync(orderPath)) {
fail('upstream release tarballs are missing; run yarn upstream:prepare-runtime first')
}
const filenames = readFileSync(orderPath, 'utf8').trim().split(/\r?\n/u).filter(Boolean)
if (filenames.length === 0 || new Set(filenames).size !== filenames.length) {
fail('upstream publish order is empty or contains duplicate tarballs')
}
mkdirSync(vendorDirectory, { recursive: true })
const packages = filenames.map((filename) => {
if (basename(filename) !== filename) fail(`tarball path escapes its directory: ${JSON.stringify(filename)}`)
const source = join(packedDirectory, filename)
if (!existsSync(source) || !statSync(source).isFile()) fail(`missing packed tarball ${filename}`)
const target = join(vendorDirectory, filename)
copyFileSync(source, target)
return {
name: packageName(filename),
version,
filename,
size: statSync(source).size,
sha256: sha256(source),
}
})
const names = packages.map(entry => entry.name)
if (new Set(names).size !== names.length) fail('two tarballs map to the same package name')
const expectedFiles = new Set([...filenames, 'manifest.json'])
for (const entry of readdirSync(vendorDirectory, { withFileTypes: true })) {
if (expectedFiles.has(entry.name)) continue
if (!entry.isFile()) fail(`unexpected directory in vendored runtime: ${entry.name}`)
unlinkSync(join(vendorDirectory, entry.name))
}
const manifest = {
formatVersion: 1,
repository: upstreamDocument.repository,
commit: upstream.commit,
version,
buildProfile: 'official',
packages,
}
writeJson(manifestPath, manifest)
upstream.runtimePackageVersion = version
upstream.runtimeSource = `${vendorRelative}/manifest.json`
writeJson(upstreamPath, upstreamDocument)
const workspace = readJson(workspacePath)
const resolutions = Object.fromEntries(Object.entries(workspace.resolutions ?? {})
.filter(([selector]) => !isDshResolution(selector) || isOtherChannelResolution(selector)))
for (const entry of packages) {
resolutions[resolutionSelector(entry.name)] = expectedResolution(entry)
resolutions[resolutionSelector(entry.name, `^${version}`)] = expectedResolution(entry)
}
workspace.resolutions = resolutions
writeJson(workspacePath, workspace)
const packageNames = new Set(packages.map(entry => entry.name))
for (const path of pluginPaths) {
const plugin = readJson(path)
for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {
for (const name of Object.keys(plugin[field] ?? {})) {
if (isDshPackage(name) && packageNames.has(name)) plugin[field][name] = version
}
}
writeJson(path, plugin)
}
}
function checkVendor() {
if (!existsSync(manifestPath)) fail(`missing ${relative(root, manifestPath)}`)
const manifest = readJson(manifestPath)
if (manifest.formatVersion !== 1) fail('unsupported vendored runtime manifest format')
for (const field of ['repository', 'commit']) {
const expected = field === 'repository' ? upstreamDocument.repository : upstream[field]
if (manifest[field] !== expected) fail(`manifest ${field} differs from upstream.json ${channel} channel`)
}
if (manifest.version !== version || upstream.runtimePackageVersion !== version) {
fail('source, runtime, and manifest versions must match')
}
if (manifest.buildProfile !== 'official') fail('vendored runtime must use the official client build profile')
if (upstream.runtimeSource !== `${vendorRelative}/manifest.json`) {
fail('upstream.json points at the wrong runtime manifest')
}
if (!Array.isArray(manifest.packages) || manifest.packages.length === 0) fail('runtime manifest has no packages')
const workspace = readJson(workspacePath)
const resolutions = workspace.resolutions ?? {}
const expectedFiles = new Set(['manifest.json'])
const names = new Set()
for (const entry of manifest.packages) {
if (entry.version !== version || packageName(entry.filename) !== entry.name || names.has(entry.name)) {
fail(`invalid manifest package ${JSON.stringify(entry.name)}`)
}
names.add(entry.name)
expectedFiles.add(entry.filename)
const path = join(vendorDirectory, entry.filename)
if (!existsSync(path) || !statSync(path).isFile()) fail(`missing vendored tarball ${entry.filename}`)
if (statSync(path).size !== entry.size || sha256(path) !== entry.sha256) {
fail(`vendored tarball integrity differs for ${entry.filename}`)
}
if (resolutions[resolutionSelector(entry.name)] !== expectedResolution(entry)
|| resolutions[resolutionSelector(entry.name, `^${version}`)] !== expectedResolution(entry)) {
fail(`workspace resolution differs for ${entry.name}`)
}
}
for (const selector of Object.keys(resolutions).filter(isChannelResolution)) {
const name = selector.slice(0, selector.indexOf('@npm:'))
if (!names.has(name)) fail(`workspace has a stale ${channel} DSH resolution for ${selector}`)
}
for (const entry of readdirSync(vendorDirectory, { withFileTypes: true })) {
if (!entry.isFile() || !expectedFiles.has(entry.name)) fail(`unexpected vendored runtime entry ${entry.name}`)
}
for (const path of pluginPaths) {
const plugin = readJson(path)
for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) {
for (const [name, range] of Object.entries(plugin[field] ?? {})) {
if (!isDshPackage(name)) continue
if (!names.has(name)) fail(`${relative(root, path)} references absent runtime package ${name}`)
if (range !== version) fail(`${relative(root, path)} ${field}.${name} must use ${version}`)
}
}
}
process.stdout.write(
`sync-vendored-runtime: ${channel} ${String(manifest.packages.length)} packages from ${manifest.commit.slice(0, 10)} are verified\n`,
)
}
if (mode === '--write') writeVendor()
checkVendor()