Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Fix telemetry for \"--changed-projects-only\" when toggled in watch mode.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "(rush-serve-plugin) Support websocket message to enable/disable operations.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
12 changes: 9 additions & 3 deletions libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
private readonly _alwaysInstall: boolean | undefined;
private readonly _knownPhases: ReadonlyMap<string, IPhase>;
private readonly _terminal: ITerminal;
private _changedProjectsOnly: boolean;

private readonly _changedProjectsOnlyParameter: CommandLineFlagParameter | undefined;
private readonly _selectionParameters: SelectionParameterSet;
Expand All @@ -162,8 +163,6 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
private readonly _debugBuildCacheIdsParameter: CommandLineFlagParameter;
private readonly _includePhaseDeps: CommandLineFlagParameter | undefined;

private _changedProjectsOnly: boolean;

public constructor(options: IPhasedScriptActionOptions) {
super(options);
this._enableParallelism = options.enableParallelism;
Expand Down Expand Up @@ -845,7 +844,7 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
// Account for consumer relationships
const executeOperationsContext: IExecuteOperationsContext = {
...initialCreateOperationsContext,
changedProjectsOnly: this._changedProjectsOnly,
changedProjectsOnly: !!this._changedProjectsOnly,
isInitial: false,
inputsSnapshot: state,
projectsInUnknownState: changedProjects,
Expand Down Expand Up @@ -961,6 +960,13 @@ export class PhasedScriptAction extends BaseScriptAction<IPhasedCommandConfig> {
countNoOp: 0
};

const { _changedProjectsOnlyParameter: changedProjectsOnlyParameter } = this;
if (changedProjectsOnlyParameter) {
// Overwrite this value since we allow changing it at runtime.
extraData[changedProjectsOnlyParameter.scopedLongName ?? changedProjectsOnlyParameter.longName] =
this._changedProjectsOnly;
}

if (result) {
const { operationResults } = result;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ const PLUGIN_NAME: 'PhasedOperationPlugin' = 'PhasedOperationPlugin';
export class PhasedOperationPlugin implements IPhasedCommandPlugin {
public apply(hooks: PhasedCommandHooks): void {
hooks.createOperations.tap(PLUGIN_NAME, createOperations);
// Configure operations later.
hooks.createOperations.tap(
{
name: `${PLUGIN_NAME}.Configure`,
stage: 1000
},
configureOperations
);
}
}

Expand All @@ -40,8 +48,6 @@ function createOperations(
}
}

configureOperations(existingOperations, context);

return existingOperations;

// Binds phaseSelection, projectSelection, operations via closure
Expand Down Expand Up @@ -88,7 +94,7 @@ function createOperations(
}
}

function configureOperations(operations: ReadonlySet<Operation>, context: ICreateOperationsContext): void {
function configureOperations(operations: Set<Operation>, context: ICreateOperationsContext): Set<Operation> {
const {
changedProjectsOnly,
projectsInUnknownState: changedProjects,
Expand Down Expand Up @@ -149,6 +155,8 @@ function configureOperations(operations: ReadonlySet<Operation>, context: ICreat
operation.enabled &&= phaseSelection.has(associatedPhase) && projectSelection.has(associatedProject);
}
}

return operations;
}

// Convert the [IPhase, RushConfigurationProject] into a value suitable for use as a Map key
Expand Down
26 changes: 25 additions & 1 deletion rush-plugins/rush-serve-plugin/src/api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,31 @@ export interface IWebSocketSyncCommandMessage {
command: 'sync';
}

/**
* Message received from a WebSocket client to request invalidation of one or more operations.
*/
export interface IWebSocketInvalidateCommandMessage {
command: 'invalidate';
operationNames: string[];
}

/**
* The set of possible operation enabled states.
*/
export type OperationEnabledState = 'never' | 'changed' | 'affected';

/**
* Message received from a WebSocket client to change the enabled states of operations.
*/
export interface IWebSocketSetEnabledStatesCommandMessage {
command: 'set-enabled-states';
enabledStateByOperationName: Record<string, OperationEnabledState>;
}

/**
* The set of possible messages received from a WebSocket client.
*/
export type IWebSocketCommandMessage = IWebSocketSyncCommandMessage;
export type IWebSocketCommandMessage =
| IWebSocketSyncCommandMessage
| IWebSocketInvalidateCommandMessage
| IWebSocketSetEnabledStatesCommandMessage;
69 changes: 68 additions & 1 deletion rush-plugins/rush-serve-plugin/src/phasedCommandHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ import type {
ReadableOperationStatus,
IWebSocketCommandMessage,
IRushSessionInfo,
ILogFileURLs
ILogFileURLs,
OperationEnabledState
} from './api.types';

export interface IPhasedCommandHandlerOptions {
Expand Down Expand Up @@ -389,6 +390,51 @@ function tryEnableBuildStatusWebSocketServer(

const { hooks } = command;

let invalidateOperation: ((operation: Operation, reason: string) => void) | undefined;

const operationEnabledStates: Map<string, OperationEnabledState> = new Map();
hooks.createOperations.tap(
{
name: PLUGIN_NAME,
stage: Infinity
},
(operations: Set<Operation>, context: ICreateOperationsContext) => {
const potentiallyAffectedOperations: Set<Operation> = new Set();
for (const operation of operations) {
const { associatedProject } = operation;
if (context.projectsInUnknownState.has(associatedProject)) {
potentiallyAffectedOperations.add(operation);
}
}
for (const operation of potentiallyAffectedOperations) {
for (const consumer of operation.consumers) {
potentiallyAffectedOperations.add(consumer);
}

const { name } = operation;
const expectedState: OperationEnabledState | undefined = operationEnabledStates.get(name);
switch (expectedState) {
case 'affected':
operation.enabled = true;
break;
case 'never':
operation.enabled = false;
break;
case 'changed':
operation.enabled = context.projectsInUnknownState.has(operation.associatedProject);
break;
case undefined:
// Use the original value.
break;
}
}

invalidateOperation = context.invalidateOperation;

return operations;
}
);

hooks.beforeExecuteOperations.tap(
PLUGIN_NAME,
(operationsToExecute: Map<Operation, IOperationExecutionResult>): void => {
Expand Down Expand Up @@ -452,6 +498,27 @@ function tryEnableBuildStatusWebSocketServer(
break;
}

case 'set-enabled-states': {
const { enabledStateByOperationName } = parsedMessage;
for (const [name, state] of Object.entries(enabledStateByOperationName)) {
operationEnabledStates.set(name, state);
}
break;
}

case 'invalidate': {
const { operationNames } = parsedMessage;
const operationNameSet: Set<string> = new Set(operationNames);
if (invalidateOperation && operationStates) {
for (const operation of operationStates.keys()) {
if (operationNameSet.has(operation.name)) {
invalidateOperation(operation, 'WebSocket');
}
}
}
break;
}

default: {
// Unknown message. Ignore.
}
Expand Down
Loading