Skip to content

Commit 96693b7

Browse files
committed
build: add function to validate and update 3rd party license files
- Implemented functionality to compare and update third-party license files - Added handling for '--accept' argument to update the golden license file - Included tests to ensure the Critters license file matches the golden reference This update ensures license file consistency and provides an easy way to update the reference file when necessary.
1 parent faee5c6 commit 96693b7

File tree

12 files changed

+529
-11
lines changed

12 files changed

+529
-11
lines changed

.prettierignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
/CONTRIBUTING.md
1313
.yarn/
1414
dist/
15-
third_party/
15+
third_party/github.com/
1616
/tests/legacy-cli/e2e/assets/
17-
/tools/test/*.json
17+
/tools/test/*.json

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"@bazel/buildifier": "7.1.2",
8484
"@bazel/concatjs": "patch:@bazel/concatjs@npm%3A5.8.1#~/.yarn/patches/@bazel-concatjs-npm-5.8.1-1bf81df846.patch",
8585
"@bazel/jasmine": "patch:@bazel/jasmine@npm%3A5.8.1#~/.yarn/patches/@bazel-jasmine-npm-5.8.1-3370fee155.patch",
86+
"@bazel/runfiles": "^5.8.1",
8687
"@discoveryjs/json-ext": "0.6.1",
8788
"@inquirer/confirm": "3.1.22",
8889
"@inquirer/prompts": "5.3.8",
@@ -91,6 +92,7 @@
9192
"@rollup/plugin-node-resolve": "^13.0.5",
9293
"@types/babel__core": "7.20.5",
9394
"@types/browser-sync": "^2.27.0",
95+
"@types/diff": "^5.2.1",
9496
"@types/express": "^4.16.0",
9597
"@types/http-proxy": "^1.17.4",
9698
"@types/ini": "^4.0.0",
@@ -130,6 +132,7 @@
130132
"critters": "0.0.24",
131133
"css-loader": "7.1.2",
132134
"debug": "^4.1.1",
135+
"diff": "^5.2.0",
133136
"esbuild": "0.23.1",
134137
"esbuild-wasm": "0.23.1",
135138
"eslint": "8.57.0",

packages/angular/ssr/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
"@angular/platform-browser": "19.0.0-next.1",
2929
"@angular/platform-server": "19.0.0-next.1",
3030
"@angular/router": "19.0.0-next.1",
31+
"@bazel/runfiles": "^5.8.1",
32+
"diff": "^5.2.0",
3133
"zone.js": "^0.15.0"
3234
},
3335
"schematics": "./schematics/collection.json",

packages/angular/ssr/test/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ ts_library(
1313
testonly = True,
1414
srcs = glob(
1515
include = ["**/*_spec.ts"],
16-
exclude = ESM_TESTS,
16+
exclude = ESM_TESTS + ["npm_package/**"],
1717
),
1818
deps = [
1919
"//packages/angular/ssr",
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
2+
load("@npm//@bazel/jasmine:index.bzl", "jasmine_node_test")
3+
load("//tools:defaults.bzl", "ts_library")
4+
5+
ts_library(
6+
name = "unit_test_lib",
7+
testonly = True,
8+
srcs = glob(["**/*.ts"]),
9+
deps = [
10+
"@npm//@bazel/runfiles",
11+
"@npm//@types/diff",
12+
],
13+
)
14+
15+
jasmine_node_test(
16+
name = "test",
17+
srcs = [":unit_test_lib"],
18+
data = [
19+
"THIRD_PARTY_LICENSES.txt.golden",
20+
"//packages/angular/ssr:npm_package",
21+
"@npm//diff",
22+
],
23+
)
24+
25+
nodejs_binary(
26+
name = "test.accept",
27+
testonly = True,
28+
data = [
29+
"THIRD_PARTY_LICENSES.txt.golden",
30+
":unit_test_lib",
31+
"//packages/angular/ssr:npm_package",
32+
"@npm//diff",
33+
],
34+
entry_point = ":update-package-golden.ts",
35+
)

packages/angular/ssr/test/npm_package/THIRD_PARTY_LICENSES.txt.golden

Lines changed: 350 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { createPatch } from 'diff';
10+
import { existsSync } from 'node:fs';
11+
import { readFile } from 'node:fs/promises';
12+
import { join } from 'node:path';
13+
import {
14+
ANGULAR_SSR_PACKAGE_PATH,
15+
CRITTERS_ACTUAL_LICENSE_FILE_PATH,
16+
CRITTERS_GOLDEN_LICENSE_FILE_PATH,
17+
} from './utils';
18+
19+
describe('NPM Package Tests', () => {
20+
it('should not include the contents of third_party/critters/index.js in the FESM bundle', async () => {
21+
const fesmFilePath = join(ANGULAR_SSR_PACKAGE_PATH, 'fesm2022/ssr.mjs');
22+
const fesmContent = await readFile(fesmFilePath, 'utf-8');
23+
expect(fesmContent).toContain(`import Critters from '../third_party/critters/index.js'`);
24+
});
25+
26+
describe('third_party/critters/THIRD_PARTY_LICENSES.txt', () => {
27+
it('should exist', () => {
28+
expect(existsSync(CRITTERS_ACTUAL_LICENSE_FILE_PATH)).toBe(true);
29+
});
30+
31+
it('should match the expected golden file', async () => {
32+
const [expectedContent, actualContent] = await Promise.all([
33+
readFile(CRITTERS_GOLDEN_LICENSE_FILE_PATH, 'utf-8'),
34+
readFile(CRITTERS_ACTUAL_LICENSE_FILE_PATH, 'utf-8'),
35+
]);
36+
37+
if (expectedContent.trim() === actualContent.trim()) {
38+
return;
39+
}
40+
41+
const patch = createPatch(
42+
CRITTERS_GOLDEN_LICENSE_FILE_PATH,
43+
expectedContent,
44+
actualContent,
45+
'Golden License File',
46+
'Current License File',
47+
{ context: 5 },
48+
);
49+
50+
const errorMessage = `The content of the actual license file differs from the expected golden reference.
51+
Diff:
52+
${patch}
53+
To accept the new golden file, execute:
54+
yarn bazel run ${process.env['BAZEL_TARGET']}.accept
55+
`;
56+
57+
const error = new Error(errorMessage);
58+
error.stack = error.stack?.replace(` Diff:\n ${patch}`, '');
59+
throw error;
60+
});
61+
});
62+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { readFileSync, writeFileSync } from 'node:fs';
10+
import { CRITTERS_ACTUAL_LICENSE_FILE_PATH, CRITTERS_GOLDEN_LICENSE_FILE_PATH } from './utils';
11+
12+
/**
13+
* Updates the golden reference license file.
14+
*/
15+
writeFileSync(CRITTERS_GOLDEN_LICENSE_FILE_PATH, readFileSync(CRITTERS_ACTUAL_LICENSE_FILE_PATH));
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { runfiles } from '@bazel/runfiles';
10+
import { dirname, join } from 'node:path';
11+
12+
/**
13+
* Resolve paths for the Critters license file and the golden reference file.
14+
*/
15+
export const ANGULAR_SSR_PACKAGE_PATH = dirname(
16+
runfiles.resolve('angular_cli/packages/angular/ssr/npm_package/package.json'),
17+
);
18+
19+
/**
20+
* Path to the actual license file for the Critters library.
21+
* This file is located in the `third_party/critters` directory of the Angular CLI npm package.
22+
*/
23+
export const CRITTERS_ACTUAL_LICENSE_FILE_PATH = join(
24+
ANGULAR_SSR_PACKAGE_PATH,
25+
'third_party/critters/THIRD_PARTY_LICENSES.txt',
26+
);
27+
28+
/**
29+
* Path to the golden reference license file for the Critters library.
30+
* This file is used as a reference for comparison and is located in the same directory as this script.
31+
*/
32+
export const CRITTERS_GOLDEN_LICENSE_FILE_PATH = join(__dirname, 'THIRD_PARTY_LICENSES.txt.golden');

packages/angular/ssr/third_party/critters/esbuild.config.mjs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,9 @@ const EXTRACTION_FILE_SEPARATOR = '-'.repeat(80) + '\n';
8787
* @returns A string containing the content of the output licenses file.
8888
*/
8989
async function extractLicenses(metafile) {
90-
let extractedLicenseContent = `${EXTRACTION_FILE_HEADER}\n${EXTRACTION_FILE_SEPARATOR}`;
91-
9290
const seenPaths = new Set();
9391
const seenPackages = new Set();
92+
const extractedLicenses = {};
9493

9594
for (const entry of Object.values(metafile.outputs)) {
9695
for (const [inputPath, { bytesInOutput }] of Object.entries(entry.inputs)) {
@@ -186,12 +185,20 @@ async function extractLicenses(metafile) {
186185
}
187186

188187
// Generate the package's license entry in the output content
189-
extractedLicenseContent += `Package: ${packageJson.name}\n`;
190-
extractedLicenseContent += `License: ${JSON.stringify(packageJson.license, null, 2)}\n`;
191-
extractedLicenseContent += `\n${licenseText}\n`;
188+
let extractedLicenseContent = `Package: ${packageJson.name}\n`;
189+
extractedLicenseContent += `License: ${JSON.stringify(packageJson.license, undefined, 2)}\n`;
190+
extractedLicenseContent += `\n${licenseText.trim().replace(/\r?\n/g, '\n')}\n`;
192191
extractedLicenseContent += EXTRACTION_FILE_SEPARATOR;
192+
193+
extractedLicenses[packageJson.name] = extractedLicenseContent;
193194
}
194195
}
195196

196-
return extractedLicenseContent;
197+
// Get the keys of the object and sort them and etract and join the values corresponding to the sorted keys
198+
const joinedLicenseContent = Object.keys(extractedLicenses)
199+
.sort()
200+
.map((pkgName) => extractedLicenses[pkgName])
201+
.join('');
202+
203+
return `${EXTRACTION_FILE_HEADER}\n${EXTRACTION_FILE_SEPARATOR}${joinedLicenseContent}`;
197204
}

0 commit comments

Comments
 (0)