Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rush] Refactor the task runner code to be more naturally adapted to phased builds. #3103

Merged
merged 5 commits into from
Dec 21, 2021
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
69 changes: 33 additions & 36 deletions apps/rush-lib/src/cli/RushCommandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { SetupAction } from './actions/SetupAction';
import { EnvironmentConfiguration } from '../api/EnvironmentConfiguration';
import { ICustomCommandLineConfigurationInfo, PluginManager } from '../pluginFramework/PluginManager';
import { RushSession } from '../pluginFramework/RushSession';
import { ProjectLogWritable } from '../logic/taskExecution/ProjectLogWritable';

/**
* Options for `RushCommandLineParser`.
Expand All @@ -67,9 +68,9 @@ export class RushCommandLineParser extends CommandLineParser {
public readonly pluginManager: PluginManager;

private _debugParameter!: CommandLineFlagParameter;
private _rushOptions: IRushCommandLineParserOptions;
private _terminalProvider: ConsoleTerminalProvider;
private _terminal: Terminal;
private readonly _rushOptions: IRushCommandLineParserOptions;
private readonly _terminalProvider: ConsoleTerminalProvider;
private readonly _terminal: Terminal;

public constructor(options?: Partial<IRushCommandLineParserOptions>) {
super({
Expand All @@ -88,7 +89,6 @@ export class RushCommandLineParser extends CommandLineParser {

this._terminalProvider = new ConsoleTerminalProvider();
this._terminal = new Terminal(this._terminalProvider);

this._rushOptions = this._normalizeOptions(options || {});

try {
Expand Down Expand Up @@ -261,6 +261,8 @@ export class RushCommandLineParser extends CommandLineParser {
}

if (!this.tryGetAction(RushConstants.rebuildCommandName)) {
// By default, the "rebuild" action runs the "build" script. However, if the command-line.json file
// overrides "rebuild," the "rebuild" script should be run.
this._addCommandLineConfigAction(
commandLineConfiguration,
CommandLineConfiguration.defaultRebuildCommandJson,
Expand All @@ -283,7 +285,7 @@ export class RushCommandLineParser extends CommandLineParser {
private _addCommandLineConfigAction(
commandLineConfiguration: CommandLineConfiguration | undefined,
command: CommandJson,
commandToRun?: string
commandToRun: string = command.name
): void {
if (this.tryGetAction(command.name)) {
throw new Error(
Expand All @@ -292,18 +294,29 @@ export class RushCommandLineParser extends CommandLineParser {
);
}

this._validateCommandLineConfigCommand(command);
if (
(command.name === RushConstants.buildCommandName ||
command.name === RushConstants.rebuildCommandName) &&
command.safeForSimultaneousRushProcesses
) {
// Build and rebuild can't be designated "safeForSimultaneousRushProcesses"
throw new Error(
`${RushConstants.commandLineFilename} defines a command "${command.name}" using ` +
`"safeForSimultaneousRushProcesses=true". This configuration is not supported for "${command.name}".`
);
}

const overrideAllowWarnings: boolean = EnvironmentConfiguration.allowWarningsInSuccessfulBuild;

switch (command.commandKind) {
case RushConstants.bulkCommandKind: {
const logFilenameIdentifier: string =
ProjectLogWritable.normalizeNameForLogFilenameIdentifiers(commandToRun);

this.addAction(
new BulkScriptAction({
actionName: command.name,

// By default, the "rebuild" action runs the "build" script. However, if the command-line.json file
// overrides "rebuild," the "rebuild" script should be run.
logFilenameIdentifier: logFilenameIdentifier,
commandToRun: commandToRun,

summary: command.summary,
Expand All @@ -327,6 +340,17 @@ export class RushCommandLineParser extends CommandLineParser {
}

case RushConstants.globalCommandKind: {
if (
command.name === RushConstants.buildCommandName ||
command.name === RushConstants.rebuildCommandName
) {
throw new Error(
`${RushConstants.commandLineFilename} defines a command "${command.name}" using ` +
`the command kind "${RushConstants.globalCommandKind}". This command can only be designated as a command ` +
`kind "${RushConstants.bulkCommandKind}" or "${RushConstants.phasedCommandKind}".`
);
}

this.addAction(
new GlobalScriptAction({
actionName: command.name,
Expand Down Expand Up @@ -367,33 +391,6 @@ export class RushCommandLineParser extends CommandLineParser {
}
}

private _validateCommandLineConfigCommand(command: CommandJson): void {
// There are some restrictions on the 'build' and 'rebuild' commands.
if (
command.name !== RushConstants.buildCommandName &&
command.name !== RushConstants.rebuildCommandName
) {
return;
}

if (
command.commandKind !== RushConstants.bulkCommandKind &&
command.commandKind !== RushConstants.phasedCommandKind
) {
throw new Error(
`${RushConstants.commandLineFilename} defines a command "${command.name}" using ` +
`the command kind "${RushConstants.globalCommandKind}". This command can only be designated as a command ` +
`kind "${RushConstants.bulkCommandKind}" or "${RushConstants.phasedCommandKind}".`
);
}
if (command.safeForSimultaneousRushProcesses) {
throw new Error(
`${RushConstants.commandLineFilename} defines a command "${command.name}" using ` +
`"safeForSimultaneousRushProcesses=true". This configuration is not supported for "${command.name}".`
);
}
}

private _reportErrorAndSetExitCode(error: Error): void {
if (!(error instanceof AlreadyReportedError)) {
const prefix: string = 'ERROR: ';
Expand Down
19 changes: 14 additions & 5 deletions apps/rush-lib/src/cli/actions/WriteBuildCacheAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import { BaseRushAction } from './BaseRushAction';
import { RushCommandLineParser } from '../RushCommandLineParser';

import { BuildCacheConfiguration } from '../../api/BuildCacheConfiguration';
import { ProjectBuilder } from '../../logic/taskRunner/ProjectBuilder';
import { ProjectTaskRunner } from '../../logic/taskExecution/ProjectTaskRunner';
import { ProjectChangeAnalyzer } from '../../logic/ProjectChangeAnalyzer';
import { Utilities } from '../../utilities/Utilities';
import { TaskSelector } from '../../logic/TaskSelector';
import { NonPhasedProjectTaskSelector } from '../../logic/NonPhasedProjectTaskSelector';
import { RushConstants } from '../../logic/RushConstants';
import { CommandLineConfiguration } from '../../api/CommandLineConfiguration';
import { ProjectLogWritable } from '../../logic/taskExecution/ProjectLogWritable';

export class WriteBuildCacheAction extends BaseRushAction {
private _command!: CommandLineStringParameter;
Expand Down Expand Up @@ -74,18 +75,26 @@ export class WriteBuildCacheAction extends BaseRushAction {
);

const command: string = this._command.value!;
const commandToRun: string | undefined = TaskSelector.getScriptToRun(project, command, []);
const commandToRun: string | undefined = NonPhasedProjectTaskSelector.getScriptToRun(
project,
command,
[]
);

// TODO: With phased commands, this will need to be updated
const taskName: string = NonPhasedProjectTaskSelector.getTaskNameForProject(project);
const projectChangeAnalyzer: ProjectChangeAnalyzer = new ProjectChangeAnalyzer(this.rushConfiguration);
const projectBuilder: ProjectBuilder = new ProjectBuilder({
const projectBuilder: ProjectTaskRunner = new ProjectTaskRunner({
rushProject: project,
taskName,
rushConfiguration: this.rushConfiguration,
buildCacheConfiguration,
commandName: command,
commandToRun: commandToRun || '',
isIncrementalBuildAllowed: false,
projectChangeAnalyzer,
packageDepsFilename: Utilities.getPackageDepsFilenameForCommand(command)
packageDepsFilename: Utilities.getPackageDepsFilenameForCommand(command),
logFilenameIdentifier: ProjectLogWritable.normalizeNameForLogFilenameIdentifiers(command)
});

const trackedFiles: string[] = Array.from(
Expand Down
48 changes: 28 additions & 20 deletions apps/rush-lib/src/cli/scriptActions/BulkScriptAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ import { CommandLineFlagParameter, CommandLineStringParameter } from '@rushstack

import { Event } from '../../index';
import { SetupChecks } from '../../logic/SetupChecks';
import { ITaskSelectorOptions, TaskSelector } from '../../logic/TaskSelector';
import {
INonPhasedProjectTaskSelectorOptions,
NonPhasedProjectTaskSelector
} from '../../logic/NonPhasedProjectTaskSelector';
import { Stopwatch, StopwatchState } from '../../utilities/Stopwatch';
import { BaseScriptAction, IBaseScriptActionOptions } from './BaseScriptAction';
import { ITaskRunnerOptions, TaskRunner } from '../../logic/taskRunner/TaskRunner';
import {
ITaskExecutionManagerOptions,
TaskExecutionManager
} from '../../logic/taskExecution/TaskExecutionManager';
import { Utilities } from '../../utilities/Utilities';
import { RushConstants } from '../../logic/RushConstants';
import { EnvironmentVariableNames } from '../../api/EnvironmentConfiguration';
Expand All @@ -34,16 +40,13 @@ export interface IBulkScriptActionOptions extends IBaseScriptActionOptions {
allowWarningsInSuccessfulBuild: boolean;
watchForChanges: boolean;
disableBuildCache: boolean;

/**
* Optional command to run. Otherwise, use the `actionName` as the command to run.
*/
commandToRun?: string;
logFilenameIdentifier: string;
commandToRun: string;
}

