Skip to content

Commit 6ef1cf9

Browse files
author
Nicholas Pape
authored
Add "enforceConsistentVersions" configuration which runs "rush check" before certain commands (microsoft#829)
* Initial implementation * Fix small bug * Refactor * Rename to enforceConsistentVersions * changefiles * Add an entry to the rush init's rush.json file * PR Feedback
1 parent 0755a9f commit 6ef1cf9

8 files changed

Lines changed: 104 additions & 42 deletions

File tree

apps/rush-lib/assets/rush-init/rush.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@
5959
*/
6060
"nodeSupportedVersionRange": ">=8.9.4 <9.0.0",
6161

62+
/**
63+
* If you would like the version specifiers for your dependencies to be consistent, then
64+
* uncomment this line. Note this is effectively like running "rush check" before the following:
65+
* rush install, rush update, rush link, rush version, rush publish
66+
* In some cases you may want this turned on, but need to allow some packages to use a different
67+
* version. In those cases, you will need to add an entry to the "allowedAlternateVersions"
68+
* section of the common-versions.json.
69+
*/
70+
/*[LINE "HYPOTHETICAL"]*/ "enforceConsistentVersions": true,
71+
6272
/**
6373
* Large monorepos can become intimidating for newcomers if project folder paths don't follow
6474
* a consistent and recognizable pattern. When the system allows nested folder trees,

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export interface IRushConfigurationJson {
9797
eventHooks?: IEventHooksJson;
9898
hotfixChangeEnabled?: boolean;
9999
pnpmOptions?: IPnpmOptionsJson;
100+
enforceConsistentVersions?: boolean;
100101
}
101102

102103
/**
@@ -171,6 +172,7 @@ export class RushConfiguration {
171172
private _packageManagerToolFilename: string;
172173
private _projectFolderMinDepth: number;
173174
private _projectFolderMaxDepth: number;
175+
private _enforceConsistentVersions: boolean;
174176

175177
// "approvedPackagesPolicy" feature
176178
private _approvedPackagesPolicy: ApprovedPackagesPolicy;
@@ -635,6 +637,14 @@ export class RushConfiguration {
635637
return this._repositoryUrl;
636638
}
637639

640+
/**
641+
* If true, then consistent version specifiers for dependencies will be enforced.
642+
* I.e. "rush check" is run before some commands.
643+
*/
644+
public get enforceConsistentVersions(): boolean {
645+
return this._enforceConsistentVersions;
646+
}
647+
638648
/**
639649
* Indicates whether telemetry collection is enabled for Rush runs.
640650
* @beta
@@ -773,6 +783,8 @@ export class RushConfiguration {
773783

774784
this._rushLinkJsonFilename = path.join(this._commonTempFolder, 'rush-link.json');
775785

786+
this._enforceConsistentVersions = !!rushConfigurationJson.enforceConsistentVersions;
787+
776788
this._pnpmOptions = new PnpmOptionsConfiguration(rushConfigurationJson.pnpmOptions || { });
777789

778790
// TODO: Add an actual "packageManager" field in rush.json

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
22
// See LICENSE in the project root for license information.
33

4+
import * as colors from 'colors';
5+
6+
import { RushConfiguration } from './RushConfiguration';
47
import { RushConfigurationProject } from './RushConfigurationProject';
8+
import { RushConstants } from '../logic/RushConstants';
59

610
/**
711
* @public
@@ -18,6 +22,63 @@ export class VersionMismatchFinder {
1822
private _mismatches: Map<string, Map<string, string[]>>;
1923
private _projects: RushConfigurationProject[];
2024

25+
public static rushCheck(rushConfiguration: RushConfiguration): void {
26+
VersionMismatchFinder._checkForInconsistentVersions(rushConfiguration, true);
27+
}
28+
29+
public static enforceConsistentVersions(rushConfiguration: RushConfiguration): void {
30+
VersionMismatchFinder._checkForInconsistentVersions(rushConfiguration, false);
31+
}
32+
33+
private static _checkForInconsistentVersions(
34+
rushConfiguration: RushConfiguration,
35+
isRushCheckCommand: boolean): void {
36+
37+
if (rushConfiguration.enforceConsistentVersions || isRushCheckCommand) {
38+
// Collect all the preferred versions into a single table
39+
const allPreferredVersions: { [dependency: string]: string } = {};
40+
41+
rushConfiguration.commonVersions.getAllPreferredVersions().forEach((version: string, dependency: string) => {
42+
allPreferredVersions[dependency] = version;
43+
});
44+
45+
// Create a fake project for the purposes of reporting conflicts with preferredVersions
46+
// or xstitchPreferredVersions from common-versions.json
47+
const projects: RushConfigurationProject[] = [...rushConfiguration.projects];
48+
49+
projects.push({
50+
packageName: 'preferred versions from ' + RushConstants.commonVersionsFilename,
51+
packageJson: { dependencies: allPreferredVersions }
52+
} as RushConfigurationProject);
53+
54+
const mismatchFinder: VersionMismatchFinder = new VersionMismatchFinder(
55+
projects,
56+
rushConfiguration.commonVersions.allowedAlternativeVersions
57+
);
58+
59+
// Iterate over the list. For any dependency with mismatching versions, print the projects
60+
mismatchFinder.getMismatches().forEach((dependency: string) => {
61+
console.log(colors.yellow(dependency));
62+
mismatchFinder.getVersionsOfMismatch(dependency)!.forEach((version: string) => {
63+
console.log(` ${version}`);
64+
mismatchFinder.getConsumersOfMismatch(dependency, version)!.forEach((project: string) => {
65+
console.log(` - ${project}`);
66+
});
67+
});
68+
console.log();
69+
});
70+
71+
if (mismatchFinder.numberOfMismatches) {
72+
console.log(colors.red(`Found ${mismatchFinder.numberOfMismatches} mis-matching dependencies!`));
73+
process.exit(1);
74+
} else {
75+
if (isRushCheckCommand) {
76+
console.log(colors.green(`Found no mis-matching dependencies!`));
77+
}
78+
}
79+
}
80+
}
81+
2182
constructor(projects: RushConfigurationProject[], allowedAlternativeVersions?: Map<string, ReadonlyArray<string>>) {
2283
this._projects = projects;
2384
this._mismatches = new Map<string, Map<string, string[]>>();

apps/rush-lib/src/cli/actions/BaseInstallAction.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { PurgeManager } from '../../logic/PurgeManager';
1313
import { SetupChecks } from '../../logic/SetupChecks';
1414
import { StandardScriptUpdater } from '../../logic/StandardScriptUpdater';
1515
import { Stopwatch } from '../../utilities/Stopwatch';
16+
import { VersionMismatchFinder } from '../../api/VersionMismatchFinder';
1617

1718
/**
1819
* This is the common base class for InstallAction and UpdateAction.
@@ -57,6 +58,8 @@ export abstract class BaseInstallAction extends BaseRushAction {
5758
protected abstract buildInstallOptions(): IInstallManagerOptions;
5859

5960
protected run(): Promise<void> {
61+
VersionMismatchFinder.enforceConsistentVersions(this.rushConfiguration);
62+
6063
const stopwatch: Stopwatch = Stopwatch.start();
6164

6265
SetupChecks.validate(this.rushConfiguration);
Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
22
// See LICENSE in the project root for license information.
33

4-
import * as colors from 'colors';
5-
6-
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
7-
import { RushConstants } from '../../logic/RushConstants';
8-
import { VersionMismatchFinder } from '../../api/VersionMismatchFinder';
94
import { RushCommandLineParser } from '../RushCommandLineParser';
105
import { BaseRushAction } from './BaseRushAction';
6+
import { VersionMismatchFinder } from '../../api/VersionMismatchFinder';
117

128
export class CheckAction extends BaseRushAction {
139
constructor(parser: RushCommandLineParser) {
@@ -27,43 +23,7 @@ export class CheckAction extends BaseRushAction {
2723
}
2824

2925
protected run(): Promise<void> {
30-
// Collect all the preferred versions into a single table
31-
const allPreferredVersions: { [dependency: string]: string } = {};
32-
33-
this.rushConfiguration.commonVersions.getAllPreferredVersions().forEach((version: string, dependency: string) => {
34-
allPreferredVersions[dependency] = version;
35-
});
36-
37-
// Create a fake project for the purposes of reporting conflicts with preferredVersions
38-
// or xstitchPreferredVersions from common-versions.json
39-
this.rushConfiguration.projects.push({
40-
packageName: 'preferred versions from ' + RushConstants.commonVersionsFilename,
41-
packageJson: { dependencies: allPreferredVersions }
42-
} as RushConfigurationProject);
43-
44-
const mismatchFinder: VersionMismatchFinder = new VersionMismatchFinder(
45-
this.rushConfiguration.projects,
46-
this.rushConfiguration.commonVersions.allowedAlternativeVersions
47-
);
48-
49-
// Iterate over the list. For any dependency with mismatching versions, print the projects
50-
mismatchFinder.getMismatches().forEach((dependency: string) => {
51-
console.log(colors.yellow(dependency));
52-
mismatchFinder.getVersionsOfMismatch(dependency)!.forEach((version: string) => {
53-
console.log(` ${version}`);
54-
mismatchFinder.getConsumersOfMismatch(dependency, version)!.forEach((project: string) => {
55-
console.log(` - ${project}`);
56-
});
57-
});
58-
console.log();
59-
});
60-
61-
if (mismatchFinder.numberOfMismatches) {
62-
console.log(colors.red(`Found ${mismatchFinder.numberOfMismatches} mis-matching dependencies!`));
63-
process.exit(1);
64-
} else {
65-
console.log(colors.green(`Found no mis-matching dependencies!`));
66-
}
26+
VersionMismatchFinder.rushCheck(this.rushConfiguration);
6727
return Promise.resolve();
6828
}
6929
}

apps/rush-lib/src/schemas/rush.schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
"description": "The minimum folder depth for the projectFolder field. The default value is 1, i.e. no slashes in the path name.",
4242
"type": "number"
4343
},
44+
"enforceConsistentVersions": {
45+
"description": "If true, consistent version specifiers for dependencies will be enforced (i.e. \"rush check\" is run before some commands).",
46+
"type": "boolean"
47+
},
4448
"hotfixChangeEnabled": {
4549
"description": "Allows creation of hotfix changes. This feature is experimental so it is disabled by default.",
4650
"type": "boolean"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"comment": "Add \"enforceConsistentVersions\" configuration which runs \"rush check\" before certain commands",
5+
"packageName": "@microsoft/rush",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush",
10+
"email": "nickpape-msft@users.noreply.github.com"
11+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ class RushConfiguration {
151151
readonly commonScriptsFolder: string;
152152
readonly commonTempFolder: string;
153153
readonly commonVersions: CommonVersionsConfiguration;
154+
readonly enforceConsistentVersions: boolean;
154155
// @beta
155156
readonly eventHooks: EventHooks;
156157
findProjectByShorthandName(shorthandProjectName: string): RushConfigurationProject | undefined;

0 commit comments

Comments
 (0)