California gets roads, traffic, and a car to follow
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Lockfile license gate for every installed package, including workspaces.
|
||||
*
|
||||
* The allowed set is intentionally small and explicit. A new license is a
|
||||
* review event, not a string this script tries to interpret optimistically.
|
||||
* This does not replace third-party notices; it prevents a dependency update
|
||||
* from quietly introducing terms outside Tera's permissive distribution model.
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const lock = JSON.parse(readFileSync(resolve(root, "package-lock.json"), "utf8"));
|
||||
const allowed = new Set(["Apache-2.0", "BSD-3-Clause", "ISC", "MIT"]);
|
||||
const problems = [];
|
||||
const counts = new Map();
|
||||
|
||||
for (const [path, entry] of Object.entries(lock.packages ?? {})) {
|
||||
if (path === "") continue;
|
||||
|
||||
let license = entry.license;
|
||||
if (!license && entry.link === true && typeof entry.resolved === "string") {
|
||||
try {
|
||||
const workspace = JSON.parse(
|
||||
readFileSync(resolve(root, entry.resolved, "package.json"), "utf8"),
|
||||
);
|
||||
license = workspace.license;
|
||||
} catch (error) {
|
||||
problems.push(`${path}: could not read linked workspace license (${String(error)})`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof license !== "string" || license.length === 0) {
|
||||
problems.push(`${path}: no license recorded in the lockfile or linked workspace`);
|
||||
continue;
|
||||
}
|
||||
if (!allowed.has(license)) {
|
||||
problems.push(`${path}: ${license} is not in the reviewed allowlist`);
|
||||
continue;
|
||||
}
|
||||
counts.set(license, (counts.get(license) ?? 0) + 1);
|
||||
|
||||
const resolved = entry.resolved;
|
||||
if (typeof resolved === "string" && /^(?:git\+|github:|gitlab:|bitbucket:)/i.test(resolved)) {
|
||||
problems.push(`${path}: source dependency ${resolved} is not a registry artifact`);
|
||||
}
|
||||
}
|
||||
|
||||
if (problems.length > 0) {
|
||||
console.error("\ncheck-dependency-licenses: FAIL\n");
|
||||
for (const problem of problems) console.error(` ${problem}`);
|
||||
console.error(`\nReviewed allowlist: ${[...allowed].join(", ")}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const total = [...counts.values()].reduce((sum, count) => sum + count, 0);
|
||||
console.log(`check-dependency-licenses: ok — ${total} locked packages reviewed.`);
|
||||
for (const license of [...counts.keys()].sort()) {
|
||||
console.log(` ${license}: ${counts.get(license)}`);
|
||||
}
|
||||
Reference in New Issue
Block a user