From c7a04a10f907a2ee76f4af0a32a59c04e71ed19e Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Fri, 31 Jul 2026 17:13:50 +0800 Subject: [PATCH] fix(notices): derive first-party Python projects --- scripts/gen-third-party-notices.spec.ts | 14 ++++- scripts/gen-third-party-notices.ts | 68 +++++++++++++++++++------ 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/scripts/gen-third-party-notices.spec.ts b/scripts/gen-third-party-notices.spec.ts index 987bf9073b..f0cd55c09b 100644 --- a/scripts/gen-third-party-notices.spec.ts +++ b/scripts/gen-third-party-notices.spec.ts @@ -1,7 +1,7 @@ import { readdirSync, readFileSync } from 'node:fs' import { resolve } from 'node:path' import { describe, expect, it } from 'vitest' -import { isPermissive, type Manifest, manifestPatterns, parsePyprojectRequirements, parseVendoredRows, render, tierExternalDeps } from './gen-third-party-notices.ts' +import { collectPythonDependencies, isPermissive, type Manifest, manifestPatterns, parsePyprojectRequirements, parseVendoredRows, render, tierExternalDeps } from './gen-third-party-notices.ts' const root = resolve(import.meta.dirname, '..') @@ -157,6 +157,18 @@ describe('parsePyprojectRequirements', () => { }) }) +describe('collectPythonDependencies', () => { + it('excludes normalized local project names without exempting a third-party prefix', () => { + const pyprojects = [ + '[project]\nname = "deepseek-harness-runtime-bin"\ndependencies = ["pydantic"]\n', + '[project]\nname = "deepseek-harness"\ndependencies = ["DeepSeek.Harness_Runtime-Bin", "deepseek-unrelated"]\n', + ] + expect(() => collectPythonDependencies(pyprojects)).toThrow( + 'python dependency deepseek-unrelated is missing from PYTHON_METADATA', + ) + }) +}) + describe('isPermissive', () => { it('accepts the licenses this project ships and rejects copyleft or unknown ones', () => { expect(['MIT', 'ISC', 'BSD-3-Clause', 'Apache-2.0', 'MIT / Apache-2.0', '(MIT OR CC0-1.0)'].every(isPermissive)).toBe(true) diff --git a/scripts/gen-third-party-notices.ts b/scripts/gen-third-party-notices.ts index c0a3c55887..8e961377c7 100644 --- a/scripts/gen-third-party-notices.ts +++ b/scripts/gen-third-party-notices.ts @@ -75,6 +75,8 @@ const PYTHON_METADATA: Record ( + projectName === undefined ? [] : [normalizePythonDistributionName(projectName)] + ))) + const found = new Set(parsed + .flatMap(({ requirements }) => requirements.map(normalizePythonDistributionName)) + .filter(name => !firstParty.has(name))) + return [...found].sort((a, b) => a.localeCompare(b)).map((name) => { + const entry = metadata[name] + if (entry === undefined) throw new Error(`gen-third-party-notices: python dependency ${name} is missing from PYTHON_METADATA.`) + return { name, ...entry } + }) } /** Direct Python dependencies named by the `pyproject.toml` manifests under `python/`. */ function collectPython(): { name: string; license: string; repo: string; role: string }[] { - const found = new Set() const manifests = globSync('python/*/pyproject.toml', { cwd: root }) if (manifests.length === 0) throw new Error('gen-third-party-notices: no python/*/pyproject.toml found; the Python tree moved.') - for (const path of manifests) { - for (const name of parsePyprojectRequirements(readFileSync(resolve(root, path), 'utf8'))) { - if (name.startsWith('deepseek')) continue - found.add(name) - } - } - return [...found].sort((a, b) => a.localeCompare(b)).map((name) => { - const metadata = PYTHON_METADATA[name] - if (metadata === undefined) throw new Error(`gen-third-party-notices: python dependency ${name} is missing from PYTHON_METADATA.`) - return { name, ...metadata } - }) + return collectPythonDependencies(manifests.map(path => readFileSync(resolve(root, path), 'utf8'))) } /** pnpm-patched external packages, from `pnpm-workspace.yaml`. */