mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-24 15:49:06 +02:00
[proxy] Generate the web UI license terms at release time
A hand-maintained THIRD-PARTY-LICENSES drifts whenever the UI's dependencies change. Collect the terms from package-lock.json and the installed node_modules instead, failing when a shipped package is missing, mismatched or has no license file. Inter's OFL moves beside the font since it is not an npm package. The collection now assembles into a staging directory and renames it on success, so a failed run no longer leaves a partial output behind.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// Prints the third-party terms for the prebuilt UI in dist/ to stdout.
|
||||
// Package versions come from package-lock.json and the license texts from an
|
||||
// installed node_modules (run `npm ci --ignore-scripts` first).
|
||||
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const webDir = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
// Build tools whose own code is emitted into dist: tailwindcss generates the
|
||||
// preflight and utility CSS, vite injects its modulepreload polyfill.
|
||||
const bundledTooling = new Set(["node_modules/tailwindcss", "node_modules/vite"]);
|
||||
|
||||
// Inter ships as a font file rather than a package, so its OFL lives beside it.
|
||||
const staticTerms = [
|
||||
{
|
||||
title: "Inter 4.001 (git-66647c0bb), SIL Open Font License 1.1",
|
||||
file: "src/assets/fonts/OFL.txt",
|
||||
},
|
||||
];
|
||||
|
||||
const termPattern = /^(licen[cs]e|copying|notice|patents)/i;
|
||||
|
||||
function fail(message) {
|
||||
process.stderr.write(`${message}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function shippedPackages(lock) {
|
||||
return Object.entries(lock.packages)
|
||||
.filter(([path, meta]) => path !== "" && (!meta.dev || bundledTooling.has(path)))
|
||||
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
||||
}
|
||||
|
||||
function packageSection(path, meta) {
|
||||
const dir = join(webDir, path);
|
||||
const manifest = join(dir, "package.json");
|
||||
if (!existsSync(manifest)) {
|
||||
fail(`${path} is not installed; run npm ci --ignore-scripts in proxy/web`);
|
||||
}
|
||||
const installed = JSON.parse(readFileSync(manifest, "utf8"));
|
||||
if (installed.version !== meta.version) {
|
||||
fail(`${path} is ${installed.version}, package-lock.json pins ${meta.version}`);
|
||||
}
|
||||
|
||||
const terms = readdirSync(dir).filter((name) => termPattern.test(name)).sort();
|
||||
if (terms.length === 0) {
|
||||
fail(`no license terms found for ${path}`);
|
||||
}
|
||||
|
||||
const name = path.slice(path.lastIndexOf("node_modules/") + "node_modules/".length);
|
||||
return terms
|
||||
.map((term) => `=== ${name} ${meta.version} (${term}) ===\n\n${readFileSync(join(dir, term), "utf8")}`)
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
const lock = JSON.parse(readFileSync(join(webDir, "package-lock.json"), "utf8"));
|
||||
const sections = shippedPackages(lock).map(([path, meta]) => packageSection(path, meta));
|
||||
for (const { title, file } of staticTerms) {
|
||||
sections.push(`=== ${title} ===\n\n${readFileSync(join(webDir, file), "utf8")}`);
|
||||
}
|
||||
|
||||
process.stdout.write(
|
||||
"Third-party terms for the prebuilt authentication UI in proxy/web/dist.\n" +
|
||||
"Generated from proxy/web/package-lock.json at release time.\n\n\n" +
|
||||
sections.join("\n\n\n") +
|
||||
"\n",
|
||||
);
|
||||
Reference in New Issue
Block a user