Skip to content
Open
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
1 change: 1 addition & 0 deletions apps/rush/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@rushstack/rush-amazon-s3-build-cache-plugin": "workspace:*",
"@rushstack/rush-azure-storage-build-cache-plugin": "workspace:*",
"@rushstack/rush-http-build-cache-plugin": "workspace:*",
"@rushstack/rush-serve-plugin": "workspace:*",
"@types/heft-jest": "1.0.1",
"@types/semver": "7.5.0"
}
Expand Down
1 change: 1 addition & 0 deletions apps/rush/src/start-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function includePlugin(pluginName: string, pluginPackageName?: string): void {
includePlugin('rush-amazon-s3-build-cache-plugin');
includePlugin('rush-azure-storage-build-cache-plugin');
includePlugin('rush-http-build-cache-plugin');
includePlugin('rush-serve-plugin');
// Including this here so that developers can reuse it without installing the plugin a second time
includePlugin('rush-azure-interactive-auth-plugin', '@rushstack/rush-azure-storage-build-cache-plugin');

Expand Down
10 changes: 10 additions & 0 deletions common/changes/@microsoft/rush/watch-rework_2025-09-26-23-50.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "(PLUGIN BREAKING CHANGE) Overhaul watch-mode commands such that the graph is only created once at the start of command invocation, along with a stateful manager object. Plugins may now access the manager object and use it to orchestrate and tap into the build process.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
13 changes: 13 additions & 0 deletions common/config/rush-plugins/rush-serve-plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-serve-plugin-options.schema.json",
"phasedCommands": ["start"],
"portParameterLongName": "--port",
"globalRouting": [
{
"workspaceRelativeFile": "rush-plugins/rush-serve-plugin/dashboard.html",
"servePath": "/"
}
],
"buildStatusWebSocketPath": "/ws",
"logServePath": "/logs"
}
17 changes: 13 additions & 4 deletions common/config/rush/command-line.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,24 +237,25 @@
// Used for very simple builds that don't support CLI arguments like `--production` or `--fix`
"name": "_phase:lite-build",
"dependencies": {
"upstream": ["_phase:lite-build", "_phase:build"]
"upstream": ["_phase:build"]
},
"missingScriptBehavior": "silent",
"allowWarningsOnSuccess": false
},
{
"name": "_phase:build",
"dependencies": {
"self": ["_phase:lite-build"],
"upstream": ["_phase:build"]
// Don't need to declare the dependency on _phase:build because it is transitive via _phase:lite-build
"self": ["_phase:lite-build"]
},
"missingScriptBehavior": "log",
"allowWarningsOnSuccess": false
},
{
"name": "_phase:test",
"dependencies": {
"self": ["_phase:lite-build", "_phase:build"]
// Dependency on _phase:lite-build is transitive via _phase:build
"self": ["_phase:build"]
},
"missingScriptBehavior": "silent",
"allowWarningsOnSuccess": false
Expand Down Expand Up @@ -486,6 +487,14 @@
"associatedPhases": ["_phase:build", "_phase:test"],
"associatedCommands": ["build", "rebuild", "test", "retest"]
},
{
"longName": "--port",
"parameterKind": "integer",
"argumentName": "PORT",
"description": "The port to use for the server",
"associatedPhases": [],
"associatedCommands": ["start"]
},
{
"longName": "--update-snapshots",
"parameterKind": "flag",
Expand Down
4 changes: 2 additions & 2 deletions common/config/rush/experiments.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
* Enable this experiment if you want "rush" and "rushx" commands to resync injected dependencies
* by invoking "pnpm-sync" during the build.
*/
"usePnpmSyncForInjectedDependencies": true
"usePnpmSyncForInjectedDependencies": true,

/**
* If set to true, Rush will generate a `project-impact-graph.yaml` file in the repository root during `rush update`.
Expand All @@ -88,7 +88,7 @@
* of `_phase:<name>` if they exist. The created child process will be provided with an IPC channel and expected to persist
* across invocations.
*/
// "useIPCScriptsInWatchMode": true,
"useIPCScriptsInWatchMode": true

/**
* (UNDER DEVELOPMENT) The Rush alerts feature provides a way to send announcements to engineers
Expand Down
4 changes: 4 additions & 0 deletions common/config/rush/nonbrowser-approved-packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@
"name": "@rushstack/rush-sdk",
"allowedCategories": [ "libraries", "tests", "vscode-extensions" ]
},
{
"name": "@rushstack/rush-serve-plugin",
"allowedCategories": [ "libraries" ]
},
{
"name": "@rushstack/set-webpack-public-path-plugin",
"allowedCategories": [ "libraries", "tests" ]
Expand Down
9 changes: 6 additions & 3 deletions common/config/subspaces/default/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

139 changes: 103 additions & 36 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type { StdioSummarizer } from '@rushstack/terminal';
import { SyncHook } from 'tapable';
import { SyncWaterfallHook } from 'tapable';
import { Terminal } from '@rushstack/terminal';
import type { TerminalWritable } from '@rushstack/terminal';

// @public
export class ApprovedPackagesConfiguration {
Expand Down Expand Up @@ -342,6 +343,14 @@ export type GetCacheEntryIdFunction = (options: IGenerateCacheEntryIdOptions) =>
// @beta
export type GetInputsSnapshotAsyncFn = () => Promise<IInputsSnapshot | undefined>;

// @alpha (undocumented)
export interface IBaseOperationExecutionResult {
getStateHash(): string;
getStateHashComponents(): ReadonlyArray<string>;
readonly metadataFolderPath: string | undefined;
readonly operation: Operation;
}

// @internal (undocumented)
export interface _IBuiltInPluginConfiguration extends _IRushPluginConfigurationBase {
// (undocumented)
Expand Down Expand Up @@ -402,6 +411,11 @@ export interface ICobuildLockProvider {
setCompletedStateAsync(context: Readonly<ICobuildContext>, state: ICobuildCompletedState): Promise<void>;
}

// @alpha
export interface IConfigurableOperation extends IBaseOperationExecutionResult {
enabled: boolean;
}

// @public
export interface IConfigurationEnvironment {
[environmentVariableName: string]: IConfigurationEnvironmentVariable;
Expand All @@ -419,17 +433,14 @@ export interface ICreateOperationsContext {
readonly changedProjectsOnly: boolean;
readonly cobuildConfiguration: CobuildConfiguration | undefined;
readonly customParameters: ReadonlyMap<string, CommandLineParameter>;
readonly generateFullGraph?: boolean;
readonly includePhaseDeps: boolean;
readonly invalidateOperation?: ((operation: Operation, reason: string) => void) | undefined;
readonly isIncrementalBuildAllowed: boolean;
readonly isInitial: boolean;
readonly isWatch: boolean;
readonly parallelism: number;
readonly phaseOriginal: ReadonlySet<IPhase>;
readonly phaseSelection: ReadonlySet<IPhase>;
readonly projectConfigurations: ReadonlyMap<RushConfigurationProject, RushProjectConfiguration>;
readonly projectSelection: ReadonlySet<RushConfigurationProject>;
readonly projectsInUnknownState: ReadonlySet<RushConfigurationProject>;
readonly rushConfiguration: RushConfiguration;
}

Expand Down Expand Up @@ -476,12 +487,6 @@ export interface IEnvironmentConfigurationInitializeOptions {
doNotNormalizePaths?: boolean;
}

// @alpha
export interface IExecuteOperationsContext extends ICreateOperationsContext {
readonly abortController: AbortController;
readonly inputsSnapshot?: IInputsSnapshot;
}

// @alpha
export interface IExecutionResult {
readonly operationResults: ReadonlyMap<Operation, IOperationExecutionResult>;
Expand Down Expand Up @@ -614,21 +619,52 @@ export interface _IOperationBuildCacheOptions {
}

// @alpha
export interface IOperationExecutionResult {
export interface IOperationExecutionResult extends IBaseOperationExecutionResult {
readonly enabled: boolean;
readonly error: Error | undefined;
getStateHash(): string;
getStateHashComponents(): ReadonlyArray<string>;
readonly logFilePaths: ILogFilePaths | undefined;
readonly metadataFolderPath: string | undefined;
readonly nonCachedDurationMs: number | undefined;
readonly operation: Operation;
readonly problemCollector: IProblemCollector;
readonly silent: boolean;
readonly status: OperationStatus;
readonly stdioSummarizer: StdioSummarizer;
readonly stopwatch: IStopwatchResult;
}

// @alpha
export interface IOperationGraph {
readonly abortController: AbortController;
abortCurrentIterationAsync(): Promise<void>;
addTerminalDestination(destination: TerminalWritable): void;
closeRunnersAsync(operations?: Iterable<Operation>): Promise<void>;
debugMode: boolean;
executeScheduledIterationAsync(): Promise<boolean>;
readonly hasScheduledIteration: boolean;
readonly hooks: OperationGraphHooks;
invalidateOperations(operations?: Iterable<Operation>, reason?: string): void;
readonly lastExecutionResults: ReadonlyMap<Operation, IOperationExecutionResult>;
readonly operations: ReadonlySet<Operation>;
parallelism: number;
pauseNextIteration: boolean;
quietMode: boolean;
removeTerminalDestination(destination: TerminalWritable, close?: boolean): boolean;
scheduleIterationAsync(options: IOperationGraphIterationOptions): Promise<boolean>;
setEnabledStates(operations: Iterable<Operation>, targetState: Operation['enabled'], mode: 'safe' | 'unsafe'): boolean;
readonly status: OperationStatus;
}

// @alpha
export interface IOperationGraphContext extends ICreateOperationsContext {
readonly initialSnapshot?: IInputsSnapshot;
}

// @alpha
export interface IOperationGraphIterationOptions {
// (undocumented)
inputsSnapshot?: IInputsSnapshot;
startTime?: number;
}

// @internal (undocumented)
export interface _IOperationMetadata {
// (undocumented)
Expand All @@ -653,6 +689,7 @@ export interface _IOperationMetadataManagerOptions {

// @alpha
export interface IOperationOptions {
enabled?: OperationEnabledState;
logFilenameIdentifier: string;
phase: IPhase;
project: RushConfigurationProject;
Expand All @@ -663,8 +700,10 @@ export interface IOperationOptions {
// @beta
export interface IOperationRunner {
cacheable: boolean;
executeAsync(context: IOperationRunnerContext): Promise<OperationStatus>;
closeAsync?(): Promise<void>;
executeAsync(context: IOperationRunnerContext, lastState?: {}): Promise<OperationStatus>;
getConfigHash(): string;
readonly isActive?: boolean;
readonly isNoOp?: boolean;
readonly name: string;
reportTiming: boolean;
Expand Down Expand Up @@ -751,6 +790,11 @@ export interface IPhasedCommand extends IRushCommand {
readonly sessionAbortController: AbortController;
}

// @alpha
export interface IPhasedCommandPlugin {
apply(hooks: PhasedCommandHooks): void;
}

// @public
export interface IPnpmLockfilePolicies {
disallowInsecureSha1?: {
Expand Down Expand Up @@ -998,7 +1042,7 @@ export class Operation {
readonly consumers: ReadonlySet<Operation>;
deleteDependency(dependency: Operation): void;
readonly dependencies: ReadonlySet<Operation>;
enabled: boolean;
enabled: OperationEnabledState;
get isNoOp(): boolean;
logFilenameIdentifier: string;
get name(): string;
Expand All @@ -1012,7 +1056,7 @@ export class _OperationBuildCache {
// (undocumented)
get cacheId(): string | undefined;
// (undocumented)
static forOperation(executionResult: IOperationExecutionResult, options: _IOperationBuildCacheOptions): _OperationBuildCache;
static forOperation(executionResult: IBaseOperationExecutionResult, options: _IOperationBuildCacheOptions): _OperationBuildCache;
// (undocumented)
static getOperationBuildCache(options: _IProjectBuildCacheOptions): _OperationBuildCache;
// (undocumented)
Expand All @@ -1021,6 +1065,43 @@ export class _OperationBuildCache {
trySetCacheEntryAsync(terminal: ITerminal, specifiedCacheId?: string): Promise<boolean>;
}

// @alpha
export type OperationEnabledState = boolean | 'ignore-dependency-changes';

// @alpha
export class OperationGraphHooks {
readonly afterExecuteIterationAsync: AsyncSeriesWaterfallHook<[
OperationStatus,
ReadonlyMap<Operation, IOperationExecutionResult>,
IOperationGraphIterationOptions
]>;
readonly afterExecuteOperationAsync: AsyncSeriesHook<[
IOperationRunnerContext & IOperationExecutionResult
]>;
readonly beforeExecuteIterationAsync: AsyncSeriesBailHook<[
ReadonlyMap<Operation, IOperationExecutionResult>,
IOperationGraphIterationOptions
], OperationStatus | undefined | void>;
readonly beforeExecuteOperationAsync: AsyncSeriesBailHook<[
IOperationRunnerContext & IOperationExecutionResult
], OperationStatus | undefined>;
readonly configureIteration: SyncHook<[
ReadonlyMap<Operation, IConfigurableOperation>,
ReadonlyMap<Operation, IOperationExecutionResult>,
IOperationGraphIterationOptions
]>;
readonly createEnvironmentForOperation: SyncWaterfallHook<[
IEnvironment,
IOperationRunnerContext & IOperationExecutionResult
]>;
readonly onEnableStatesChanged: SyncHook<[ReadonlySet<Operation>]>;
readonly onExecutionStatesUpdated: SyncHook<[ReadonlySet<IOperationExecutionResult>]>;
readonly onGraphStateChanged: SyncHook<[IOperationGraph]>;
readonly onInvalidateOperations: SyncHook<[Iterable<Operation>, string | undefined]>;
readonly onIterationScheduled: SyncHook<[ReadonlyMap<Operation, IOperationExecutionResult>]>;
readonly onWaitingForChanges: SyncHook<void>;
}

// @internal
export class _OperationMetadataManager {
constructor(options: _IOperationMetadataManagerOptions);
Expand Down Expand Up @@ -1150,26 +1231,12 @@ export abstract class PackageManagerOptionsConfigurationBase implements IPackage

// @alpha
export class PhasedCommandHooks {
readonly afterExecuteOperation: AsyncSeriesHook<[
IOperationRunnerContext & IOperationExecutionResult
]>;
readonly afterExecuteOperations: AsyncSeriesHook<[IExecutionResult, IExecuteOperationsContext]>;
readonly beforeExecuteOperation: AsyncSeriesBailHook<[
IOperationRunnerContext & IOperationExecutionResult
], OperationStatus | undefined>;
readonly beforeExecuteOperations: AsyncSeriesHook<[
Map<Operation, IOperationExecutionResult>,
IExecuteOperationsContext
]>;
readonly beforeLog: SyncHook<ITelemetryData, void>;
readonly createEnvironmentForOperation: SyncWaterfallHook<[
IEnvironment,
IOperationRunnerContext & IOperationExecutionResult
readonly createOperationsAsync: AsyncSeriesWaterfallHook<[
Set<Operation>,
ICreateOperationsContext
]>;
readonly createOperations: AsyncSeriesWaterfallHook<[Set<Operation>, ICreateOperationsContext]>;
readonly onOperationStatusChanged: SyncHook<[IOperationExecutionResult]>;
readonly shutdownAsync: AsyncParallelHook<void>;
readonly waitingForChanges: SyncHook<void>;
readonly onGraphCreatedAsync: AsyncSeriesHook<[IOperationGraph, IOperationGraphContext]>;
}

// @public
Expand Down
Loading