Skip to content

Commit e7e0d6a

Browse files
authored
Merge pull request #1535 from iclanton/ianc/fail-on-outdated-approved-packages
[rush] Make rush install error if the approved package files are out-of-date.
2 parents 27c729c + 64a9371 commit e7e0d6a

7 files changed

+121
-32
lines changed

apps/rush-lib/src/api/ApprovedPackagesConfiguration.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,23 @@ export class ApprovedPackagesConfiguration {
7878
return this._itemsByName.get(packageName);
7979
}
8080

81-
public addOrUpdatePackage(packageName: string, reviewCategory: string): void {
81+
public addOrUpdatePackage(packageName: string, reviewCategory: string): boolean {
82+
let changed: boolean = false;
83+
8284
let item: ApprovedPackagesItem | undefined = this._itemsByName.get(packageName);
8385
if (!item) {
8486
item = new ApprovedPackagesItem();
8587
item.packageName = packageName;
8688
this._addItem(item);
89+
changed = true;
8790
}
8891

89-
if (reviewCategory) {
92+
if (reviewCategory && !item.allowedCategories.has(reviewCategory)) {
9093
item.allowedCategories.add(reviewCategory);
94+
changed = true;
9195
}
96+
97+
return changed;
9298
}
9399

94100
/**

apps/rush-lib/src/api/ApprovedPackagesPolicy.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,18 @@ export class ApprovedPackagesPolicy {
3535
}
3636

3737
// Load browser-approved-packages.json
38-
const browserApprovedPackagesPath: string = path.join(rushConfiguration.commonRushConfigFolder,
39-
RushConstants.browserApprovedPackagesFilename);
38+
const browserApprovedPackagesPath: string = path.join(
39+
rushConfiguration.commonRushConfigFolder,
40+
RushConstants.browserApprovedPackagesFilename
41+
);
4042
this._browserApprovedPackages = new ApprovedPackagesConfiguration(browserApprovedPackagesPath);
4143
this._browserApprovedPackages.tryLoadFromFile(this._enabled);
4244

4345
// Load nonbrowser-approved-packages.json
44-
const nonbrowserApprovedPackagesPath: string = path.join(rushConfiguration.commonRushConfigFolder,
45-
RushConstants.nonbrowserApprovedPackagesFilename);
46+
const nonbrowserApprovedPackagesPath: string = path.join(
47+
rushConfiguration.commonRushConfigFolder,
48+
RushConstants.nonbrowserApprovedPackagesFilename
49+
);
4650
this._nonbrowserApprovedPackages = new ApprovedPackagesConfiguration(nonbrowserApprovedPackagesPath);
4751
this._nonbrowserApprovedPackages.tryLoadFromFile(this._enabled);
4852
}

apps/rush-lib/src/logic/ApprovedPackagesChecker.ts

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,27 @@ import { RushConfigurationProject } from '../api/RushConfigurationProject';
99
import { DependencySpecifier } from './DependencySpecifier';
1010

1111
export class ApprovedPackagesChecker {
12+
private readonly _rushConfiguration: RushConfiguration;
13+
private _approvedPackagesPolicy: ApprovedPackagesPolicy;
14+
private _filesAreOutOfDate: boolean;
15+
16+
public constructor(rushConfiguration: RushConfiguration) {
17+
this._rushConfiguration = rushConfiguration;
18+
this._approvedPackagesPolicy = this._rushConfiguration.approvedPackagesPolicy;
19+
this._filesAreOutOfDate = false;
20+
21+
if (this._approvedPackagesPolicy.enabled) {
22+
this._updateApprovedPackagesPolicy();
23+
}
24+
}
25+
26+
/**
27+
* If true, the files on disk are out of date.
28+
*/
29+
public get approvedPackagesFilesAreOutOfDate(): boolean {
30+
return this._filesAreOutOfDate;
31+
}
32+
1233
/**
1334
* Examines the current dependencies for the projects specified in RushConfiguration,
1435
* and then adds them to the 'browser-approved-packages.json' and
@@ -17,30 +38,46 @@ export class ApprovedPackagesChecker {
1738
*
1839
* If the "approvedPackagesPolicy" feature is not enabled, then no action is taken.
1940
*/
20-
public static rewriteConfigFiles(rushConfiguration: RushConfiguration): void {
21-
const approvedPackagesPolicy: ApprovedPackagesPolicy = rushConfiguration.approvedPackagesPolicy;
22-
if (!approvedPackagesPolicy.enabled) {
23-
return;
41+
public rewriteConfigFiles(): void {
42+
const approvedPackagesPolicy: ApprovedPackagesPolicy = this._rushConfiguration.approvedPackagesPolicy;
43+
if (approvedPackagesPolicy.enabled) {
44+
approvedPackagesPolicy.browserApprovedPackages.saveToFile();
45+
approvedPackagesPolicy.nonbrowserApprovedPackages.saveToFile();
2446
}
47+
}
2548

26-
for (const rushProject of rushConfiguration.projects) {
49+
private _updateApprovedPackagesPolicy(): void {
50+
for (const rushProject of this._rushConfiguration.projects) {
2751
const packageJson: IPackageJson = rushProject.packageJson;
2852

29-
ApprovedPackagesChecker._collectDependencies(packageJson.dependencies,
30-
approvedPackagesPolicy, rushProject);
31-
ApprovedPackagesChecker._collectDependencies(packageJson.optionalDependencies,
32-
approvedPackagesPolicy, rushProject);
33-
ApprovedPackagesChecker._collectDependencies(packageJson.devDependencies,
34-
approvedPackagesPolicy, rushProject);
53+
this._collectDependencies(
54+
packageJson.dependencies,
55+
this._approvedPackagesPolicy,
56+
rushProject
57+
);
58+
this._collectDependencies(
59+
packageJson.devDependencies,
60+
this._approvedPackagesPolicy,
61+
rushProject
62+
);
63+
this._collectDependencies(
64+
packageJson.peerDependencies,
65+
this._approvedPackagesPolicy,
66+
rushProject
67+
);
68+
this._collectDependencies(
69+
packageJson.optionalDependencies,
70+
this._approvedPackagesPolicy,
71+
rushProject
72+
);
3573
}
36-
37-
approvedPackagesPolicy.browserApprovedPackages.saveToFile();
38-
approvedPackagesPolicy.nonbrowserApprovedPackages.saveToFile();
3974
}
4075

41-
private static _collectDependencies(dependencies: { [key: string]: string } | undefined,
42-
approvedPackagesPolicy: ApprovedPackagesPolicy, rushProject: RushConfigurationProject): void {
43-
76+
private _collectDependencies(
77+
dependencies: { [key: string]: string } | undefined,
78+
approvedPackagesPolicy: ApprovedPackagesPolicy,
79+
rushProject: RushConfigurationProject
80+
): void {
4481
if (dependencies) {
4582
for (const packageName of Object.keys(dependencies)) {
4683

@@ -51,8 +88,10 @@ export class ApprovedPackagesChecker {
5188
// "dependencies": {
5289
// "alias-name": "npm:target-name@^1.2.3"
5390
// }
54-
const dependencySpecifier: DependencySpecifier
55-
= new DependencySpecifier(packageName, dependencies[packageName]);
91+
const dependencySpecifier: DependencySpecifier = new DependencySpecifier(
92+
packageName,
93+
dependencies[packageName]
94+
);
5695
if (dependencySpecifier.aliasTarget) {
5796
// Use "target-name" instead of "alias-name"
5897
referencedPackageName = dependencySpecifier.aliasTarget.packageName;
@@ -64,15 +103,23 @@ export class ApprovedPackagesChecker {
64103
if (!approvedPackagesPolicy.ignoredNpmScopes.has(scope)) {
65104
// Yes, add it to the list if it's not already there
66105

106+
let updated: boolean = false;
107+
67108
// By default we put everything in the browser file. But if it already appears in the
68109
// non-browser file, then use that instead.
69110
if (approvedPackagesPolicy.nonbrowserApprovedPackages.getItemByName(referencedPackageName)) {
70-
approvedPackagesPolicy.nonbrowserApprovedPackages
71-
.addOrUpdatePackage(referencedPackageName, rushProject.reviewCategory);
111+
updated = approvedPackagesPolicy.nonbrowserApprovedPackages.addOrUpdatePackage(
112+
referencedPackageName,
113+
rushProject.reviewCategory
114+
);
72115
} else {
73-
approvedPackagesPolicy.browserApprovedPackages
74-
.addOrUpdatePackage(referencedPackageName, rushProject.reviewCategory);
116+
updated = approvedPackagesPolicy.browserApprovedPackages.addOrUpdatePackage(
117+
referencedPackageName,
118+
rushProject.reviewCategory
119+
);
75120
}
121+
122+
this._filesAreOutOfDate = this._filesAreOutOfDate || updated;
76123
}
77124
}
78125
}

apps/rush-lib/src/logic/InstallManager.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,6 @@ export class InstallManager {
271271
// Check the policies
272272
PolicyValidator.validatePolicy(this._rushConfiguration, options.bypassPolicy);
273273

274-
ApprovedPackagesChecker.rewriteConfigFiles(this._rushConfiguration);
275-
276274
// Git hooks are only installed if the repo opts in by including files in /common/git-hooks
277275
const hookSource: string = path.join(this._rushConfiguration.commonFolder, 'git-hooks');
278276
const hookDestination: string | undefined = Git.getHooksFolder();
@@ -300,6 +298,18 @@ export class InstallManager {
300298
}
301299
}
302300

301+
const approvedPackagesChecker: ApprovedPackagesChecker = new ApprovedPackagesChecker(this._rushConfiguration);
302+
if (approvedPackagesChecker.approvedPackagesFilesAreOutOfDate) {
303+
if (this._options.allowShrinkwrapUpdates) {
304+
approvedPackagesChecker.rewriteConfigFiles();
305+
console.log(colors.yellow(
306+
'Approved package files have been updated. These updates should be committed to source control'
307+
));
308+
} else {
309+
throw new Error(`Approved packages files are out-of date. Run "rush update" to update them.`);
310+
}
311+
}
312+
303313
// Ensure that the package manager is installed
304314
return this.ensureLocalPackageManager()
305315
.then(() => {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"comment": "Rush update now prints a message when the approved packages files are out-of-date, and rush install exits with an error if they are out-of-date.",
5+
"packageName": "@microsoft/rush",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush",
10+
"email": "[email protected]"
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"comment": "Include peerDependencies in the approved packages files.",
5+
"packageName": "@microsoft/rush",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush",
10+
"email": "[email protected]"
11+
}

common/reviews/api/rush-lib.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { IPackageJson } from '@microsoft/node-core-library';
1010
export class ApprovedPackagesConfiguration {
1111
constructor(jsonFilename: string);
1212
// (undocumented)
13-
addOrUpdatePackage(packageName: string, reviewCategory: string): void;
13+
addOrUpdatePackage(packageName: string, reviewCategory: string): boolean;
1414
clear(): void;
1515
// (undocumented)
1616
getItemByName(packageName: string): ApprovedPackagesItem | undefined;

0 commit comments

Comments
 (0)