Skip to content

Commit c81518d

Browse files
authored
Merge pull request microsoft#1388 from iclanton/ianc/updates-to-rush-messages
[rush] Update rush NodeJS warning messages and add option to suppress non-LTS warning messages.
2 parents b4641e5 + 68ff2f7 commit c81518d

18 files changed

Lines changed: 359 additions & 93 deletions

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,28 @@
6262
},
6363

6464
/**
65-
* Older releases of the NodeJS engine may be missing features required by your system.
65+
* Older releases of the Node.js engine may be missing features required by your system.
6666
* Other releases may have bugs. In particular, the "latest" version will not be a
6767
* Long Term Support (LTS) version and is likely to have regressions.
6868
*
69-
* Specify a SemVer range to ensure developers use a NodeJS version that is appropriate
69+
* Specify a SemVer range to ensure developers use a Node.js version that is appropriate
7070
* for your repo.
7171
*/
7272
"nodeSupportedVersionRange": ">=10.13.0 <11.0.0",
7373

74+
/**
75+
* Odd-numbered major versions of Node.js are experimental. Even-numbered releases
76+
* spend six months in a stabilization period before the first Long Term Support (LTS) version.
77+
* For example, 8.9.0 was the first LTS version of Node.js 8. Pre-LTS versions are not recommended
78+
* for production usage because they frequently have bugs. They may cause Rush itself
79+
* to malfunction.
80+
*
81+
* Rush normally prints a warning if it detects a pre-LTS Node.js version. If you are testing
82+
* pre-LTS versions in preparation for supporting the first LTS version, you can use this setting
83+
* to disable Rush's warning.
84+
*/
85+
/*[LINE "HYPOTHETICAL"]*/ "suppressNodeLtsWarning": false,
86+
7487
/**
7588
* If you would like the version specifiers for your dependencies to be consistent, then
7689
* uncomment this line. This is effectively similar to running "rush check" before any
@@ -100,7 +113,7 @@
100113
* occasionally, but if that's painful, it's a warning sign that your development style may
101114
* discourage refactoring. Reorganizing the categories should be an enlightening discussion
102115
* that brings people together, and maybe also identifies poor coding practices (e.g. file
103-
* references that reach into other project's folders without using NodeJS module resolution).
116+
* references that reach into other project's folders without using Node.js module resolution).
104117
*
105118
* The defaults are projectFolderMinDepth=1 and projectFolderMaxDepth=2.
106119
*

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

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,27 @@ import { RushCommandLineParser } from '../cli/RushCommandLineParser';
99
import { RushConstants } from '../logic/RushConstants';
1010
import { RushXCommandLine } from '../cli/RushXCommandLine';
1111
import { CommandLineMigrationAdvisor } from '../cli/CommandLineMigrationAdvisor';
12+
import { NodeJsCompatibility } from '../logic/NodeJsCompatibility';
13+
14+
/**
15+
* Options to pass to the rush "launch" functions.
16+
*
17+
* @public
18+
*/
19+
export interface ILaunchOptions {
20+
/**
21+
* True if the tool was invoked from within a project with a rush.json file, otherwise false. We
22+
* consider a project without a rush.json to be "unmanaged" and we'll print that to the command line when
23+
* the tool is executed. This is mainly used for debugging purposes.
24+
*/
25+
isManaged: boolean;
26+
27+
/**
28+
* If true, the wrapper process already printed a warning that the version of Node.js hasn't been tested
29+
* with this version of Rush, so we shouldn't print a similar error.
30+
*/
31+
alreadyReportedNodeTooNewError?: boolean;
32+
}
1233

