OSS-43Required
Ship the licence texts and notices of every dependency you distribute
Ship the licence text, the copyright lines and any NOTICE file of every open source dependency inside each thing you distribute. That covers app bundles, installers, container images, firmware and the JavaScript you send to browsers.
How to do it
Permissive licences allow almost any use, but only if the notice travels with the code. MIT asks you to include its copyright and permission notice in all copies. Apache-2.0 section 4 also asks you to pass on the NOTICE file. Shipping without them breaks the licence, and the right to use the code ends with it. Generate one third-party notices file in the build from the installed packages, never by hand. Show it where users can find it, such as an About screen, a /licenses page or a file in the image. Minifiers strip comments, so keep /*! licence comments or link the notices file from the bundle. Code you copy into your own tree follows Keep copied code under its own licence.
Examples
JavaScript example
// notices.mjs: one notices file from every installed package
import { readFileSync, readdirSync, existsSync, writeFileSync } from 'node:fs';
const lock = JSON.parse(readFileSync('package-lock.json', 'utf8'));
const sections = Object.keys(lock.packages).filter(path => path.startsWith('node_modules/')).map(path => {
const files = readdirSync(path).filter(name => /^(licen[cs]e|notice|copying)/i.test(name));
return [`## ${path.slice(13)}`, ...files.map(name => readFileSync(`${path}/${name}`, 'utf8'))].join('\n\n');
});
writeFileSync('dist/THIRD-PARTY-NOTICES.txt', sections.join('\n\n'));
if (!existsSync('dist/THIRD-PARTY-NOTICES.txt')) process.exit(1);
Python example
# notices.py: the same file for a Python application
from importlib.metadata import distributions
import pathlib, re
LICENCE_FILE = re.compile(r"(licen[cs]e|notice|copying)", re.I)
sections = []
for dist in sorted(distributions(), key=lambda d: d.metadata["Name"].lower()):
texts = [f.read_text() for f in (dist.files or []) if LICENCE_FILE.search(f.name)]
sections.append("\n\n".join([f"## {dist.metadata['Name']} {dist.version}", *texts]))
pathlib.Path("dist/THIRD-PARTY-NOTICES.txt").write_text("\n\n".join(sections))
How to check
The release job builds the notices file from the lock file. A test compares its package list with the packages in the release, and fails on any package missing from it. Every Apache-2.0 package that has a NOTICE file has that text in the notices file.
Sources
Related rules
- OSS-5 Keep copied code under its own licence in Open source maintenance
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/open-source/using-open-source/dependency-notices">Agent Wranglers open source maintenance playbook</a>. In code you write, add a comment next to it with the same link.