Skip to content

Commit

Permalink
feat: add hooks in PhasedScriptAction
Browse files Browse the repository at this point in the history
  • Loading branch information
sherlockfeng committed Mar 18, 2023
1 parent e006e8f commit d58485c
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "add hooks to PhasedCommandHooks to give user the operation records",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
2 changes: 2 additions & 0 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,9 @@ export abstract class PackageManagerOptionsConfigurationBase implements IPackage
// @alpha
export class PhasedCommandHooks {
readonly afterExecuteOperations: AsyncSeriesHook<[IExecutionResult, ICreateOperationsContext]>;
readonly beforeExecuteOperations: AsyncSeriesHook<[Map<Operation, IOperationExecutionResult>]>;
readonly createOperations: AsyncSeriesWaterfallHook<[Set<Operation>, ICreateOperationsContext]>;
readonly onOperationStatusChanged: SyncHook<[IOperationExecutionResult]>;
readonly waitingForChanges: SyncHook<void>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { BuildCacheConfiguration } from '../../api/BuildCacheConfiguration';
import { SelectionParameterSet } from '../parsing/SelectionParameterSet';
import type { IPhase, IPhasedCommandConfig } from '../../api/CommandLineConfiguration';
import { Operation } from '../../logic/operations/Operation';
import { OperationExecutionRecord } from '../../logic/operations/OperationExecutionRecord';
import { PhasedOperationPlugin } from '../../logic/operations/PhasedOperationPlugin';
import { ShellOperationRunnerPlugin } from '../../logic/operations/ShellOperationRunnerPlugin';
import { Event } from '../../api/EventHooks';
Expand Down Expand Up @@ -340,7 +341,13 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
quietMode: isQuietMode,
debugMode: this.parser.isDebug,
parallelism,
changedProjectsOnly
changedProjectsOnly,
beforeExecuteOperations: async (records: Map<Operation, OperationExecutionRecord>) => {
await this.hooks.beforeExecuteOperations.promise(records);
},
onOperationStatusChanged: (record: OperationExecutionRecord) => {
this.hooks.onOperationStatusChanged.call(record);
}
};

const internalOptions: IRunPhasesOptions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export interface IOperationExecutionManagerOptions {
parallelism: number;
changedProjectsOnly: boolean;
destination?: TerminalWritable;

onOperationStatusChanged?: (record: OperationExecutionRecord) => void;
beforeExecuteOperations?: (records: Map<Operation, OperationExecutionRecord>) => Promise<void>;
}

/**
Expand All @@ -44,20 +47,35 @@ export class OperationExecutionManager {

private readonly _terminal: CollatedTerminal;

private readonly _onOperationStatusChanged?: (record: OperationExecutionRecord) => void;
private readonly _beforeExecuteOperations?: (
records: Map<Operation, OperationExecutionRecord>
) => Promise<void>;

// Variables for current status
private _hasAnyFailures: boolean;
private _hasAnyNonAllowedWarnings: boolean;
private _completedOperations: number;

public constructor(operations: Set<Operation>, options: IOperationExecutionManagerOptions) {
const { quietMode, debugMode, parallelism, changedProjectsOnly } = options;
const {
quietMode,
debugMode,
parallelism,
changedProjectsOnly,
onOperationStatusChanged,
beforeExecuteOperations
} = options;
this._completedOperations = 0;
this._quietMode = quietMode;
this._hasAnyFailures = false;
this._hasAnyNonAllowedWarnings = false;
this._changedProjectsOnly = changedProjectsOnly;
this._parallelism = parallelism;

this._beforeExecuteOperations = beforeExecuteOperations;
this._onOperationStatusChanged = onOperationStatusChanged;

// TERMINAL PIPELINE:
//
// streamCollator --> colorsNewlinesTransform --> StdioWritable
Expand All @@ -77,6 +95,7 @@ export class OperationExecutionManager {
// Convert the developer graph to the mutable execution graph
const executionRecordContext: IOperationExecutionRecordContext = {
streamCollator: this._streamCollator,
onOperationStatusChanged,
debugMode,
quietMode
};
Expand Down Expand Up @@ -183,6 +202,8 @@ export class OperationExecutionManager {
prioritySort
);

await this._beforeExecuteOperations?.(this._executionRecords);

// This function is a callback because it may write to the collatedWriter before
// operation.executeAsync returns (and cleans up the writer)
const onOperationComplete: (record: OperationExecutionRecord) => void = (
Expand Down Expand Up @@ -250,6 +271,7 @@ export class OperationExecutionManager {
terminal.writeStdoutLine(`"${blockedRecord.name}" is blocked by "${name}".`);
}
blockedRecord.status = OperationStatus.Blocked;
this._onOperationStatusChanged?.(blockedRecord);

for (const dependent of blockedRecord.consumers) {
blockedQueue.add(dependent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { OperationMetadataManager } from './OperationMetadataManager';

export interface IOperationExecutionRecordContext {
streamCollator: StreamCollator;
onOperationStatusChanged?: (record: OperationExecutionRecord) => void;

debugMode: boolean;
quietMode: boolean;
Expand Down Expand Up @@ -135,6 +136,7 @@ export class OperationExecutionRecord implements IOperationRunnerContext {
public async executeAsync(onResult: (record: OperationExecutionRecord) => void): Promise<void> {
this.status = OperationStatus.Executing;
this.stopwatch.start();
this._context.onOperationStatusChanged?.(this);

try {
this.status = await this.runner.executeAsync(this);
Expand All @@ -149,6 +151,7 @@ export class OperationExecutionRecord implements IOperationRunnerContext {
this._collatedWriter?.close();
this.stdioSummarizer.close();
this.stopwatch.stop();
this._context.onOperationStatusChanged?.(this);
}
}
}
15 changes: 14 additions & 1 deletion libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { RushConfigurationProject } from '../api/RushConfigurationProject';

import type { Operation } from '../logic/operations/Operation';
import type { ProjectChangeAnalyzer } from '../logic/ProjectChangeAnalyzer';
import { IExecutionResult } from '../logic/operations/IOperationExecutionResult';
import { IExecutionResult, IOperationExecutionResult } from '../logic/operations/IOperationExecutionResult';

/**
* A plugin that interacts with a phased commands.
Expand Down Expand Up @@ -87,6 +87,19 @@ export class PhasedCommandHooks {
public readonly createOperations: AsyncSeriesWaterfallHook<[Set<Operation>, ICreateOperationsContext]> =
new AsyncSeriesWaterfallHook(['operations', 'context'], 'createOperations');

/**
* Hook invoked before operation start
* Hook is series for stable output.
*/
public readonly beforeExecuteOperations: AsyncSeriesHook<[Map<Operation, IOperationExecutionResult>]> =
new AsyncSeriesHook(['records']);

/**
* Hook invoked when operation statue changed
* Hook is series for stable output.
*/
public readonly onOperationStatusChanged: SyncHook<[IOperationExecutionResult]> = new SyncHook(['record']);

/**
* Hook invoked after executing a set of operations.
* Use the context to distinguish between the initial run and phased runs.
Expand Down

0 comments on commit d58485c

Please sign in to comment.