1334
/**
1435
* General operations for the Rush engine.
@@ -19,40 +40,46 @@ export class Rush {
1940
/**
2041
* This API is used by the `@microsoft/rush` front end to launch the "rush" command-line.
2142
* Third-party tools should not use this API. Instead, they should execute the "rush" binary
22-
* and start a new NodeJS process.
43+
* and start a new Node.js process.
2344
*
2445
* @param launcherVersion - The version of the `@microsoft/rush` wrapper used to call invoke the CLI.
25-
* @param isManaged - True if the tool was invoked from within a project with a rush.json file, otherwise false. We
26-
* consider a project without a rush.json to be "unmanaged" and we'll print that to the command line when
27-
* the tool is executed. This is mainly used for debugging purposes.
46+
*
47+
* @remarks
48+
* Earlier versions of the rush frontend used a different API contract. In the old contract,
49+
* the second argument was the `isManaged` value of the {@see ILaunchOptions} object.
50+
*
51+
* Even though this API isn't documented, it is still supported for legacy compatibility.
2852
*/
29-
public static launch(launcherVersion: string, isManaged: boolean): void {
30-
Rush._printStartupBanner(isManaged);
53+
public static launch(launcherVersion: string, arg: ILaunchOptions): void {
54+
const options: ILaunchOptions = Rush._normalizeLaunchOptions(arg);
55+
56+
Rush._printStartupBanner(options.isManaged);
3157

3258
if (!CommandLineMigrationAdvisor.checkArgv(process.argv)) {
3359
// The migration advisor recognized an obsolete command-line
3460
process.exitCode = 1;
3561
return;
3662
}
3763

38-
const parser: RushCommandLineParser = new RushCommandLineParser();
64+
const parser: RushCommandLineParser = new RushCommandLineParser({
65+
alreadyReportedNodeTooNewError: options.alreadyReportedNodeTooNewError
66+
});
3967
parser.execute().catch(console.error); // CommandLineParser.execute() should never reject the promise
4068
}
4169

4270
/**
4371
* This API is used by the `@microsoft/rush` front end to launch the "rushx" command-line.
4472
* Third-party tools should not use this API. Instead, they should execute the "rushx" binary
45-
* and start a new NodeJS process.
73+
* and start a new Node.js process.
4674
*
4775
* @param launcherVersion - The version of the `@microsoft/rush` wrapper used to call invoke the CLI.
48-
* @param isManaged - True if the tool was invoked from within a project with a rush.json file, otherwise false. We
49-
* consider a project without a rush.json to be "unmanaged" and we'll print that to the command line when
50-
* the tool is executed. This is mainly used for debugging purposes.
5176
*/
52-
public static launchRushX(launcherVersion: string, isManaged: boolean): void {
53-
Rush._printStartupBanner(isManaged);
77+
public static launchRushX(launcherVersion: string, options: ILaunchOptions): void {
78+
options = Rush._normalizeLaunchOptions(options);
5479

55-
RushXCommandLine.launchRushX(launcherVersion, isManaged);
80+
Rush._printStartupBanner(options.isManaged);
81+
82+
RushXCommandLine._launchRushXInternal(launcherVersion, { ...options });
5683
}
5784

5885
/**
@@ -63,11 +90,27 @@ export class Rush {
6390
return PackageJsonLookup.loadOwnPackageJson(__dirname).version;
6491
}
6592

93+
/**
94+
* This function normalizes legacy options to the current {@see ILaunchOptions} object.
95+
*/
96+
private static _normalizeLaunchOptions(arg: ILaunchOptions): ILaunchOptions {
97+
return (typeof arg === 'boolean')
98+
? { isManaged: arg } // In older versions of Rush, this the `launch` functions took a boolean arg for "isManaged"
99+
: arg;
100+
}
101+
66102
private static _printStartupBanner(isManaged: boolean): void {
103+
const nodeVersion: string = process.versions.node;
104+
const nodeReleaseLabel: string = (NodeJsCompatibility.isOddNumberedVersion)
105+
? 'unstable'
106+
: (NodeJsCompatibility.isLtsVersion ? 'LTS' : 'pre-LTS');
107+
67108
console.log(
68109
EOL +
69110
colors.bold(`Rush Multi-Project Build Tool ${Rush.version}` + colors.yellow(isManaged ? '' : ' (unmanaged)')) +
70111
colors.cyan(` - ${RushConstants.rushWebSiteUrl}`) +
112+
EOL +
113+
`Node.js version is ${nodeVersion} (${nodeReleaseLabel})` +
71114
EOL
72115
);
73116
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export interface IRushConfigurationJson {
115115
rushVersion: string;
116116
repository?: IRushRepositoryJson;
117117
nodeSupportedVersionRange?: string;
118+
suppressNodeLtsWarning?: boolean;
118119
projectFolderMinDepth?: number;
119120
projectFolderMaxDepth?: number;
120121
approvedPackagesPolicy?: IApprovedPackagesPolicyJson;
@@ -269,6 +270,7 @@ export class RushConfiguration {
269270
private _projectFolderMinDepth: number;
270271
private _projectFolderMaxDepth: number;
271272
private _ensureConsistentVersions: boolean;
273+
private _suppressNodeLtsWarning: boolean;
272274
private _variants: {
273275
[variantName: string]: boolean;
274276
};
@@ -775,6 +777,21 @@ export class RushConfiguration {
775777
return this._repositoryUrl;
776778
}
777779

780+
/**
781+
* Odd-numbered major versions of Node.js are experimental. Even-numbered releases
782+
* spend six months in a stabilization period before the first Long Term Support (LTS) version.
783+
* For example, 8.9.0 was the first LTS version of Node.js 8. Pre-LTS versions are not recommended
784+
* for production usage because they frequently have bugs. They may cause Rush itself
785+
* to malfunction.
786+
*
787+
* Rush normally prints a warning if it detects a pre-LTS Node.js version. If you are testing
788+
* pre-LTS versions in preparation for supporting the first LTS version, you can use this setting
789+
* to disable Rush's warning.
790+
*/
791+
public get suppressNodeLtsWarning(): boolean {
792+
return this._suppressNodeLtsWarning;
793+
}
794+
778795
/**
779796
* If true, then consistent version specifiers for dependencies will be enforced.
780797
* I.e. "rush check" is run before some commands.
@@ -1016,6 +1033,8 @@ export class RushConfiguration {
10161033
this._rushLinkJsonFilename = path.join(this._commonTempFolder, 'rush-link.json');
10171034
this._currentVariantJsonFilename = path.join(this._commonTempFolder, 'current-variant.json');
10181035

1036+
this._suppressNodeLtsWarning = !!rushConfigurationJson.suppressNodeLtsWarning;
1037+
10191038
this._ensureConsistentVersions = !!rushConfigurationJson.ensureConsistentVersions;
10201039

10211040
this._pnpmOptions = new PnpmOptionsConfiguration(rushConfigurationJson.pnpmOptions || {});

apps/rush-lib/src/cli/RushCommandLineParser.ts

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,25 @@ import { GlobalScriptAction } from './scriptActions/GlobalScriptAction';
3535
import { Telemetry } from '../logic/Telemetry';
3636
import { AlreadyReportedError } from '../utilities/AlreadyReportedError';
3737
import { RushGlobalFolder } from '../api/RushGlobalFolder';
38+
import { NodeJsCompatibility } from '../logic/NodeJsCompatibility';
3839

3940
/**
4041
* Options for `RushCommandLineParser`.
4142
*/
4243
export interface IRushCommandLineParserOptions {
43-
cwd?: string; // Defaults to `cwd`
44+
cwd: string; // Defaults to `cwd`
45+
alreadyReportedNodeTooNewError: boolean;
4446
}
4547

4648
export class RushCommandLineParser extends CommandLineParser {
4749
public telemetry: Telemetry | undefined;
48-
public rushConfiguration: RushConfiguration;
4950
public rushGlobalFolder: RushGlobalFolder;
51+
public rushConfiguration: RushConfiguration;
5052

5153
private _debugParameter: CommandLineFlagParameter;
5254
private _rushOptions: IRushCommandLineParserOptions;
5355

54-
constructor(options?: IRushCommandLineParserOptions) {
56+
constructor(options?: Partial<IRushCommandLineParserOptions>) {
5557
super({
5658
toolFilename: 'rush',
5759
toolDescription: 'Rush makes life easier for JavaScript developers who develop, build, and publish'
@@ -63,10 +65,27 @@ export class RushCommandLineParser extends CommandLineParser {
6365
+ ' automation tools. If you are looking for a proven turnkey solution for monorepo management,'
6466
+ ' Rush is for you.'
6567
});
66-
const optionsIn: IRushCommandLineParserOptions = options || {};
67-
this._rushOptions = {
68-
cwd: optionsIn.cwd || process.cwd()
69-
};
68+
69+
this._rushOptions = this._normalizeOptions(options || {});
70+
71+
try {
72+
const rushJsonFilename: string | undefined = RushConfiguration.tryFindRushJsonLocation({
73+
startingFolder: this._rushOptions.cwd,
74+
showVerbose: true
75+
});
76+
if (rushJsonFilename) {
77+
this.rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFilename);
78+
}
79+
} catch (error) {
80+
this._reportErrorAndSetExitCode(error);
81+
}
82+
83+
NodeJsCompatibility.warnAboutCompatibilityIssues({
84+
isRushLib: true,
85+
alreadyReportedNodeTooNewError: this._rushOptions.alreadyReportedNodeTooNewError,
86+
rushConfiguration: this.rushConfiguration
87+
});
88+
7089
this._populateActions();
7190
}
7291

@@ -90,8 +109,8 @@ export class RushCommandLineParser extends CommandLineParser {
90109

91110
protected onExecute(): Promise<void> {
92111
// Defensively set the exit code to 1 so if Rush crashes for whatever reason, we'll have a nonzero exit code.
93-
// For example, NodeJS currently has the inexcusable design of terminating with zero exit code when
94-
// there is an uncaught promise exception. This will supposedly be fixed in NodeJS 9.
112+
// For example, Node.js currently has the inexcusable design of terminating with zero exit code when
113+
// there is an uncaught promise exception. This will supposedly be fixed in Node.js 9.
95114
// Ideally we should do this for all the Rush actions, but "rush build" is the most critical one
96115
// -- if it falsely appears to succeed, we could merge bad PRs, publish empty packages, etc.
97116
process.exitCode = 1;
@@ -108,6 +127,13 @@ export class RushCommandLineParser extends CommandLineParser {
108127
});
109128
}
110129

130+
private _normalizeOptions(options: Partial<IRushCommandLineParserOptions>): IRushCommandLineParserOptions {
131+
return {
132+
cwd: options.cwd || process.cwd(),
133+
alreadyReportedNodeTooNewError: options.alreadyReportedNodeTooNewError || false
134+
};
135+
}
136+
111137
private _wrapOnExecute(): Promise<void> {
112138
try {
113139
if (this.rushConfiguration) {
@@ -125,14 +151,6 @@ export class RushCommandLineParser extends CommandLineParser {
125151

126152
private _populateActions(): void {
127153
try {
128-
const rushJsonFilename: string | undefined = RushConfiguration.tryFindRushJsonLocation({
129-
startingFolder: this._rushOptions.cwd,
130-
showVerbose: true
131-
});
132-
if (rushJsonFilename) {
133-
this.rushConfiguration = RushConfiguration.loadFromConfigurationFile(rushJsonFilename);
134-
}
135-
136154
this.rushGlobalFolder = new RushGlobalFolder();
137155

138156
this.addAction(new AddAction(this));
@@ -163,7 +181,8 @@ export class RushCommandLineParser extends CommandLineParser {
163181
// command-line help
164182
if (this.rushConfiguration) {
165183
const commandLineConfigFile: string = path.join(
166-
this.rushConfiguration.commonRushConfigFolder, RushConstants.commandLineFilename
184+
this.rushConfiguration.commonRushConfigFolder,
185+
RushConstants.commandLineFilename
167186
);
168187

169188
commandLineConfiguration = CommandLineConfiguration.loadFromFileOrDefault(commandLineConfigFile);
@@ -320,7 +339,7 @@ export class RushCommandLineParser extends CommandLineParser {
320339
}
321340

322341
if (this._debugParameter.value) {
323-
// If catchSyncErrors() called this, then show a call stack similar to what NodeJS
342+
// If catchSyncErrors() called this, then show a call stack similar to what Node.js
324343
// would show for an uncaught error
325344
console.error(os.EOL + error.stack);
326345
}

apps/rush-lib/src/cli/RushXCommandLine.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,43 @@ import {
1313
import { Utilities } from '../utilities/Utilities';
1414
import { ProjectCommandSet } from '../logic/ProjectCommandSet';
1515
import { RushConfiguration } from '../api/RushConfiguration';
16+
import { NodeJsCompatibility } from '../logic/NodeJsCompatibility';
17+
18+
/**
19+
* @internal
20+
*/
21+
export interface ILaunchRushXInternalOptions {
22+
isManaged: boolean;
23+
alreadyReportedNodeTooNewError?: boolean;
24+
}
1625

1726
export class RushXCommandLine {
1827
public static launchRushX(launcherVersion: string, isManaged: boolean): void {
19-
// NodeJS can sometimes accidentally terminate with a zero exit code (e.g. for an uncaught
28+
RushXCommandLine._launchRushXInternal(launcherVersion, { isManaged });
29+
}
30+
31+
/**
32+
* @internal
33+
*/
34+
public static _launchRushXInternal(launcherVersion: string, options: ILaunchRushXInternalOptions): void {
35+
// Node.js can sometimes accidentally terminate with a zero exit code (e.g. for an uncaught
2036
// promise exception), so we start with the assumption that the exit code is 1
2137
// and set it to 0 only on success.
2238
process.exitCode = 1;
2339

2440
try {
41+
// Are we in a Rush repo?
42+
let rushConfiguration: RushConfiguration | undefined = undefined;
43+
if (RushConfiguration.tryFindRushJsonLocation()) {
44+
rushConfiguration = RushConfiguration.loadFromDefaultLocation({ showVerbose: true });
45+
}
46+
47+
NodeJsCompatibility.warnAboutCompatibilityIssues({
48+
isRushLib: true,
49+
alreadyReportedNodeTooNewError: !!options.alreadyReportedNodeTooNewError,
50+
rushConfiguration
51+
});
52+
2553
// Find the governing package.json for this folder:
2654
const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup();
2755

@@ -67,12 +95,6 @@ export class RushXCommandLine {
6795
return;
6896
}
6997

70-
// Are we in a Rush repo?
71-
let rushConfiguration: RushConfiguration | undefined = undefined;
72-
if (RushConfiguration.tryFindRushJsonLocation()) {
73-
rushConfiguration = RushConfiguration.loadFromDefaultLocation({ showVerbose: true });
74-
}
75-
7698
console.log('Executing: ' + JSON.stringify(scriptBody) + os.EOL);
7799

78100
const packageFolder: string = path.dirname(packageJsonFilePath);

apps/rush-lib/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,7 @@ export {
7575
VersionPolicyConfiguration
7676
} from './api/VersionPolicyConfiguration';
7777

78-
export { Rush } from './api/Rush';
78+
export {
79+
ILaunchOptions,
80+
Rush
81+
} from './api/Rush';

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ export class InstallManager {
991991
if (FileSystem.exists(lastCheckFile)) {
992992
let cachedResult: boolean | 'error' | undefined = undefined;
993993
try {
994-
// NOTE: mtimeMs is not supported yet in NodeJS 6.x
994+
// NOTE: mtimeMs is not supported yet in Node.js 6.x
995995
const nowMs: number = new Date().getTime();
996996
const ageMs: number = nowMs - FileSystem.getStatistics(lastCheckFile).mtime.getTime();
997997
const HOUR: number = 60 * 60 * 1000;

0 commit comments

Comments
 (0)