Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ jobs:
run: yarn run lint:lit --quiet
- name: Run prettier
run: yarn run lint:prettier
- name: Check dependency licenses
run: yarn run lint:licenses
test:
name: Run tests
runs-on: ubuntu-latest
Expand Down
8 changes: 7 additions & 1 deletion build-scripts/gulp/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "./compress.js";
import "./entry-html.js";
import "./gather-static.js";
import "./gen-icons-json.js";
import "./licenses.js";
import "./locale-data.js";
import "./service-worker.js";
import "./translations.js";
Expand Down Expand Up @@ -36,7 +37,12 @@ gulp.task(
process.env.NODE_ENV = "production";
},
"clean",
gulp.parallel("gen-icons-json", "build-translations", "build-locale-data"),
gulp.parallel(
"gen-icons-json",
"build-translations",
"build-locale-data",
"gen-licenses"
),
"copy-static-app",
"rspack-prod-app",
gulp.parallel("gen-pages-app-prod", "gen-service-worker-app-prod"),
Expand Down
34 changes: 34 additions & 0 deletions build-scripts/gulp/licenses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Gulp task to generate third-party license notices.

import { generateLicenseFile } from "generate-license-file";
import gulp from "gulp";
import path from "path";
import paths from "../paths.cjs";

const OUTPUT_FILE = path.join(
paths.app_output_static,
"third-party-licenses.txt"
);

// The echarts package ships an Apache-2.0 NOTICE file that must be
// redistributed alongside the compiled output per Apache License §4(d).
const NOTICE_FILES = [
path.resolve(paths.root_dir, "node_modules/echarts/NOTICE"),
];

// type-fest ships two license files (MIT for code, CC0 for types).
// We use the MIT license since that covers the bundled code.
const LICENSE_OVERRIDES = {
"type-fest@5.4.4": path.resolve(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing to consider is that this won't get updated by dependabot or renovate bot.

paths.root_dir,
"node_modules/type-fest/license-mit"
),
};

gulp.task("gen-licenses", async () => {
await generateLicenseFile(
path.resolve(paths.root_dir, "package.json"),
OUTPUT_FILE,
{ append: NOTICE_FILES, replace: LICENSE_OVERRIDES }
);
});
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"format:prettier": "prettier . --cache --write",
"lint:types": "tsc",
"lint:lit": "lit-analyzer \"{.,*}/src/**/*.ts\"",
"lint:licenses": "node --no-deprecation script/check-licenses",
"lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types && yarn run lint:lit",
"format": "yarn run format:eslint && yarn run format:prettier",
"postinstall": "husky",
Expand Down Expand Up @@ -162,6 +163,7 @@
"@types/leaflet": "1.9.21",
"@types/leaflet-draw": "1.0.13",
"@types/leaflet.markercluster": "1.5.6",
"@types/license-checker": "^25",
"@types/lodash.merge": "4.6.9",
"@types/luxon": "3.7.1",
"@types/mocha": "10.0.10",
Expand All @@ -185,6 +187,7 @@
"eslint-plugin-wc": "3.1.0",
"fancy-log": "2.0.0",
"fs-extra": "11.3.4",
"generate-license-file": "4.1.1",
"glob": "13.0.6",
"gulp": "5.0.1",
"gulp-brotli": "3.0.0",
Expand All @@ -194,6 +197,7 @@
"husky": "9.1.7",
"jsdom": "29.0.1",
"jszip": "3.10.1",
"license-checker": "25.0.1",
"lint-staged": "16.4.0",
"lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2",
Expand Down
97 changes: 97 additions & 0 deletions script/check-licenses
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env node
// Checks that all production dependencies use approved open-source licenses.
//
// To allow a new license type, add its SPDX identifier to ALLOWED_LICENSES.
// To allow a specific package that cannot be relicensed (e.g. a dual-license
// package where the reported identifier is non-standard), add it to
// ALLOWED_PACKAGES with a comment explaining why.

import checker from "license-checker";
import { createRequire } from "module";
import { fileURLToPath } from "url";
import path from "path";

const require = createRequire(import.meta.url);
const root = path.resolve(fileURLToPath(import.meta.url), "../../");

// Permissive licenses that are compatible with distribution in a compiled wheel.
// Copyleft licenses (GPL, LGPL, AGPL, EUPL, etc.) must NOT be added here.
const ALLOWED_LICENSES = new Set([
"MIT",
"MIT*",
"ISC",
"BSD-2-Clause",
"BSD-3-Clause",
"BSD*",
"Apache-2.0",
"0BSD",
"CC0-1.0",
"(MIT OR CC0-1.0)",
"(MIT AND Zlib)",
"Python-2.0", // argparse - Python Software Foundation License (permissive)
"Public Domain",
"W3C-20150513", // wicg-inert - W3C Software and Document License (permissive)
"Unlicense",
"CC-BY-4.0",
]);

// Packages whose license identifier is ambiguous or non-standard but have been
// manually verified as permissive. Add only when strictly necessary.
const ALLOWED_PACKAGES = {
// No entries currently needed.
};

checker.init(
{
start: root,
production: true,
excludePrivatePackages: true,
},
(err, packages) => {
if (err) {
console.error("license-checker failed:", err);
process.exit(1);
}

const violations = [];

for (const [nameAtVersion, info] of Object.entries(packages)) {
if (nameAtVersion in ALLOWED_PACKAGES) {
continue;
}

const license = info.licenses;

if (!ALLOWED_LICENSES.has(license)) {
violations.push({ package: nameAtVersion, license });
}
}

if (violations.length > 0) {
console.error(
"The following packages have licenses that are not on the allowlist:\n"
);
for (const { package: pkg, license } of violations) {
console.error(` ${pkg}: ${license}`);
}
console.error(
"\nIf the license is permissive and appropriate for distribution, add it"
);
console.error(
"to ALLOWED_LICENSES in script/check-licenses. If it is a specific"
);
console.error(
"package with an ambiguous identifier, add it to ALLOWED_PACKAGES."
);
console.error(
"\nDo NOT add copyleft licenses (GPL, LGPL, AGPL, etc.) to the allowlist."
);
process.exit(1);
}

const count = Object.keys(packages).length;
console.log(
`License check passed: all ${count} production dependencies use approved licenses.`
);
}
);
Loading
Loading