validate-licenses.ts (5514B)
1 // The MIT License 2 3 // Copyright (c) 2010-2022 Google LLC. http://angular.io/license 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 // this software and associated documentation files (the "Software"), to deal in 7 // the Software without restriction, including without limitation the rights to 8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 // the Software, and to permit persons to whom the Software is furnished to do so, 10 // subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in all 13 // copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 // Taken and adapted from https://github.com/angular/angular-cli/blob/173823d/scripts/validate-licenses.ts. 23 24 import {dirname, join} from 'node:path'; 25 import {fileURLToPath} from 'node:url'; 26 27 import checker from 'license-checker'; 28 import spdxSatisfies from 'spdx-satisfies'; 29 30 /** 31 * A general note on some black listed specific licenses: 32 * 33 * - CC0 This is not a valid license. It does not grant copyright of the 34 * code/asset, and does not resolve patents or other licensed work. The 35 * different claims also have no standing in court and do not provide 36 * protection to or from Google and/or third parties. We cannot use nor 37 * contribute to CC0 licenses. 38 * - Public Domain Same as CC0, it is not a valid license. 39 */ 40 const allowedLicenses = [ 41 // Regular valid open source licenses supported by Google. 42 'MIT', 43 'ISC', 44 'Apache-2.0', 45 'Python-2.0', 46 'Artistic-2.0', 47 'BlueOak-1.0.0', 48 49 'BSD-2-Clause', 50 'BSD-3-Clause', 51 'BSD-4-Clause', 52 53 // All CC-BY licenses have a full copyright grant and attribution section. 54 'CC-BY-3.0', 55 'CC-BY-4.0', 56 57 // Have a full copyright grant. Validated by opensource team. 58 'Unlicense', 59 'CC0-1.0', 60 '0BSD', 61 62 'AFL-2.1', 63 ]; 64 65 // Name variations of SPDX licenses that some packages have. 66 // Licenses not included in SPDX but accepted will be converted to MIT. 67 const licenseReplacements: {[key: string]: string} = { 68 // Just a longer string that our script catches. SPDX official name is the shorter one. 69 'Apache License, Version 2.0': 'Apache-2.0', 70 Apache2: 'Apache-2.0', 71 'Apache 2.0': 'Apache-2.0', 72 'Apache v2': 'Apache-2.0', 73 'AFLv2.1': 'AFL-2.1', 74 // BSD is BSD-2-clause by default. 75 BSD: 'BSD-2-Clause', 76 }; 77 78 // Specific packages to ignore, add a reason in a comment. Format: package-name@version. 79 const ignoredPackages = [ 80 // * Development only 81 'spdx-license-ids@3.0.5', // CC0 but it's content only (index.json, no code) and not distributed. 82 ]; 83 84 // Check if a license is accepted by an array of accepted licenses 85 function _passesSpdx(licenses: string[], accepted: string[]) { 86 try { 87 return spdxSatisfies(licenses.join(' AND '), accepted); 88 } catch (error) { 89 console.error('error while checking licenses:', error); 90 process.exit(1); 91 } 92 } 93 94 function main(): Promise<number> { 95 return new Promise(resolve => { 96 const __dirname = dirname(fileURLToPath(import.meta.url)); 97 const startFolder = join(__dirname, '..', '..'); 98 checker.init( 99 {start: startFolder, excludePrivatePackages: true}, 100 (err: Error, json: object) => { 101 if (err) { 102 console.error(`Something happened:\n${err.message}`); 103 resolve(1); 104 } else { 105 console.info(`Testing ${Object.keys(json).length} packages.\n`); 106 107 // Packages with bad licenses are those that neither pass SPDX nor are ignored. 108 const badLicensePackages = Object.keys(json) 109 .map(key => { 110 return { 111 id: key, 112 licenses: ([] as string[]) 113 .concat((json[key] as {licenses: string[]}).licenses) 114 // `*` is used when the license is guessed. 115 .map(x => { 116 return x.replace(/\*$/, ''); 117 }) 118 .map(x => { 119 return x in licenseReplacements 120 ? licenseReplacements[x] 121 : x; 122 }), 123 }; 124 }) 125 .filter(pkg => { 126 return !_passesSpdx(pkg.licenses, allowedLicenses); 127 }) 128 .filter(pkg => { 129 return !ignoredPackages.find(ignored => { 130 return ignored === pkg.id; 131 }); 132 }); 133 134 // Report packages with bad licenses 135 if (badLicensePackages.length > 0) { 136 console.error('Invalid package licenses found:'); 137 badLicensePackages.forEach(pkg => { 138 console.error(`${pkg.id}: ${JSON.stringify(pkg.licenses)}`); 139 }); 140 console.error( 141 `\n${badLicensePackages.length} total packages with invalid licenses.`, 142 ); 143 resolve(2); 144 } else { 145 console.info('All package licenses are valid.'); 146 resolve(0); 147 } 148 } 149 }, 150 ); 151 }); 152 } 153 154 main().then(code => { 155 return process.exit(code); 156 });