Skip to content

Commit ad9aba9

Browse files
authored
[rush] Add --node-diagnostic-dir=DIR parameter (#5099)
* [rush] Add `createEnvironmentForOperation` hook * [rush] Add --node-diagnostic-dir parameter * Address PR feedback * fixup --------- Co-authored-by: David Michon <[email protected]>
1 parent f352d59 commit ad9aba9

15 files changed

+277
-90
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/rush",
5+
"comment": "Add a new command line parameter `--node-diagnostic-dir=DIR` to phased commands that, when specified, tells all child build processes to write NodeJS diagnostics into `${DIR}/${packageName}/${phaseIdentifier}`. This is useful if `--cpu-prof` or `--heap-prof` are enabled, to avoid polluting workspace folders.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush"
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/rush",
5+
"comment": "Add a new phased command hook `createEnvironmentForOperation` that can be used to customize the environment variables passed to individual operation subprocesses. This may be used to, for example, customize `NODE_OPTIONS` to pass `--diagnostic-dir` or other such parameters.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@microsoft/rush"
10+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { LookupByPath } from '@rushstack/lookup-by-path';
2525
import { PackageNameParser } from '@rushstack/node-core-library';
2626
import type { StdioSummarizer } from '@rushstack/terminal';
2727
import { SyncHook } from 'tapable';
28+
import { SyncWaterfallHook } from 'tapable';
2829
import { Terminal } from '@rushstack/terminal';
2930

3031
// @public
@@ -644,6 +645,7 @@ export interface IOperationRunner {
644645
export interface IOperationRunnerContext {
645646
collatedWriter: CollatedWriter;
646647
debugMode: boolean;
648+
environment: IEnvironment | undefined;
647649
error?: Error;
648650
// @internal
649651
_operationMetadataManager?: _OperationMetadataManager;
@@ -1078,6 +1080,10 @@ export class PhasedCommandHooks {
10781080
IExecuteOperationsContext
10791081
]>;
10801082
readonly beforeLog: SyncHook<ITelemetryData, void>;
1083+
readonly createEnvironmentForOperation: SyncWaterfallHook<[
1084+
IEnvironment,
1085+
IOperationRunnerContext & IOperationExecutionResult
1086+
]>;
10811087
readonly createOperations: AsyncSeriesWaterfallHook<[Set<Operation>, ICreateOperationsContext]>;
10821088
readonly onOperationStatusChanged: SyncHook<[IOperationExecutionResult]>;
10831089
readonly shutdownAsync: AsyncParallelHook<void>;

libraries/rush-lib/src/api/test/__snapshots__/RushCommandLine.test.ts.snap

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,14 @@ Object {
12441244
"required": false,
12451245
"shortName": undefined,
12461246
},
1247+
Object {
1248+
"description": "Specifies the directory where Node.js diagnostic reports will be written. This directory will contain a subdirectory for each project and phase.",
1249+
"environmentVariable": undefined,
1250+
"kind": "String",
1251+
"longName": "--node-diagnostic-dir",
1252+
"required": false,
1253+
"shortName": undefined,
1254+
},
12471255
Object {
12481256
"description": "Selects a single instead of the default locale (en-us) for non-ship builds or all locales for ship builds.",
12491257
"environmentVariable": undefined,
@@ -1382,6 +1390,14 @@ Object {
13821390
"required": false,
13831391
"shortName": undefined,
13841392
},
1393+
Object {
1394+
"description": "Specifies the directory where Node.js diagnostic reports will be written. This directory will contain a subdirectory for each project and phase.",
1395+
"environmentVariable": undefined,
1396+
"kind": "String",
1397+
"longName": "--node-diagnostic-dir",
1398+
"required": false,
1399+
"shortName": undefined,
1400+
},
13851401
Object {
13861402
"description": "Perform a production build, including minification and localization steps",
13871403
"environmentVariable": undefined,
@@ -1507,6 +1523,14 @@ Object {
15071523
"required": false,
15081524
"shortName": undefined,
15091525
},
1526+
Object {
1527+
"description": "Specifies the directory where Node.js diagnostic reports will be written. This directory will contain a subdirectory for each project and phase.",
1528+
"environmentVariable": undefined,
1529+
"kind": "String",
1530+
"longName": "--node-diagnostic-dir",
1531+
"required": false,
1532+
"shortName": undefined,
1533+
},
15101534
Object {
15111535
"description": "Perform a production build, including minification and localization steps",
15121536
"environmentVariable": undefined,

libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import { FlagFile } from '../../api/FlagFile';
5757
import { WeightedOperationPlugin } from '../../logic/operations/WeightedOperationPlugin';
5858
import { getVariantAsync, VARIANT_PARAMETER } from '../../api/Variants';
5959
import { Selection } from '../../logic/Selection';
60+
import { NodeDiagnosticDirPlugin } from '../../logic/operations/NodeDiagnosticDirPlugin';
6061

6162
/**
6263
* Constructor parameters for PhasedScriptAction.
@@ -153,6 +154,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
153154
private readonly _installParameter: CommandLineFlagParameter | undefined;
154155
private readonly _variantParameter: CommandLineStringParameter | undefined;
155156
private readonly _noIPCParameter: CommandLineFlagParameter | undefined;
157+
private readonly _nodeDiagnosticDirParameter: CommandLineStringParameter;
156158

157159
public constructor(options: IPhasedScriptActionOptions) {
158160
super(options);
@@ -284,6 +286,14 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
284286
});
285287
}
286288

289+
this._nodeDiagnosticDirParameter = this.defineStringParameter({
290+
parameterLongName: '--node-diagnostic-dir',
291+
argumentName: 'DIRECTORY',
292+
description:
293+
'Specifies the directory where Node.js diagnostic reports will be written. ' +
294+
'This directory will contain a subdirectory for each project and phase.'
295+
});
296+
287297
this.defineScriptParameters();
288298

289299
for (const [{ associatedPhases }, tsCommandLineParameter] of this.customParameters) {
@@ -366,6 +376,13 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
366376
new ConsoleTimelinePlugin(terminal).apply(this.hooks);
367377
}
368378

379+
const diagnosticDir: string | undefined = this._nodeDiagnosticDirParameter.value;
380+
if (diagnosticDir) {
381+
new NodeDiagnosticDirPlugin({
382+
diagnosticDir
383+
}).apply(this.hooks);
384+
}
385+
369386
// Enable the standard summary
370387
new OperationResultSummarizerPlugin(terminal).apply(this.hooks);
371388

@@ -507,6 +524,11 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
507524
afterExecuteOperationAsync: async (record: OperationExecutionRecord) => {
508525
await this.hooks.afterExecuteOperation.promise(record);
509526
},
527+
createEnvironmentForOperation: this.hooks.createEnvironmentForOperation.isUsed()
528+
? (record: OperationExecutionRecord) => {
529+
return this.hooks.createEnvironmentForOperation.call({ ...process.env }, record);
530+
}
531+
: undefined,
510532
onOperationStatusChangedAsync: (record: OperationExecutionRecord) => {
511533
this.hooks.onOperationStatusChanged.call(record);
512534
}

libraries/rush-lib/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ exports[`CommandLineHelp prints the help for each action: build 1`] = `
155155
[-i PROJECT] [-I PROJECT]
156156
[--to-version-policy VERSION_POLICY_NAME]
157157
[--from-version-policy VERSION_POLICY_NAME] [-v] [-c]
158-
[--ignore-hooks] [-s] [-m]
158+
[--ignore-hooks] [--node-diagnostic-dir DIRECTORY] [-s] [-m]
159159
160160
161161
This command is similar to \\"rush rebuild\\", except that \\"rush build\\" performs
@@ -281,6 +281,10 @@ Optional arguments:
281281
--ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined
282282
in rush.json. Make sure you know what you are
283283
skipping.
284+
--node-diagnostic-dir DIRECTORY
285+
Specifies the directory where Node.js diagnostic
286+
reports will be written. This directory will contain
287+
a subdirectory for each project and phase.
284288
-s, --ship Perform a production build, including minification
285289
and localization steps
286290
-m, --minimal Perform a fast build, which disables certain tasks
@@ -432,7 +436,7 @@ exports[`CommandLineHelp prints the help for each action: import-strings 1`] = `
432436
[-i PROJECT] [-I PROJECT]
433437
[--to-version-policy VERSION_POLICY_NAME]
434438
[--from-version-policy VERSION_POLICY_NAME] [-v]
435-
[--ignore-hooks]
439+
[--ignore-hooks] [--node-diagnostic-dir DIRECTORY]
436440
[--locale {en-us,fr-fr,es-es,zh-cn}]
437441
438442
@@ -541,6 +545,10 @@ Optional arguments:
541545
--ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined
542546
in rush.json. Make sure you know what you are
543547
skipping.
548+
--node-diagnostic-dir DIRECTORY
549+
Specifies the directory where Node.js diagnostic
550+
reports will be written. This directory will contain
551+
a subdirectory for each project and phase.
544552
--locale {en-us,fr-fr,es-es,zh-cn}
545553
Selects a single instead of the default locale
546554
(en-us) for non-ship builds or all locales for ship
@@ -1030,7 +1038,8 @@ exports[`CommandLineHelp prints the help for each action: rebuild 1`] = `
10301038
[-i PROJECT] [-I PROJECT]
10311039
[--to-version-policy VERSION_POLICY_NAME]
10321040
[--from-version-policy VERSION_POLICY_NAME] [-v]
1033-
[--ignore-hooks] [-s] [-m]
1041+
[--ignore-hooks] [--node-diagnostic-dir DIRECTORY] [-s]
1042+
[-m]
10341043
10351044
10361045
This command assumes that the package.json file for each project contains a
@@ -1144,6 +1153,10 @@ Optional arguments:
11441153
--ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined
11451154
in rush.json. Make sure you know what you are
11461155
skipping.
1156+
--node-diagnostic-dir DIRECTORY
1157+
Specifies the directory where Node.js diagnostic
1158+
reports will be written. This directory will contain
1159+
a subdirectory for each project and phase.
11471160
-s, --ship Perform a production build, including minification
11481161
and localization steps
11491162
-m, --minimal Perform a fast build, which disables certain tasks

libraries/rush-lib/src/logic/operations/IOperationRunner.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { CollatedWriter } from '@rushstack/stream-collator';
77
import type { OperationStatus } from './OperationStatus';
88
import type { OperationMetadataManager } from './OperationMetadataManager';
99
import type { IStopwatchResult } from '../../utilities/Stopwatch';
10+
import type { IEnvironment } from '../../utilities/Utilities';
1011

1112
/**
1213
* Information passed to the executing `IOperationRunner`
@@ -44,6 +45,12 @@ export interface IOperationRunnerContext {
4445
*/
4546
status: OperationStatus;
4647

48+
/**
49+
* The environment in which the operation is being executed.
50+
* A return value of `undefined` indicates that it should inherit the environment from the parent process.
51+
*/
52+
environment: IEnvironment | undefined;
53+
4754
/**
4855
* Error which occurred while executing this operation, this is stored in case we need
4956
* it later (for example to re-print errors at end of execution).

libraries/rush-lib/src/logic/operations/IPCOperationRunner.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { TerminalProviderSeverity, type ITerminal, type ITerminalProvider } from
1515

1616
import type { IPhase } from '../../api/CommandLineConfiguration';
1717
import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration';
18-
import type { RushConfiguration } from '../../api/RushConfiguration';
1918
import type { RushConfigurationProject } from '../../api/RushConfigurationProject';
2019
import { Utilities } from '../../utilities/Utilities';
2120
import type { IOperationRunner, IOperationRunnerContext } from './IOperationRunner';
@@ -26,7 +25,8 @@ export interface IIPCOperationRunnerOptions {
2625
phase: IPhase;
2726
project: RushConfigurationProject;
2827
name: string;
29-
shellCommand: string;
28+
commandToRun: string;
29+
commandForHash: string;
3030
persist: boolean;
3131
requestRun: (requestor?: string) => void;
3232
}
@@ -53,9 +53,9 @@ export class IPCOperationRunner implements IOperationRunner {
5353
public readonly silent: boolean = false;
5454
public readonly warningsAreAllowed: boolean;
5555

56-
private readonly _rushConfiguration: RushConfiguration;
57-
private readonly _shellCommand: string;
58-
private readonly _workingDirectory: string;
56+
private readonly _rushProject: RushConfigurationProject;
57+
private readonly _commandToRun: string;
58+
private readonly _commandForHash: string;
5959
private readonly _persist: boolean;
6060
private readonly _requestRun: (requestor?: string) => void;
6161

@@ -68,9 +68,10 @@ export class IPCOperationRunner implements IOperationRunner {
6868
EnvironmentConfiguration.allowWarningsInSuccessfulBuild ||
6969
options.phase.allowWarningsOnSuccess ||
7070
false;
71-
this._rushConfiguration = options.project.rushConfiguration;
72-
this._shellCommand = options.shellCommand;
73-
this._workingDirectory = options.project.projectFolder;
71+
this._rushProject = options.project;
72+
this._commandToRun = options.commandToRun;
73+
this._commandForHash = options.commandForHash;
74+
7475
this._persist = options.persist;
7576
this._requestRun = options.requestRun;
7677
}
@@ -81,18 +82,23 @@ export class IPCOperationRunner implements IOperationRunner {
8182
let isConnected: boolean = false;
8283
if (!this._ipcProcess || typeof this._ipcProcess.exitCode === 'number') {
8384
// Run the operation
84-
terminal.writeLine('Invoking: ' + this._shellCommand);
85+
terminal.writeLine('Invoking: ' + this._commandToRun);
86+
87+
const { rushConfiguration, projectFolder } = this._rushProject;
88+
89+
const { environment: initialEnvironment } = context;
8590

86-
this._ipcProcess = Utilities.executeLifecycleCommandAsync(this._shellCommand, {
87-
rushConfiguration: this._rushConfiguration,
88-
workingDirectory: this._workingDirectory,
89-
initCwd: this._rushConfiguration.commonTempFolder,
91+
this._ipcProcess = Utilities.executeLifecycleCommandAsync(this._commandToRun, {
92+
rushConfiguration,
93+
workingDirectory: projectFolder,
94+
initCwd: rushConfiguration.commonTempFolder,
9095
handleOutput: true,
9196
environmentPathOptions: {
9297
includeProjectBin: true
9398
},
9499
ipc: true,
95-
connectSubprocessTerminator: true
100+
connectSubprocessTerminator: true,
101+
initialEnvironment
96102
});
97103

98104
let resolveReadyPromise!: () => void;
@@ -193,7 +199,7 @@ export class IPCOperationRunner implements IOperationRunner {
193199
}
194200

195201
public getConfigHash(): string {
196-
return this._shellCommand;
202+
return this._commandForHash;
197203
}
198204

199205
public async shutdownAsync(): Promise<void> {

libraries/rush-lib/src/logic/operations/IPCOperationRunnerPlugin.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ export class IPCOperationRunnerPlugin implements IPhasedCommandPlugin {
6969
continue;
7070
}
7171

72+
// This is the command that will be used to identify the cache entry for this operation, to allow
73+
// for this operation (or downstream operations) to be restored from the build cache.
74+
const commandForHash: string | undefined = phase.shellCommand ?? scripts?.[phaseName];
75+
7276
const customParameterValues: ReadonlyArray<string> = getCustomParameterValuesForPhase(phase);
7377
const commandToRun: string = formatCommand(rawScript, customParameterValues);
7478

@@ -79,7 +83,8 @@ export class IPCOperationRunnerPlugin implements IPhasedCommandPlugin {
7983
phase,
8084
project,
8185
name: operationName,
82-
shellCommand: commandToRun,
86+
commandToRun,
87+
commandForHash,
8388
persist: true,
8489
requestRun: (requestor?: string) => {
8590
const operationState: IOperationExecutionResult | undefined =

0 commit comments

Comments
 (0)