Skip to content

Commit

Permalink
[rush] Add --node-diagnostic-dir=DIR parameter (#5099)
Browse files Browse the repository at this point in the history
* [rush] Add `createEnvironmentForOperation` hook

* [rush] Add --node-diagnostic-dir parameter

* Address PR feedback

* fixup

---------

Co-authored-by: David Michon <[email protected]>
  • Loading branch information
dmichon-msft and dmichon-msft authored Feb 7, 2025
1 parent f352d59 commit ad9aba9
Show file tree
Hide file tree
Showing 15 changed files with 277 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"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.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"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.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
6 changes: 6 additions & 0 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { LookupByPath } from '@rushstack/lookup-by-path';
import { PackageNameParser } from '@rushstack/node-core-library';
import type { StdioSummarizer } from '@rushstack/terminal';
import { SyncHook } from 'tapable';
import { SyncWaterfallHook } from 'tapable';
import { Terminal } from '@rushstack/terminal';

// @public
Expand Down Expand Up @@ -644,6 +645,7 @@ export interface IOperationRunner {
export interface IOperationRunnerContext {
collatedWriter: CollatedWriter;
debugMode: boolean;
environment: IEnvironment | undefined;
error?: Error;
// @internal
_operationMetadataManager?: _OperationMetadataManager;
Expand Down Expand Up @@ -1078,6 +1080,10 @@ export class PhasedCommandHooks {
IExecuteOperationsContext
]>;
readonly beforeLog: SyncHook<ITelemetryData, void>;
readonly createEnvironmentForOperation: SyncWaterfallHook<[
IEnvironment,
IOperationRunnerContext & IOperationExecutionResult
]>;
readonly createOperations: AsyncSeriesWaterfallHook<[Set<Operation>, ICreateOperationsContext]>;
readonly onOperationStatusChanged: SyncHook<[IOperationExecutionResult]>;
readonly shutdownAsync: AsyncParallelHook<void>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,14 @@ Object {
"required": false,
"shortName": undefined,
},
Object {
"description": "Specifies the directory where Node.js diagnostic reports will be written. This directory will contain a subdirectory for each project and phase.",
"environmentVariable": undefined,
"kind": "String",
"longName": "--node-diagnostic-dir",
"required": false,
"shortName": undefined,
},
Object {
"description": "Selects a single instead of the default locale (en-us) for non-ship builds or all locales for ship builds.",
"environmentVariable": undefined,
Expand Down Expand Up @@ -1382,6 +1390,14 @@ Object {
"required": false,
"shortName": undefined,
},
Object {
"description": "Specifies the directory where Node.js diagnostic reports will be written. This directory will contain a subdirectory for each project and phase.",
"environmentVariable": undefined,
"kind": "String",
"longName": "--node-diagnostic-dir",
"required": false,
"shortName": undefined,
},
Object {
"description": "Perform a production build, including minification and localization steps",
"environmentVariable": undefined,
Expand Down Expand Up @@ -1507,6 +1523,14 @@ Object {
"required": false,
"shortName": undefined,
},
Object {
"description": "Specifies the directory where Node.js diagnostic reports will be written. This directory will contain a subdirectory for each project and phase.",
"environmentVariable": undefined,
"kind": "String",
"longName": "--node-diagnostic-dir",
"required": false,
"shortName": undefined,
},
Object {
"description": "Perform a production build, including minification and localization steps",
"environmentVariable": undefined,
Expand Down
22 changes: 22 additions & 0 deletions libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { FlagFile } from '../../api/FlagFile';
import { WeightedOperationPlugin } from '../../logic/operations/WeightedOperationPlugin';
import { getVariantAsync, VARIANT_PARAMETER } from '../../api/Variants';
import { Selection } from '../../logic/Selection';
import { NodeDiagnosticDirPlugin } from '../../logic/operations/NodeDiagnosticDirPlugin';

/**
* Constructor parameters for PhasedScriptAction.
Expand Down Expand Up @@ -153,6 +154,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
private readonly _installParameter: CommandLineFlagParameter | undefined;
private readonly _variantParameter: CommandLineStringParameter | undefined;
private readonly _noIPCParameter: CommandLineFlagParameter | undefined;
private readonly _nodeDiagnosticDirParameter: CommandLineStringParameter;

public constructor(options: IPhasedScriptActionOptions) {
super(options);
Expand Down Expand Up @@ -284,6 +286,14 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
});
}

this._nodeDiagnosticDirParameter = this.defineStringParameter({
parameterLongName: '--node-diagnostic-dir',
argumentName: 'DIRECTORY',
description:
'Specifies the directory where Node.js diagnostic reports will be written. ' +
'This directory will contain a subdirectory for each project and phase.'
});

this.defineScriptParameters();

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

const diagnosticDir: string | undefined = this._nodeDiagnosticDirParameter.value;
if (diagnosticDir) {
new NodeDiagnosticDirPlugin({
diagnosticDir
}).apply(this.hooks);
}

// Enable the standard summary
new OperationResultSummarizerPlugin(terminal).apply(this.hooks);

Expand Down Expand Up @@ -507,6 +524,11 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
afterExecuteOperationAsync: async (record: OperationExecutionRecord) => {
await this.hooks.afterExecuteOperation.promise(record);
},
createEnvironmentForOperation: this.hooks.createEnvironmentForOperation.isUsed()
? (record: OperationExecutionRecord) => {
return this.hooks.createEnvironmentForOperation.call({ ...process.env }, record);
}
: undefined,
onOperationStatusChangedAsync: (record: OperationExecutionRecord) => {
this.hooks.onOperationStatusChanged.call(record);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ exports[`CommandLineHelp prints the help for each action: build 1`] = `
[-i PROJECT] [-I PROJECT]
[--to-version-policy VERSION_POLICY_NAME]
[--from-version-policy VERSION_POLICY_NAME] [-v] [-c]
[--ignore-hooks] [-s] [-m]
[--ignore-hooks] [--node-diagnostic-dir DIRECTORY] [-s] [-m]
This command is similar to \\"rush rebuild\\", except that \\"rush build\\" performs
Expand Down Expand Up @@ -281,6 +281,10 @@ Optional arguments:
--ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined
in rush.json. Make sure you know what you are
skipping.
--node-diagnostic-dir DIRECTORY
Specifies the directory where Node.js diagnostic
reports will be written. This directory will contain
a subdirectory for each project and phase.
-s, --ship Perform a production build, including minification
and localization steps
-m, --minimal Perform a fast build, which disables certain tasks
Expand Down Expand Up @@ -432,7 +436,7 @@ exports[`CommandLineHelp prints the help for each action: import-strings 1`] = `
[-i PROJECT] [-I PROJECT]
[--to-version-policy VERSION_POLICY_NAME]
[--from-version-policy VERSION_POLICY_NAME] [-v]
[--ignore-hooks]
[--ignore-hooks] [--node-diagnostic-dir DIRECTORY]
[--locale {en-us,fr-fr,es-es,zh-cn}]
Expand Down Expand Up @@ -541,6 +545,10 @@ Optional arguments:
--ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined
in rush.json. Make sure you know what you are
skipping.
--node-diagnostic-dir DIRECTORY
Specifies the directory where Node.js diagnostic
reports will be written. This directory will contain
a subdirectory for each project and phase.
--locale {en-us,fr-fr,es-es,zh-cn}
Selects a single instead of the default locale
(en-us) for non-ship builds or all locales for ship
Expand Down Expand Up @@ -1030,7 +1038,8 @@ exports[`CommandLineHelp prints the help for each action: rebuild 1`] = `
[-i PROJECT] [-I PROJECT]
[--to-version-policy VERSION_POLICY_NAME]
[--from-version-policy VERSION_POLICY_NAME] [-v]
[--ignore-hooks] [-s] [-m]
[--ignore-hooks] [--node-diagnostic-dir DIRECTORY] [-s]
[-m]
This command assumes that the package.json file for each project contains a
Expand Down Expand Up @@ -1144,6 +1153,10 @@ Optional arguments:
--ignore-hooks Skips execution of the \\"eventHooks\\" scripts defined
in rush.json. Make sure you know what you are
skipping.
--node-diagnostic-dir DIRECTORY
Specifies the directory where Node.js diagnostic
reports will be written. This directory will contain
a subdirectory for each project and phase.
-s, --ship Perform a production build, including minification
and localization steps
-m, --minimal Perform a fast build, which disables certain tasks
Expand Down
7 changes: 7 additions & 0 deletions libraries/rush-lib/src/logic/operations/IOperationRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { CollatedWriter } from '@rushstack/stream-collator';
import type { OperationStatus } from './OperationStatus';
import type { OperationMetadataManager } from './OperationMetadataManager';
import type { IStopwatchResult } from '../../utilities/Stopwatch';
import type { IEnvironment } from '../../utilities/Utilities';

/**
* Information passed to the executing `IOperationRunner`
Expand Down Expand Up @@ -44,6 +45,12 @@ export interface IOperationRunnerContext {
*/
status: OperationStatus;

/**
* The environment in which the operation is being executed.
* A return value of `undefined` indicates that it should inherit the environment from the parent process.
*/
environment: IEnvironment | undefined;

/**
* Error which occurred while executing this operation, this is stored in case we need
* it later (for example to re-print errors at end of execution).
Expand Down
36 changes: 21 additions & 15 deletions libraries/rush-lib/src/logic/operations/IPCOperationRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { TerminalProviderSeverity, type ITerminal, type ITerminalProvider } from

import type { IPhase } from '../../api/CommandLineConfiguration';
import { EnvironmentConfiguration } from '../../api/EnvironmentConfiguration';
import type { RushConfiguration } from '../../api/RushConfiguration';
import type { RushConfigurationProject } from '../../api/RushConfigurationProject';
import { Utilities } from '../../utilities/Utilities';
import type { IOperationRunner, IOperationRunnerContext } from './IOperationRunner';
Expand All @@ -26,7 +25,8 @@ export interface IIPCOperationRunnerOptions {
phase: IPhase;
project: RushConfigurationProject;
name: string;
shellCommand: string;
commandToRun: string;
commandForHash: string;
persist: boolean;
requestRun: (requestor?: string) => void;
}
Expand All @@ -53,9 +53,9 @@ export class IPCOperationRunner implements IOperationRunner {
public readonly silent: boolean = false;
public readonly warningsAreAllowed: boolean;

private readonly _rushConfiguration: RushConfiguration;
private readonly _shellCommand: string;
private readonly _workingDirectory: string;
private readonly _rushProject: RushConfigurationProject;
private readonly _commandToRun: string;
private readonly _commandForHash: string;
private readonly _persist: boolean;
private readonly _requestRun: (requestor?: string) => void;

Expand All @@ -68,9 +68,10 @@ export class IPCOperationRunner implements IOperationRunner {
EnvironmentConfiguration.allowWarningsInSuccessfulBuild ||
options.phase.allowWarningsOnSuccess ||
false;
this._rushConfiguration = options.project.rushConfiguration;
this._shellCommand = options.shellCommand;
this._workingDirectory = options.project.projectFolder;
this._rushProject = options.project;
this._commandToRun = options.commandToRun;
this._commandForHash = options.commandForHash;

this._persist = options.persist;
this._requestRun = options.requestRun;
}
Expand All @@ -81,18 +82,23 @@ export class IPCOperationRunner implements IOperationRunner {
let isConnected: boolean = false;
if (!this._ipcProcess || typeof this._ipcProcess.exitCode === 'number') {
// Run the operation
terminal.writeLine('Invoking: ' + this._shellCommand);
terminal.writeLine('Invoking: ' + this._commandToRun);

const { rushConfiguration, projectFolder } = this._rushProject;

const { environment: initialEnvironment } = context;

this._ipcProcess = Utilities.executeLifecycleCommandAsync(this._shellCommand, {
rushConfiguration: this._rushConfiguration,
workingDirectory: this._workingDirectory,
initCwd: this._rushConfiguration.commonTempFolder,
this._ipcProcess = Utilities.executeLifecycleCommandAsync(this._commandToRun, {
rushConfiguration,
workingDirectory: projectFolder,
initCwd: rushConfiguration.commonTempFolder,
handleOutput: true,
environmentPathOptions: {
includeProjectBin: true
},
ipc: true,
connectSubprocessTerminator: true
connectSubprocessTerminator: true,
initialEnvironment
});

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

public getConfigHash(): string {
return this._shellCommand;
return this._commandForHash;
}

public async shutdownAsync(): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export class IPCOperationRunnerPlugin implements IPhasedCommandPlugin {
continue;
}

// This is the command that will be used to identify the cache entry for this operation, to allow
// for this operation (or downstream operations) to be restored from the build cache.
const commandForHash: string | undefined = phase.shellCommand ?? scripts?.[phaseName];

const customParameterValues: ReadonlyArray<string> = getCustomParameterValuesForPhase(phase);
const commandToRun: string = formatCommand(rawScript, customParameterValues);

Expand All @@ -79,7 +83,8 @@ export class IPCOperationRunnerPlugin implements IPhasedCommandPlugin {
phase,
project,
name: operationName,
shellCommand: commandToRun,
commandToRun,
commandForHash,
persist: true,
requestRun: (requestor?: string) => {
const operationState: IOperationExecutionResult | undefined =
Expand Down
Loading

0 comments on commit ad9aba9

Please sign in to comment.