interface IExecuteInternalOptions {
taskSelectorOptions: ITaskSelectorOptions;
taskRunnerOptions: ITaskRunnerOptions;
taskSelectorOptions: INonPhasedProjectTaskSelectorOptions;
taskExecutionManagerOptions: ITaskExecutionManagerOptions;
stopwatch: Stopwatch;
ignoreHooks?: boolean;
terminal: Terminal;
Expand All @@ -68,6 +71,7 @@ export class BulkScriptAction extends BaseScriptAction {
private readonly _repoCommandLineConfiguration: CommandLineConfiguration | undefined;
private readonly _ignoreDependencyOrder: boolean;
private readonly _allowWarningsInSuccessfulBuild: boolean;
private readonly _logFilenameIdentifier: string;

private _changedProjectsOnly!: CommandLineFlagParameter;
private _selectionParameters!: SelectionParameterSet;
Expand All @@ -80,12 +84,13 @@ export class BulkScriptAction extends BaseScriptAction {
this._enableParallelism = options.enableParallelism;
this._ignoreMissingScript = options.ignoreMissingScript;
this._isIncrementalBuildAllowed = options.incremental;
this._commandToRun = options.commandToRun || options.actionName;
this._commandToRun = options.commandToRun;
this._ignoreDependencyOrder = options.ignoreDependencyOrder;
this._allowWarningsInSuccessfulBuild = options.allowWarningsInSuccessfulBuild;
this._watchForChanges = options.watchForChanges;
this._disableBuildCache = options.disableBuildCache;
this._repoCommandLineConfiguration = options.commandLineConfiguration;
this._logFilenameIdentifier = options.logFilenameIdentifier;
}

public async runAsync(): Promise<void> {
Expand Down Expand Up @@ -139,7 +144,7 @@ export class BulkScriptAction extends BaseScriptAction {
return;
}

const taskSelectorOptions: ITaskSelectorOptions = {
const taskSelectorOptions: INonPhasedProjectTaskSelectorOptions = {
rushConfiguration: this.rushConfiguration,
buildCacheConfiguration,
selection,
Expand All @@ -152,21 +157,22 @@ export class BulkScriptAction extends BaseScriptAction {
ignoreMissingScript: this._ignoreMissingScript,
ignoreDependencyOrder: this._ignoreDependencyOrder,
allowWarningsInSuccessfulBuild: this._allowWarningsInSuccessfulBuild,
packageDepsFilename: Utilities.getPackageDepsFilenameForCommand(this._commandToRun)
packageDepsFilename: Utilities.getPackageDepsFilenameForCommand(this._commandToRun),
logFilenameIdentifier: this._logFilenameIdentifier
};

const taskRunnerOptions: ITaskRunnerOptions = {
const taskExecutionManagerOptions: ITaskExecutionManagerOptions = {
quietMode: isQuietMode,
debugMode: this.parser.isDebug,
parallelism: parallelism,
changedProjectsOnly: changedProjectsOnly,
allowWarningsInSuccessfulBuild: this._allowWarningsInSuccessfulBuild,
allowWarningsInSuccessfulExecution: this._allowWarningsInSuccessfulBuild,
repoCommandLineConfiguration: this._repoCommandLineConfiguration
};

const executeOptions: IExecuteInternalOptions = {
taskSelectorOptions,
taskRunnerOptions,
taskExecutionManagerOptions: taskExecutionManagerOptions,
stopwatch,
terminal
};
Expand Down Expand Up @@ -245,7 +251,7 @@ export class BulkScriptAction extends BaseScriptAction {
// Pass the ProjectChangeAnalyzer from the state differ to save a bit of overhead
projectChangeAnalyzer: state
},
taskRunnerOptions: options.taskRunnerOptions,
taskExecutionManagerOptions: options.taskExecutionManagerOptions,
stopwatch,
// For now, don't run pre-build or post-build in watch mode
ignoreHooks: true,
Expand Down Expand Up @@ -318,19 +324,21 @@ export class BulkScriptAction extends BaseScriptAction {
* Runs a single invocation of the command
*/
private async _runOnce(options: IExecuteInternalOptions): Promise<void> {
const taskSelector: TaskSelector = new TaskSelector(options.taskSelectorOptions);
const taskSelector: NonPhasedProjectTaskSelector = new NonPhasedProjectTaskSelector(
options.taskSelectorOptions
);

// Register all tasks with the task collection

const taskRunner: TaskRunner = new TaskRunner(
const taskExecutionManager: TaskExecutionManager = new TaskExecutionManager(
taskSelector.registerTasks().getOrderedTasks(),
options.taskRunnerOptions
options.taskExecutionManagerOptions
);

const { ignoreHooks, stopwatch } = options;

try {
await taskRunner.executeAsync();
await taskExecutionManager.executeAsync();

stopwatch.stop();
console.log(colors.green(`rush ${this.actionName} (${stopwatch.toString()})`));
Expand Down
16 changes: 12 additions & 4 deletions apps/rush-lib/src/cli/test/RushCommandLineParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,9 @@ describe('RushCommandLineParser', () => {

await expect(() => {
getCommandLineParserInstance(repoName, 'doesnt-matter');
}).toThrowError('This command can only be designated as a command kind "bulk"');
}).toThrowErrorMatchingInlineSnapshot(
`"command-line.json defines a command \\"build\\" using the command kind \\"global\\". This command can only be designated as a command kind \\"bulk\\" or \\"phased\\"."`
);
});
});

Expand All @@ -301,7 +303,9 @@ describe('RushCommandLineParser', () => {

await expect(() => {
getCommandLineParserInstance(repoName, 'doesnt-matter');
}).toThrowError('This command can only be designated as a command kind "bulk"');
}).toThrowErrorMatchingInlineSnapshot(
`"command-line.json defines a command \\"rebuild\\" using the command kind \\"global\\". This command can only be designated as a command kind \\"bulk\\" or \\"phased\\"."`
);
});
});

Expand All @@ -311,7 +315,9 @@ describe('RushCommandLineParser', () => {

await expect(() => {
getCommandLineParserInstance(repoName, 'doesnt-matter');
}).toThrowError('"safeForSimultaneousRushProcesses=true". This configuration is not supported');
}).toThrowErrorMatchingInlineSnapshot(
`"command-line.json defines a command \\"build\\" using \\"safeForSimultaneousRushProcesses=true\\". This configuration is not supported for \\"build\\"."`
);
});
});

Expand All @@ -321,7 +327,9 @@ describe('RushCommandLineParser', () => {

await expect(() => {
getCommandLineParserInstance(repoName, 'doesnt-matter');
}).toThrowError('"safeForSimultaneousRushProcesses=true". This configuration is not supported');
}).toThrowErrorMatchingInlineSnapshot(
`"command-line.json defines a command \\"rebuild\\" using \\"safeForSimultaneousRushProcesses=true\\". This configuration is not supported for \\"rebuild\\"."`
);
});
});
});
Expand Down
Loading