Skip to content

Commit 4bd95a9

Browse files
committed
ci: create script which generates path filters from internal dependencies map
1 parent 66ffa19 commit 4bd95a9

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

scripts/generate-filters.js

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// @ts-check
2+
/**
3+
* This script generates filters for https://github.com/dorny/paths-filter
4+
*
5+
* It is used in CI to find all the packages which have been modified or
6+
* have a dependency (including transitive) that has been modified.
7+
*
8+
* This enables running checks only for the packages affected by the changes.
9+
*/
10+
const fs = require("fs");
11+
const path = require("path");
12+
13+
function main() {
14+
const packageIgnore = JSON.parse(process.env.PACKAGE_IGNORE || "[]");
15+
const commonFilters = JSON.parse(process.env.COMMON_FILTERS || "[]");
16+
17+
const pnpmLockfilePath = path.join(__dirname, "..", "pnpm-lock.json");
18+
if (!fs.existsSync(pnpmLockfilePath)) {
19+
console.warn(
20+
`${pnpmLockfilePath} doesn't exist, please run: yq -p yaml -o json pnpm-lock.yaml | tee pnpm-lock.json`
21+
);
22+
process.exit(1);
23+
}
24+
25+
const pnpmLockfile = JSON.parse(fs.readFileSync(pnpmLockfilePath, "utf8"));
26+
27+
// Find all direct internal dependencies for all packages
28+
const internalDependenciesMap = {};
29+
for (const [package, allDependencies] of Object.entries(
30+
pnpmLockfile.importers
31+
)) {
32+
const internalDependencies = Object.values(allDependencies)
33+
.flatMap((dependencies) => Object.values(dependencies))
34+
.map((dependency) => dependency.version)
35+
.filter((version) => version.startsWith("link:"))
36+
.map((version) => version.replace("link:", ""))
37+
.map((version) => path.join(package, version));
38+
internalDependenciesMap[package] = internalDependencies;
39+
}
40+
41+
// Add transitive internal dependencies
42+
for (const dependencies of Object.values(internalDependenciesMap)) {
43+
const dependencyQueue = [...dependencies];
44+
while (dependencyQueue.length !== 0) {
45+
const dependency = dependencyQueue.pop();
46+
for (const transitiveDependency of internalDependenciesMap[dependency]) {
47+
if (!dependencies.includes(transitiveDependency)) {
48+
dependencies.push(transitiveDependency);
49+
dependencyQueue.push(transitiveDependency);
50+
}
51+
}
52+
}
53+
}
54+
55+
// Generate filters
56+
const filters = {};
57+
for (const [package, dependencies] of Object.entries(
58+
internalDependenciesMap
59+
)) {
60+
// Ignore packages that start with one of the prefixes in PACKAGE_IGNORE
61+
if (packageIgnore.some((prefix) => package.startsWith(prefix))) {
62+
continue;
63+
}
64+
// Calculate glob patterns for the package and its dependencies
65+
const packageFilters = [package, ...dependencies].map((dependency) =>
66+
path.join(dependency, "**")
67+
);
68+
// Set filters for the package
69+
filters[package] = [...commonFilters, ...packageFilters];
70+
}
71+
72+
// Pretty print the filters
73+
console.log(JSON.stringify(filters, null, 2));
74+
}
75+
76+
main();

0 commit comments

Comments
 (0)