verify-dsh-package-licenses.ts 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. /**
  2. * Enforce the MIT license declaration for repository-owned DSH npm packages.
  3. * @module scripts/verify-dsh-package-licenses
  4. */
  5. import { globSync, readFileSync } from 'node:fs'
  6. import { resolve, sep } from 'node:path'
  7. const ROOT = resolve(import.meta.dirname, '..')
  8. const DSH_PACKAGE_NAME = /^@deepseek-ai\/dsh(?:-|$)/
  9. /** Result of checking every DSH package reachable through the root workspace list. */
  10. export interface DshPackageLicenseReport {
  11. /** Number of DSH package manifests checked. */
  12. packageCount: number
  13. /** Repository-relative diagnostics for non-MIT declarations. */
  14. failures: string[]
  15. }
  16. function readManifest(root: string, file: string): Record<string, unknown> {
  17. const parsed: unknown = JSON.parse(readFileSync(resolve(root, file), 'utf8'))
  18. if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
  19. throw new Error(`verify-dsh-package-licenses: ${file} must contain a JSON object.`)
  20. }
  21. return parsed as Record<string, unknown>
  22. }
  23. function isStringArray(value: unknown): value is string[] {
  24. return Array.isArray(value) && value.every((entry: unknown) => typeof entry === 'string')
  25. }
  26. function workspaceManifestPaths(root: string): string[] {
  27. const rootManifest = readManifest(root, 'package.json')
  28. const workspaces = rootManifest.workspaces
  29. if (!isStringArray(workspaces)) {
  30. throw new Error('verify-dsh-package-licenses: package.json workspaces must be a string array.')
  31. }
  32. const files = new Set(['package.json'])
  33. for (const pattern of workspaces) {
  34. for (const file of globSync(`${pattern}/package.json`, { cwd: root })) {
  35. files.add(file)
  36. }
  37. }
  38. return [...files].sort()
  39. }
  40. function printable(value: unknown): string {
  41. return value === undefined ? 'undefined' : JSON.stringify(value)
  42. }
  43. /**
  44. * Check every DSH npm package declared by the repository workspace.
  45. * @param root - absolute repository root containing the workspace package.json.
  46. * @returns the checked package count and every non-MIT declaration.
  47. */
  48. export function inspectDshPackageLicenses(root: string): DshPackageLicenseReport {
  49. let packageCount = 0
  50. const failures: string[] = []
  51. for (const file of workspaceManifestPaths(root)) {
  52. const manifest = readManifest(root, file)
  53. const name = manifest.name
  54. if (typeof name !== 'string' || !DSH_PACKAGE_NAME.test(name)) continue
  55. packageCount++
  56. if (manifest.license !== 'MIT') {
  57. const normalizedFile = file.split(sep).join('/')
  58. failures.push(
  59. `${normalizedFile}: ${name} must declare "license": "MIT"; found ${printable(manifest.license)}.`,
  60. )
  61. }
  62. }
  63. return { packageCount, failures }
  64. }
  65. if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
  66. const report = inspectDshPackageLicenses(ROOT)
  67. if (report.failures.length > 0) {
  68. process.stderr.write('verify-dsh-package-licenses: non-MIT DSH package declarations found:\n')
  69. for (const failure of report.failures) process.stderr.write(` ${failure}\n`)
  70. process.exitCode = 1
  71. } else {
  72. process.stdout.write(
  73. `verify-dsh-package-licenses: ${String(report.packageCount)} DSH package(s) checked; all declare MIT.\n`,
  74. )
  75. }
  76. }