diff --git a/CHANGELOG.md b/CHANGELOG.md index aa5a1c97..912023a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [Unreleased] +### :syringe: Fixed + +- [#333](https://github.com/FantasticFiasco/action-update-license-year/issues/333) [BREAKING CHANGE] Update all licenses in a file, not only the first found (discovered by [@glimchb](https://github.com/glimchb)). + + **Migration guide** + + Custom RegExp transforms where previously written to execute with the `mi` flags ("multiline" and "ignore case"). With this new major version the transform also needs to respect the `g` flag ("global"). I.e. please verify that your custom transform behaves as expected when used with the `gmi` flags. + ## [2.3.0] - 2023-01-06 ### :zap: Added diff --git a/dist/index.js b/dist/index.js index a2405a51..b9649449 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1244,16 +1244,18 @@ exports.create = create; * Computes the sha256 hash of a glob * * @param patterns Patterns separated by newlines + * @param currentWorkspace Workspace used when matching files * @param options Glob options + * @param verbose Enables verbose logging */ -function hashFiles(patterns, options, verbose = false) { +function hashFiles(patterns, currentWorkspace = '', options, verbose = false) { return __awaiter(this, void 0, void 0, function* () { let followSymbolicLinks = true; if (options && typeof options.followSymbolicLinks === 'boolean') { followSymbolicLinks = options.followSymbolicLinks; } const globber = yield create(patterns, { followSymbolicLinks }); - return internal_hash_files_1.hashFiles(globber, verbose); + return internal_hash_files_1.hashFiles(globber, currentWorkspace, verbose); }); } exports.hashFiles = hashFiles; @@ -1613,13 +1615,15 @@ const fs = __importStar(__nccwpck_require__(7147)); const stream = __importStar(__nccwpck_require__(2781)); const util = __importStar(__nccwpck_require__(3837)); const path = __importStar(__nccwpck_require__(1017)); -function hashFiles(globber, verbose = false) { +function hashFiles(globber, currentWorkspace, verbose = false) { var e_1, _a; var _b; return __awaiter(this, void 0, void 0, function* () { const writeDelegate = verbose ? core.info : core.debug; let hasMatch = false; - const githubWorkspace = (_b = process.env['GITHUB_WORKSPACE']) !== null && _b !== void 0 ? _b : process.cwd(); + const githubWorkspace = currentWorkspace + ? currentWorkspace + : (_b = process.env['GITHUB_WORKSPACE']) !== null && _b !== void 0 ? _b : process.cwd(); const result = crypto.createHash('sha256'); let count = 0; try { @@ -16932,56 +16936,36 @@ module.exports = Repository // Regular expressions capable of transforming the following license files: // - GNU Affero General Public License v3.0 only (AGPL-3.0-only) // prettier-ignore -const AGPL_3_ONLY_YEAR_RANGE = new RegExp( +const AGPL_3_ONLY = new RegExp( '(?<=copyright\\s+\\(c\\)\\s+)' + // 'Copyright (C) ' positive lookbehind '(?\\d{4})' + // 'YYYY' group named 'from' - '-\\d{4}' + // '-YYYY' + '(-\\d{4})?' + // '(-YYYY)?' '(?!\\s+free\\s+software\\s+foundation)', // ' Free Software Foundation' negative lookahead - 'im' // Multi-line/Insensitive -); -// prettier-ignore -const AGPL_3_ONLY_SINGLE_YEAR = new RegExp( - '(?<=copyright\\s+\\(c\\)\\s+)' + // 'Copyright (C) ' positive lookbehind - '(?\\d{4})' + // 'YYYY' group named 'from' - '(?!\\s+free software foundation)', // ' Free Software Foundation' negative lookahead - 'im' // Multi-line/Insensitive + 'gmi' // Global/Multi-line/Insensitive ); // Regular expressions capable of transforming the following license files: // - Apache 2.0 (Apache-2.0) // - MIT (MIT) // prettier-ignore -const APACHE_2_MIT_YEAR_RANGE = new RegExp( +const APACHE_2_MIT = new RegExp( '(?<=copyright\\s+)' + // 'Copyright ' positive lookbehind '(?\\d{4})' + // 'YYYY' group named 'from' - '-\\d{4}', // '-YYYY' - 'im' // Multi-line/Insensitive -); -// prettier-ignore -const APACHE_2_MIT_SINGLE_YEAR = new RegExp( - '(?<=copyright\\s+)' + // 'Copyright ' positive lookbehind - '(?\\d{4})', // 'YYYY' group named 'from' - 'im' // Multi-line/Insensitive + '(-\\d{4})?', // '(-YYYY)?' + 'gmi' // Global/Multi-line/Insensitive ); // Regular expressions capable of transforming the following license files: // - BSD 2-clause "Simplified" (BSD-2-Clause) // - BSD 3-clause "New" or "Revised" (BSD-3-Clause) // prettier-ignore -const BSD_YEAR_RANGE = new RegExp( +const BSD = new RegExp( '(?<=copyright\\s+\\(c\\)\\s+)' + // 'Copyright (c) ' positive lookbehind '(?\\d{4})' + // 'YYYY' group named 'from' - '-\\d{4}' + // '-YYYY' + '(-\\d{4})?' + // '(-YYYY)?' '(?=,)', // ',' positive lookahead - 'im' // Multi-line/Insensitive + 'gmi' // Global/Multi-line/Insensitive ) -// prettier-ignore -const BSD_SINGLE_YEAR = new RegExp( - '(?<=copyright\\s+\\(c\\)\\s+)' + // 'Copyright (c) ' positive lookbehind - '(?\\d{4})' + // 'YYYY' group named 'from' - '(?=,)', // ',' positive lookahead - 'im' // Multi-line/Insensitive -); /** * @typedef LicenseTransform @@ -16989,12 +16973,9 @@ const BSD_SINGLE_YEAR = new RegExp( * @property {RegExp} transform */ const DEFAULT_LICENSE_TRANSFORMS = [ - { name: 'AGPL-3.0-only', transform: AGPL_3_ONLY_YEAR_RANGE }, - { name: 'AGPL-3.0-only', transform: AGPL_3_ONLY_SINGLE_YEAR }, - { name: 'Apache-2.0', transform: APACHE_2_MIT_YEAR_RANGE }, - { name: 'Apache-2.0', transform: APACHE_2_MIT_SINGLE_YEAR }, - { name: 'BSD-2-Clause/BSD-3-Clause/MIT', transform: BSD_YEAR_RANGE }, - { name: 'BSD-2-Clause/BSD-3-Clause/MIT', transform: BSD_SINGLE_YEAR }, + { name: 'AGPL-3.0-only', transform: AGPL_3_ONLY }, + { name: 'Apache-2.0', transform: APACHE_2_MIT }, + { name: 'BSD-2-Clause/BSD-3-Clause/MIT', transform: BSD }, ] /** @@ -17033,7 +17014,7 @@ const applyDefaultTransform = (license, currentYear, fileName) => { const applyCustomTransform = (transform, license, currentYear, fileName) => { const licenseTransform = { name: 'Custom', - transform: new RegExp(transform, 'im'), + transform: new RegExp(transform, 'gmi'), } if (!canApplyLicenseTransform(licenseTransform, license)) { @@ -17057,16 +17038,24 @@ const canApplyLicenseTransform = (licenseTransform, license) => { * @param {number} currentYear */ const applyLicenseTransform = (licenseTransform, license, currentYear) => { - const match = licenseTransform.transform.exec(license) - if (match === null || match.groups === undefined) { - throw new Error(`Transforming ${licenseTransform.name} license failed`) - } + license = license.replace(licenseTransform.transform, (match, ...args) => { + // The last argument is the groups object + const groups = args[args.length - 1] - if (Number(match.groups['from']) === currentYear) { - return license - } + if (groups === undefined) { + throw new Error(`Transforming ${licenseTransform.name} license failed`) + } + + const from = groups['from'] + + if (Number(from) === currentYear) { + return from + } + + return `${from}-${currentYear}` + }) - return license.replace(licenseTransform.transform, `$-${currentYear}`) + return license } module.exports = { diff --git a/src/transforms.js b/src/transforms.js index 4c8bc8a8..cc946d65 100644 --- a/src/transforms.js +++ b/src/transforms.js @@ -1,56 +1,36 @@ // Regular expressions capable of transforming the following license files: // - GNU Affero General Public License v3.0 only (AGPL-3.0-only) // prettier-ignore -const AGPL_3_ONLY_YEAR_RANGE = new RegExp( +const AGPL_3_ONLY = new RegExp( '(?<=copyright\\s+\\(c\\)\\s+)' + // 'Copyright (C) ' positive lookbehind '(?\\d{4})' + // 'YYYY' group named 'from' - '-\\d{4}' + // '-YYYY' + '(-\\d{4})?' + // '(-YYYY)?' '(?!\\s+free\\s+software\\s+foundation)', // ' Free Software Foundation' negative lookahead - 'im' // Multi-line/Insensitive -); -// prettier-ignore -const AGPL_3_ONLY_SINGLE_YEAR = new RegExp( - '(?<=copyright\\s+\\(c\\)\\s+)' + // 'Copyright (C) ' positive lookbehind - '(?\\d{4})' + // 'YYYY' group named 'from' - '(?!\\s+free software foundation)', // ' Free Software Foundation' negative lookahead - 'im' // Multi-line/Insensitive + 'gmi' // Global/Multi-line/Insensitive ); // Regular expressions capable of transforming the following license files: // - Apache 2.0 (Apache-2.0) // - MIT (MIT) // prettier-ignore -const APACHE_2_MIT_YEAR_RANGE = new RegExp( +const APACHE_2_MIT = new RegExp( '(?<=copyright\\s+)' + // 'Copyright ' positive lookbehind '(?\\d{4})' + // 'YYYY' group named 'from' - '-\\d{4}', // '-YYYY' - 'im' // Multi-line/Insensitive -); -// prettier-ignore -const APACHE_2_MIT_SINGLE_YEAR = new RegExp( - '(?<=copyright\\s+)' + // 'Copyright ' positive lookbehind - '(?\\d{4})', // 'YYYY' group named 'from' - 'im' // Multi-line/Insensitive + '(-\\d{4})?', // '(-YYYY)?' + 'gmi' // Global/Multi-line/Insensitive ); // Regular expressions capable of transforming the following license files: // - BSD 2-clause "Simplified" (BSD-2-Clause) // - BSD 3-clause "New" or "Revised" (BSD-3-Clause) // prettier-ignore -const BSD_YEAR_RANGE = new RegExp( +const BSD = new RegExp( '(?<=copyright\\s+\\(c\\)\\s+)' + // 'Copyright (c) ' positive lookbehind '(?\\d{4})' + // 'YYYY' group named 'from' - '-\\d{4}' + // '-YYYY' + '(-\\d{4})?' + // '(-YYYY)?' '(?=,)', // ',' positive lookahead - 'im' // Multi-line/Insensitive + 'gmi' // Global/Multi-line/Insensitive ) -// prettier-ignore -const BSD_SINGLE_YEAR = new RegExp( - '(?<=copyright\\s+\\(c\\)\\s+)' + // 'Copyright (c) ' positive lookbehind - '(?\\d{4})' + // 'YYYY' group named 'from' - '(?=,)', // ',' positive lookahead - 'im' // Multi-line/Insensitive -); /** * @typedef LicenseTransform @@ -58,12 +38,9 @@ const BSD_SINGLE_YEAR = new RegExp( * @property {RegExp} transform */ const DEFAULT_LICENSE_TRANSFORMS = [ - { name: 'AGPL-3.0-only', transform: AGPL_3_ONLY_YEAR_RANGE }, - { name: 'AGPL-3.0-only', transform: AGPL_3_ONLY_SINGLE_YEAR }, - { name: 'Apache-2.0', transform: APACHE_2_MIT_YEAR_RANGE }, - { name: 'Apache-2.0', transform: APACHE_2_MIT_SINGLE_YEAR }, - { name: 'BSD-2-Clause/BSD-3-Clause/MIT', transform: BSD_YEAR_RANGE }, - { name: 'BSD-2-Clause/BSD-3-Clause/MIT', transform: BSD_SINGLE_YEAR }, + { name: 'AGPL-3.0-only', transform: AGPL_3_ONLY }, + { name: 'Apache-2.0', transform: APACHE_2_MIT }, + { name: 'BSD-2-Clause/BSD-3-Clause/MIT', transform: BSD }, ] /** @@ -102,7 +79,7 @@ const applyDefaultTransform = (license, currentYear, fileName) => { const applyCustomTransform = (transform, license, currentYear, fileName) => { const licenseTransform = { name: 'Custom', - transform: new RegExp(transform, 'im'), + transform: new RegExp(transform, 'gmi'), } if (!canApplyLicenseTransform(licenseTransform, license)) { @@ -126,16 +103,24 @@ const canApplyLicenseTransform = (licenseTransform, license) => { * @param {number} currentYear */ const applyLicenseTransform = (licenseTransform, license, currentYear) => { - const match = licenseTransform.transform.exec(license) - if (match === null || match.groups === undefined) { - throw new Error(`Transforming ${licenseTransform.name} license failed`) - } + license = license.replace(licenseTransform.transform, (match, ...args) => { + // The last argument is the groups object + const groups = args[args.length - 1] - if (Number(match.groups['from']) === currentYear) { - return license - } + if (groups === undefined) { + throw new Error(`Transforming ${licenseTransform.name} license failed`) + } + + const from = groups['from'] + + if (Number(from) === currentYear) { + return from + } + + return `${from}-${currentYear}` + }) - return license.replace(licenseTransform.transform, `$-${currentYear}`) + return license } module.exports = { diff --git a/test/transform.test.js b/test/transform.test.js index f0ac0ccc..8bfdcd92 100644 --- a/test/transform.test.js +++ b/test/transform.test.js @@ -40,6 +40,17 @@ describe('#applyTransform should', () => { const want = readTestFile('AGPL-3.0-only/SINGLE_YEAR') expect(got).toBe(want) }) + + test('not transform license from a range of years given year is unchanged', () => { + const got = transforms.applyTransform( + defaultTransform, + readTestFile('AGPL-3.0-only/RANGE_OF_YEARS'), + 2001, + defaultFileName + ) + const want = readTestFile('AGPL-3.0-only/RANGE_OF_YEARS') + expect(got).toBe(want) + }) }) describe('given Apache-2.0', () => { @@ -75,6 +86,17 @@ describe('#applyTransform should', () => { const want = readTestFile('Apache-2.0/SINGLE_YEAR') expect(got).toBe(want) }) + + test('not transform license from a range of years given year is unchanged', () => { + const got = transforms.applyTransform( + defaultTransform, + readTestFile('Apache-2.0/RANGE_OF_YEARS'), + 2001, + defaultFileName + ) + const want = readTestFile('Apache-2.0/RANGE_OF_YEARS') + expect(got).toBe(want) + }) }) describe('given BSD-2-Clause', () => { @@ -110,6 +132,17 @@ describe('#applyTransform should', () => { const want = readTestFile('BSD-2-Clause/SINGLE_YEAR') expect(got).toBe(want) }) + + test('not transform license from a range of years given year is unchanged', () => { + const got = transforms.applyTransform( + defaultTransform, + readTestFile('BSD-2-Clause/RANGE_OF_YEARS'), + 2001, + defaultFileName + ) + const want = readTestFile('BSD-2-Clause/RANGE_OF_YEARS') + expect(got).toBe(want) + }) }) describe('given BSD-3-Clause', () => { @@ -145,6 +178,17 @@ describe('#applyTransform should', () => { const want = readTestFile('BSD-3-Clause/SINGLE_YEAR') expect(got).toBe(want) }) + + test('not transform license from a range of years given year is unchanged', () => { + const got = transforms.applyTransform( + defaultTransform, + readTestFile('BSD-3-Clause/RANGE_OF_YEARS'), + 2001, + defaultFileName + ) + const want = readTestFile('BSD-3-Clause/RANGE_OF_YEARS') + expect(got).toBe(want) + }) }) describe('given MIT', () => { @@ -180,12 +224,45 @@ describe('#applyTransform should', () => { const want = readTestFile('MIT/SINGLE_YEAR') expect(got).toBe(want) }) + + test('not transform license from a range of years given year is unchanged', () => { + const got = transforms.applyTransform( + defaultTransform, + readTestFile('MIT/RANGE_OF_YEARS'), + 2001, + defaultFileName + ) + const want = readTestFile('MIT/RANGE_OF_YEARS') + expect(got).toBe(want) + }) }) describe('given custom transform', () => { - test('transform license', () => { + test('transform license from a single year into a range of years', () => { + const got = transforms.applyTransform( + '(?<=some copyright )(?\\d{4})(-\\d{4})?', + 'some copyright 2000', + 2002, + defaultFileName + ) + const want = 'some copyright 2000-2002' + expect(got).toBe(want) + }) + + test('transform license from a multiple single years into multiple ranges of years', () => { + const got = transforms.applyTransform( + '(?<=some copyright )(?\\d{4})(-\\d{4})?', + 'some copyright 2000 aaa\nsome copyright 2000 bbb', + 2002, + defaultFileName + ) + const want = 'some copyright 2000-2002 aaa\nsome copyright 2000-2002 bbb' + expect(got).toBe(want) + }) + + test('transform license from a range of years into a new range of years', () => { const got = transforms.applyTransform( - '(?<=some copyright )(?\\d{4})-\\d{4}', + '(?<=some copyright )(?\\d{4})(-\\d{4})?', 'some copyright 2000-2001', 2002, defaultFileName @@ -193,6 +270,50 @@ describe('#applyTransform should', () => { const want = 'some copyright 2000-2002' expect(got).toBe(want) }) + + test('transform license from multiple ranges of years into multiple new ranges of years', () => { + const got = transforms.applyTransform( + '(?<=some copyright )(?\\d{4})(-\\d{4})?', + 'some copyright 2000-2001 aaa\nsome copyright 2000-2001 bbb', + 2002, + defaultFileName + ) + const want = 'some copyright 2000-2002 aaa\nsome copyright 2000-2002 bbb' + expect(got).toBe(want) + }) + + test('transform license from a mix of single years and ranges of years into multiple new ranges of years', () => { + const got = transforms.applyTransform( + '(?<=some copyright )(?\\d{4})(-\\d{4})?', + 'some copyright 2000 aaa\nsome copyright 2000-2001 bbb', + 2002, + defaultFileName + ) + const want = 'some copyright 2000-2002 aaa\nsome copyright 2000-2002 bbb' + expect(got).toBe(want) + }) + + test('not transform license from a single year given year is unchanged', () => { + const got = transforms.applyTransform( + '(?<=some copyright )(?\\d{4})(-\\d{4})?', + 'some copyright 2000', + 2000, + defaultFileName + ) + const want = 'some copyright 2000' + expect(got).toBe(want) + }) + + test('not transform license from a range of years given year is unchanged', () => { + const got = transforms.applyTransform( + '(?<=some copyright )(?\\d{4})(-\\d{4})?', + 'some copyright 2000-2001', + 2001, + defaultFileName + ) + const want = 'some copyright 2000-2001' + expect(got).toBe(want) + }) }) test('throw error given unsupported license format', () => {