import { readFile } from "node:fs/promises"; import path from "node:path"; import { repositoryRoot } from "./repository-files.mjs"; const manifestPath = path.join(repositoryRoot, "config", "dependency-licenses.json"); const catalogPath = path.join(repositoryRoot, "gradle", "libs.versions.toml"); const wrapperPath = path.join(repositoryRoot, "gradle", "wrapper", "gradle-wrapper.properties"); const documentationPath = path.join(repositoryRoot, "docs", "dependency-licenses.md"); const manifest = JSON.parse(await readFile(manifestPath, "utf8")); const catalog = await readFile(catalogPath, "utf8"); const wrapper = await readFile(wrapperPath, "utf8"); const documentation = await readFile(documentationPath, "utf8"); const failures = []; if (manifest.schemaVersion !== 1) failures.push("manifest schemaVersion must be 1"); if (!Array.isArray(manifest.components) || manifest.components.length === 0) { failures.push("manifest components must not be empty"); } const allowedLicenses = new Set(["Apache-2.0", "BSD-3-Clause", "EPL-1.0"]); const ids = new Set(); const coordinates = new Set(); for (const [index, component] of (manifest.components ?? []).entries()) { const prefix = `components[${index}]`; for (const field of ["id", "version", "scope", "license", "source"]) { if (typeof component[field] !== "string" || component[field].trim() === "") { failures.push(`${prefix}.${field} must be non-blank text`); } } if (ids.has(component.id)) failures.push(`${prefix}.id must be unique`); ids.add(component.id); if (component.coordinates !== null) { if (typeof component.coordinates !== "string" || component.coordinates.split(":").length !== 3) { failures.push(`${prefix}.coordinates must be group:name:version or null`); } else if (coordinates.has(component.coordinates)) { failures.push(`${prefix}.coordinates must be unique`); } coordinates.add(component.coordinates); } if (!allowedLicenses.has(component.license)) failures.push(`${prefix}.license is not reviewed`); try { const url = new URL(component.source); if (url.protocol !== "https:") throw new Error("not HTTPS"); } catch { failures.push(`${prefix}.source must be an absolute HTTPS URL`); } if (!documentation.includes(component.id) || !documentation.includes(component.version) || !documentation.includes(component.license)) { failures.push(`${prefix} is not represented in docs/dependency-licenses.md`); } } const version = (name) => new RegExp(`^${name}\\s*=\\s*"([^"]+)"`, "mu").exec(catalog)?.[1]; const expectedVersions = new Map([ ["gradle-wrapper", /gradle-([\d.]+)-bin\.zip/u.exec(wrapper)?.[1]], ["kotlin-gradle-plugin", version("kotlin")], ["kotlin-stdlib", version("kotlin")], ["junit4", version("junit")], ]); for (const [id, expectedVersion] of expectedVersions) { const component = manifest.components.find((candidate) => candidate.id === id); if (!component || component.version !== expectedVersion) { failures.push(`${id} license entry must match pinned version ${expectedVersion ?? ""}`); } } if (failures.length > 0) { console.error("Dependency-license verification failed:"); for (const failure of failures) console.error(`- ${failure}`); process.exitCode = 1; } else { console.log(`Dependency-license manifest passed (${manifest.components.length} reviewed components).`); }