Skip to content

Commit 9ad7d9b

Browse files
committed
build: add script to diff local npm package with snapshot repository
This script will be very useful for the `rules_js` migration, as it allows us to quickly spot differences between the local `npm_package` target and the valid golden (based on the snapshot repository).
1 parent e31be73 commit 9ad7d9b

File tree

4 files changed

+144
-5
lines changed

4 files changed

+144
-5
lines changed

.aspect/rules/external_repository_action_cache/npm_translate_lock_MzA5NzUwNzMx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Input hashes for repository rule npm_translate_lock(name = "npm2", pnpm_lock = "@//:pnpm-lock.yaml").
33
# This file should be checked into version control along with the pnpm-lock.yaml file.
44
.npmrc=-2023857461
5-
package.json=91316777
6-
pnpm-lock.yaml=-448410040
5+
package.json=-1807090903
6+
pnpm-lock.yaml=-532781438
77
pnpm-workspace.yaml=1711114604
88
yarn.lock=-1740060057

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"ng-dev": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only node_modules/@angular/ng-dev/bundles/cli.mjs",
2525
"public-api:update": "node goldens/public-api/manage.js accept",
2626
"ts-circular-deps": "yarn ng-dev ts-circular-deps --config ./scripts/circular-deps-test.conf.mjs",
27-
"check-tooling-setup": "tsc --project .ng-dev/tsconfig.json"
27+
"check-tooling-setup": "tsc --project .ng-dev/tsconfig.json",
28+
"diff-release-package": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only scripts/diff-release-package.mts"
2829
},
2930
"repository": {
3031
"type": "git",

pnpm-lock.yaml

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/diff-release-package.mts

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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+
/**
10+
* Script that can be used to compare the local `npm_package` snapshot artifact
11+
* with the snapshot artifact from GitHub at upstream `HEAD`.
12+
*
13+
* This is useful during the `rules_js` migration to verify the npm artifact
14+
* doesn't differ unexpectedly.
15+
*
16+
* Example command: yarn diff-release-package @angular/cli
17+
*/
18+
19+
import { GitClient } from '@angular/ng-dev';
20+
import childProcess from 'node:child_process';
21+
import fs from 'node:fs';
22+
import os from 'node:os';
23+
import path from 'node:path';
24+
import sh from 'shelljs';
25+
26+
// Do not remove `.git` as we use Git for comparisons later.
27+
// Also preserve `uniqueId` as it's irrelevant for the diff and not included via Bazel.
28+
// The `README.md` is also put together outside of Bazel, so ignore it too.
29+
const SKIP_FILES = ['README.md', 'uniqueId', '.git'];
30+
31+
const packageName = process.argv[2];
32+
if (!packageName) {
33+
console.error('Expected package name to be specified.');
34+
process.exit(1);
35+
}
36+
37+
try {
38+
await main(packageName);
39+
} catch (e) {
40+
console.error(e);
41+
process.exitCode = 1;
42+
}
43+
44+
async function main(packageName: string) {
45+
const bazel = process.env.BAZEL ?? 'bazel';
46+
const git = await GitClient.get();
47+
const monorepoData = JSON.parse(fs.readFileSync('./.monorepo.json', 'utf-8'));
48+
const targetDir = packageName.replace(/^@/g, '').replace(/-/g, '_');
49+
50+
// `help/` is only generated in snapshots outside of Bazel. Ignore it when
51+
// diffing the Angular CLI package
52+
// https://github.com/angular/angular-cli/blob/main/scripts/json-help.mts#L16-L17.
53+
if (packageName === '@angular/cli') {
54+
SKIP_FILES.push('help');
55+
}
56+
57+
const snapshotRepoName = monorepoData.packages[packageName]?.snapshotRepo;
58+
const tmpDir = await fs.promises.mkdtemp(
59+
path.join(os.tmpdir(), `diff-release-package-${snapshotRepoName.replace(/\//g, '_')}`),
60+
);
61+
62+
console.log(`Cloning snapshot repo (${snapshotRepoName}) into ${tmpDir}..`);
63+
git.run(['clone', `https://github.com/${snapshotRepoName}.git`, tmpDir]);
64+
console.log(`--> Cloned snapshot repo.`);
65+
66+
const bazelBinDir = childProcess
67+
.spawnSync(bazel, ['info', 'bazel-bin'], {
68+
shell: true,
69+
encoding: 'utf8',
70+
stdio: ['pipe', 'pipe', 'inherit'],
71+
})
72+
.stdout.trim();
73+
if (bazelBinDir === '') {
74+
throw new Error('Could not determine bazel-bin directory.');
75+
}
76+
77+
const outputPath = path.join(bazelBinDir, 'packages/', targetDir, 'npm_package');
78+
79+
// Delete old directory to avoid surprises, or stamping being outdated.
80+
await deleteDir(outputPath);
81+
82+
childProcess.spawnSync(
83+
bazel,
84+
['build', `//packages/${targetDir}:npm_package`, '--config=snapshot'],
85+
{
86+
shell: true,
87+
stdio: 'inherit',
88+
encoding: 'utf8',
89+
},
90+
);
91+
92+
console.log('--> Built npm package with --config=snapshot');
93+
console.error(`--> Output: ${outputPath}`);
94+
95+
const removeTasks: Promise<void>[] = [];
96+
for (const subentry of await fs.promises.readdir(tmpDir)) {
97+
if (SKIP_FILES.includes(subentry)) {
98+
continue;
99+
}
100+
101+
removeTasks.push(
102+
fs.promises.rm(path.join(tmpDir, subentry), { recursive: true, maxRetries: 3 }),
103+
);
104+
}
105+
await Promise.all(removeTasks);
106+
107+
const copyTasks: Promise<void>[] = [];
108+
for (const subentry of await fs.promises.readdir(outputPath)) {
109+
if (SKIP_FILES.includes(subentry)) {
110+
continue;
111+
}
112+
113+
copyTasks.push(
114+
fs.promises.cp(path.join(outputPath, subentry), path.join(tmpDir, subentry), {
115+
recursive: true,
116+
}),
117+
);
118+
}
119+
await Promise.all(copyTasks);
120+
121+
git.run(['config', 'core.filemode', 'false'], { cwd: tmpDir });
122+
123+
const diff = git.run(['diff', '--color'], { cwd: tmpDir }).stdout;
124+
125+
console.log('\n\n----- Diff ------');
126+
console.log(diff);
127+
128+
await deleteDir(tmpDir);
129+
}
130+
131+
async function deleteDir(dirPath: string) {
132+
if (!fs.existsSync(dirPath)) {
133+
return;
134+
}
135+
136+
// Needed as Bazel artifacts are readonly and cannot be deleted otherwise.
137+
sh.chmod('-R', 'u+w', dirPath);
138+
await fs.promises.rm(dirPath, { recursive: true, force: true, maxRetries: 3 });
139+
}

0 commit comments

Comments
 (0)