mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-11-10 04:27:42 +08:00
Merge remote-tracking branch 'origin/master' into worktree/code-diff-card
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
|
||||
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
@@ -78,7 +78,7 @@ describe('browser dependency discovery', () => {
|
||||
await expect(browserBundledExternals(root)).rejects.toThrow('has no browser build config')
|
||||
})
|
||||
|
||||
it('follows shell workspace aliases, CSS assets and lazy imports without writing output', async () => {
|
||||
it.each([false, true])('follows shell aliases, CSS and lazy imports without writing output (symlinked root: %s)', async (linked) => {
|
||||
const root = fixture()
|
||||
library(root, 'shell-lib')
|
||||
library(root, 'lazy-lib')
|
||||
@@ -104,7 +104,13 @@ describe('browser dependency discovery', () => {
|
||||
}`)
|
||||
write(root, 'apps/web/dist/sentinel.txt', 'untouched')
|
||||
|
||||
expect(await browserBundledExternals(root)).toEqual(new Set(['shell-lib', 'lazy-lib', 'asset-lib']))
|
||||
const scanRoot = linked ? join(fixture(), 'linked') : root
|
||||
if (linked) symlinkSync(root, scanRoot, 'junction')
|
||||
try {
|
||||
expect(await browserBundledExternals(scanRoot)).toEqual(new Set(['shell-lib', 'lazy-lib', 'asset-lib']))
|
||||
} finally {
|
||||
if (linked) unlinkSync(scanRoot)
|
||||
}
|
||||
expect(readFileSync(join(app, 'dist/sentinel.txt'), 'utf8')).toBe('untouched')
|
||||
expect(existsSync(join(app, 'dist/index.html'))).toBe(false)
|
||||
expect(existsSync(join(root, 'packages/client/static/lib'))).toBe(false)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Resolve direct third-party browser inputs through the shipping build configurations, without emitting files. */
|
||||
|
||||
import { globSync, readFileSync } from 'node:fs'
|
||||
import { globSync, readFileSync, realpathSync } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
@@ -159,10 +159,12 @@ async function collectShell(
|
||||
|
||||
/**
|
||||
* Direct third-party packages resolved by published browser builds.
|
||||
* @param root - Repository root with installed build dependencies; lib/ is not required.
|
||||
* @param root - Repository root, possibly symlinked, with installed build dependencies; lib/ is not required.
|
||||
* @returns Names of distributed browser inputs, excluding workspace packages and erased types.
|
||||
*/
|
||||
export async function browserBundledExternals(root: string): Promise<Set<string>> {
|
||||
// Vite resolves HTML inputs to real paths, so its root must use the same spelling.
|
||||
root = realpathSync(root)
|
||||
const manifests = new Map<string, Manifest>()
|
||||
for (const glob of ['packages/*/*/package.json', 'vendor/*/package.json']) {
|
||||
for (const path of globSync(glob, { cwd: root }).sort()) {
|
||||
|
||||
@@ -258,7 +258,8 @@ describe('CI workflow', () => {
|
||||
isRecord(step) && step.name === 'Configure persistent pnpm store' && typeof step.run === 'string'
|
||||
))
|
||||
expect(serialStore).toBeDefined()
|
||||
expect(serialStore!.run).toContain('F:\\.pnpm-store')
|
||||
expect(serialStore!.run).toContain('[IO.Path]::GetPathRoot($env:GITHUB_WORKSPACE)')
|
||||
expect(serialStore!.run).toContain("'.pnpm-store'")
|
||||
const serialInstall = serialSteps.find((step): step is Record<string, unknown> & { run: string } => (
|
||||
isRecord(step) && step.name === 'Install (immutable)' && typeof step.run === 'string'
|
||||
))
|
||||
|
||||
@@ -378,7 +378,8 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
|
||||
SaveFileStreamAttachment: 'attachment.md',
|
||||
ImageAttachmentAccess: 'llm-streaming.md',
|
||||
ImageAttachmentRef: 'attachment.md',
|
||||
ImageRequestPolicy: 'attachment.md',
|
||||
ImageRequestTarget: 'attachment.md',
|
||||
ProjectedDimensions: 'attachment.md',
|
||||
PromptContentPart: 'attachment.md',
|
||||
RequestImageAttachment: 'attachment.md',
|
||||
SaveImageAttachment: 'attachment.md',
|
||||
|
||||
@@ -222,15 +222,8 @@ const EXACT_EDITS: readonly ExactEdit[] = [
|
||||
{
|
||||
id: 'vendor-readme-preamble',
|
||||
file: 'vendor/README.md',
|
||||
find: 'All vendored packages keep their **original npm names** and are marked `private: true` — they are never published from this repo. `pnpm-workspace.yaml#linkWorkspacePackages` makes matching upstream semver ranges resolve these pinned workspaces, including imports from built `lib/`; disabling it substitutes npm copies behind the same names.',
|
||||
replace: 'All vendored packages are **renamed into the `@deepseek-ai` scope** (`cordis` → `@deepseek-ai/cordis`, `@cordisjs/plugin-<x>` → `@deepseek-ai/cordis-plugin-<x>`): every harness package declares `cordis` as a peer dependency, so publishing the harness publishes this framework layer too, and a publication under the upstream names would squat them on the registry. Directory names and upstream version numbers are deliberately unchanged, so the manifest below still reads as an upstream snapshot. `pnpm-workspace.yaml#linkWorkspacePackages` makes those preserved semver ranges resolve these pinned workspaces, including imports from built `lib/`.',
|
||||
expect: 1,
|
||||
},
|
||||
{
|
||||
id: 'vendor-readme-schemastery-note',
|
||||
file: 'vendor/README.md',
|
||||
find: 'whose lazy `require(\'cosmokit\')` can race',
|
||||
replace: 'whose lazy `require(\'@deepseek-ai/cosmokit\')` can race',
|
||||
find: 'All vendored packages keep their **original npm names** (they are resolved through pnpm workspaces) and are marked `private: true` — they are never published from this repo.',
|
||||
replace: 'All vendored packages use the **`@deepseek-ai` scope** (`cordis` → `@deepseek-ai/cordis`, `@cordisjs/plugin-<x>` → `@deepseek-ai/cordis-plugin-<x>`). The manifest table records upstream versions and source commits; each package manifest carries its Harness release version and publication metadata. Repository-owned runtime dependencies use `workspace:^`, so local builds resolve the pinned workspace packages and publication substitutes release ranges.',
|
||||
expect: 1,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs'
|
||||
import { describe, expect, it, vi, type MockInstance } from 'vitest'
|
||||
import {
|
||||
cliGateOptions,
|
||||
collectDescendants,
|
||||
defaultConcurrency,
|
||||
formatGateResultReason,
|
||||
gatesForMode,
|
||||
@@ -213,7 +214,7 @@ describe('gate graph validation', () => {
|
||||
'rescope-vendor', 'publint', 'constraints', 'package-dependencies', 'application-entrypoints',
|
||||
'dsh-package-licenses', 'package-invariants', 'built-package-invariants', 'node-next-types',
|
||||
'optional-dependency-imports', 'client-packages', 'client-ui-i18n', 'no-bare-dispatcher', 'cordis-config',
|
||||
'runtime-closure', 'vendored-links',
|
||||
'runtime-closure',
|
||||
])
|
||||
expect(defaultConcurrency('hygiene', ids.length, 8)).toEqual({
|
||||
workers: 4,
|
||||
@@ -917,6 +918,30 @@ describe('fail-fast scheduling', () => {
|
||||
})
|
||||
|
||||
describe('process-table parsing', () => {
|
||||
it('excludes the root when a parent link returns to it', () => {
|
||||
expect(collectDescendants(100, [[200, 100], [100, 200], [300, 200]]))
|
||||
.toEqual([200, 300])
|
||||
})
|
||||
|
||||
it('visits duplicate and cyclic descendant links only once', () => {
|
||||
expect(collectDescendants(100, [
|
||||
[200, 100], [200, 100], [300, 100], [200, 200], [400, 200], [200, 400], [500, 300], [900, 800],
|
||||
])).toEqual([200, 300, 400, 500])
|
||||
})
|
||||
|
||||
it('returns no descendants for an isolated or self-parented root', () => {
|
||||
expect(collectDescendants(100, [])).toEqual([])
|
||||
expect(collectDescendants(100, [[100, 100]])).toEqual([])
|
||||
})
|
||||
|
||||
it('walks a wide child set without spreading it into call arguments', () => {
|
||||
const children = Array.from({ length: 150_000 }, (_, i): [number, number] => [i + 3, 2])
|
||||
const descendants = collectDescendants(1, [[2, 1], ...children])
|
||||
expect(descendants).toHaveLength(children.length + 1)
|
||||
expect(descendants[0]).toBe(2)
|
||||
expect(descendants.at(-1)).toBe(150_002)
|
||||
})
|
||||
|
||||
it('parses `pid ppid` rows from a POSIX ps dump', () => {
|
||||
expect(parsePidPpidLines(' 123 1\n456 123\n 789 456\n')).toEqual([[123, 1], [456, 123], [789, 456]])
|
||||
})
|
||||
|
||||
+16
-11
@@ -286,7 +286,6 @@ export function gatesForMode(selected: Mode): Gate[] {
|
||||
...hygieneLeafGates(),
|
||||
pnpmScript('cordis-config', 'verify-cordis-config', { label: 'Cordis config' }),
|
||||
pnpmScript('runtime-closure', 'verify-runtime-closure', { label: 'runtime closure' }),
|
||||
pnpmScript('vendored-links', 'verify-vendored-links', { label: 'vendored links' }),
|
||||
]
|
||||
case 'doc-sync':
|
||||
return docSyncLeafGates()
|
||||
@@ -1506,23 +1505,29 @@ export function taskkillArgs(rootPid: number, descendants: number[]): string[][]
|
||||
return [rootPid, ...descendants].map(pid => ['/PID', String(pid), '/T', '/F'])
|
||||
}
|
||||
|
||||
/** Breadth-first walk of the pid/ppid rows starting at `root`. */
|
||||
function collectDescendants(root: number, rows: Array<[number, number]>): number[] {
|
||||
/**
|
||||
* Walk a process-table snapshot without revisiting duplicate or cyclic PID links.
|
||||
* @param root - process whose descendants are collected; excluded from the result.
|
||||
* @param rows - observed PID and parent PID pairs.
|
||||
* @returns distinct reachable descendants in breadth-first order.
|
||||
*/
|
||||
export function collectDescendants(root: number, rows: Array<[number, number]>): number[] {
|
||||
const byParent = new Map<number, number[]>()
|
||||
for (const [pid, ppid] of rows) {
|
||||
const children = byParent.get(ppid) ?? []
|
||||
children.push(pid)
|
||||
byParent.set(ppid, children)
|
||||
}
|
||||
const result: number[] = []
|
||||
const queue = byParent.get(root) ?? []
|
||||
for (let index = 0; index < queue.length; index += 1) {
|
||||
const pid = queue[index]
|
||||
if (pid === undefined) continue
|
||||
result.push(pid)
|
||||
queue.push(...(byParent.get(pid) ?? []))
|
||||
const seen = new Set([root])
|
||||
const queue = [root]
|
||||
for (const parent of queue) {
|
||||
for (const pid of byParent.get(parent) ?? []) {
|
||||
if (seen.has(pid)) continue
|
||||
seen.add(pid)
|
||||
queue.push(pid)
|
||||
}
|
||||
}
|
||||
return result
|
||||
return queue.slice(1)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1003,9 +1003,14 @@
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/attachment.md",
|
||||
"symbol": "ImageRequestPolicy",
|
||||
"symbol": "ImageRequestTarget",
|
||||
"source": "packages/attachment/attachment/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/attachment.md",
|
||||
"symbol": "ProjectedDimensions",
|
||||
"source": "packages/attachment/attachment/src/request-projection.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/attachment.md",
|
||||
"symbol": "RequestImageAttachment",
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* Verify that pnpm-lock.yaml resolves every vendored package name to its
|
||||
* workspace `link:` — never a registry copy. `linkWorkspacePackages: true`
|
||||
* (pnpm-workspace.yaml) makes matching upstream semver ranges resolve to the
|
||||
* pinned vendored sources; a registry copy of the same name coexisting with
|
||||
* the vendored one silently forks the framework layer (vendor/README.md).
|
||||
*/
|
||||
import { readdir, readFile } from 'node:fs/promises'
|
||||
import { join, resolve } from 'node:path'
|
||||
import * as yaml from 'js-yaml'
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
|
||||
async function vendoredNames(): Promise<Set<string>> {
|
||||
const names = new Set<string>()
|
||||
for (const entry of await readdir(join(root, 'vendor'), { withFileTypes: true })) {
|
||||
if (!entry.isDirectory()) continue
|
||||
let manifest: { name?: string }
|
||||
try {
|
||||
manifest = JSON.parse(await readFile(join(root, 'vendor', entry.name, 'package.json'), 'utf8')) as { name?: string }
|
||||
} catch {
|
||||
continue // not a package directory (e.g. vendor/README.md siblings)
|
||||
}
|
||||
if (manifest.name !== undefined) names.add(manifest.name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
interface Lockfile {
|
||||
importers?: Record<string, Record<string, unknown>>
|
||||
packages?: Record<string, unknown>
|
||||
snapshots?: Record<string, unknown>
|
||||
}
|
||||
|
||||
const names = await vendoredNames()
|
||||
if (names.size === 0) throw new Error('verify-vendored-links: no vendored package manifests found under vendor/')
|
||||
const lockfile = yaml.load(await readFile(join(root, 'pnpm-lock.yaml'), 'utf8')) as Lockfile
|
||||
|
||||
const violations: string[] = []
|
||||
|
||||
// Importer resolutions: every dependency entry naming a vendored package must
|
||||
// resolve to a link:, or the build silently uses a registry copy.
|
||||
for (const [importer, sections] of Object.entries(lockfile.importers ?? {})) {
|
||||
for (const [section, dependencies] of Object.entries(sections)) {
|
||||
if (typeof dependencies !== 'object' || dependencies === null) continue
|
||||
for (const [dependency, entry] of Object.entries(dependencies as Record<string, { version?: string }>)) {
|
||||
if (!names.has(dependency)) continue
|
||||
const version = entry.version ?? ''
|
||||
if (!version.startsWith('link:')) {
|
||||
violations.push(`${importer} ${section}.${dependency} resolves to ${JSON.stringify(version)} (expected link:)`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Package/snapshot keys: a registry copy materializes as a `<name>@<version>`
|
||||
// key; vendored names must never appear there at all.
|
||||
for (const section of ['packages', 'snapshots'] as const) {
|
||||
for (const key of Object.keys(lockfile[section] ?? {})) {
|
||||
const atIndex = key.lastIndexOf('@')
|
||||
if (atIndex <= 0) continue
|
||||
const packageName = key.slice(0, atIndex)
|
||||
if (names.has(packageName)) violations.push(`${section} entry ${key} is a registry copy of a vendored package`)
|
||||
}
|
||||
}
|
||||
|
||||
if (violations.length > 0) {
|
||||
console.error(`verify-vendored-links: ${String(violations.length)} lockfile resolution(s) bypass the vendored workspaces:`)
|
||||
for (const violation of violations) console.error(` - ${violation}`)
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(`verify-vendored-links: all ${String(names.size)} vendored package names resolve to workspace links.`)
|
||||
Reference in New Issue
Block a user