From 2780ef9f241b4d6f51fdbd389c22e4fa1c69f324 Mon Sep 17 00:00:00 2001 From: Daniel Nadeau <3473356+D4N14L@users.noreply.github.com> Date: Fri, 22 May 2020 15:47:38 -0700 Subject: [PATCH] Add --to and --from support to rush install --- .../src/cli/actions/BaseRushAction.ts | 54 ++++++++++++++++- .../rush-lib/src/cli/actions/InstallAction.ts | 40 ++++++++++++- apps/rush-lib/src/cli/actions/UpdateAction.ts | 4 +- .../src/cli/scriptActions/BulkScriptAction.ts | 59 +------------------ apps/rush-lib/src/logic/PackageJsonUpdater.ts | 4 +- .../src/logic/WorkspaceInstallManager.ts | 28 ++++++++- .../src/logic/base/BaseInstallManager.ts | 10 ++++ 7 files changed, 136 insertions(+), 63 deletions(-) diff --git a/apps/rush-lib/src/cli/actions/BaseRushAction.ts b/apps/rush-lib/src/cli/actions/BaseRushAction.ts index 6618bd64469..a3f31fe734e 100644 --- a/apps/rush-lib/src/cli/actions/BaseRushAction.ts +++ b/apps/rush-lib/src/cli/actions/BaseRushAction.ts @@ -7,16 +7,19 @@ import * as path from 'path'; import { CommandLineAction, + CommandLineStringListParameter, ICommandLineActionOptions } from '@rushstack/ts-command-line'; import { LockFile } from '@rushstack/node-core-library'; -import { RushConfiguration } from '../../api/RushConfiguration'; +import { AlreadyReportedError } from '../../utilities/AlreadyReportedError'; import { EventHooksManager } from '../../logic/EventHooksManager'; +import { PackageJsonLookup, IPackageJson } from '@rushstack/node-core-library'; import { RushCommandLineParser } from './../RushCommandLineParser'; +import { RushConfiguration } from '../../api/RushConfiguration'; +import { RushGlobalFolder } from '../../api/RushGlobalFolder' import { Utilities } from '../../utilities/Utilities'; -import { RushGlobalFolder } from '../../api/RushGlobalFolder'; export interface IBaseRushActionOptions extends ICommandLineActionOptions { /** @@ -120,4 +123,51 @@ export abstract class BaseRushAction extends BaseConfiglessRushAction { return this._eventHooksManager; } + + protected mergeProjectsWithVersionPolicy( + projectsParameters: CommandLineStringListParameter, + versionPoliciesParameters: CommandLineStringListParameter + ): string[] { + const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); + + const projects: string[] = []; + for (const projectParameter of projectsParameters.values) { + if (projectParameter === '.') { + const packageJson: IPackageJson | undefined = packageJsonLookup.tryLoadPackageJsonFor(process.cwd()); + if (packageJson) { + const projectName: string = packageJson.name; + if (this.rushConfiguration.projectsByName.has(projectName)) { + projects.push(projectName); + } else { + console.log(colors.red( + 'Rush is not currently running in a project directory specified in rush.json. ' + + `The "." value for the ${projectsParameters.longName} parameter is not allowed.` + )); + throw new AlreadyReportedError(); + } + } else { + console.log(colors.red( + 'Rush is not currently running in a project directory. ' + + `The "." value for the ${projectsParameters.longName} parameter is not allowed.` + )); + throw new AlreadyReportedError(); + } + } else { + projects.push(projectParameter); + } + } + + if (versionPoliciesParameters.values && versionPoliciesParameters.values.length > 0) { + this.rushConfiguration.projects.forEach(project => { + const matches: boolean = versionPoliciesParameters.values.some(policyName => { + return project.versionPolicyName === policyName; + }); + if (matches) { + projects.push(project.packageName); + } + }); + } + + return projects; + } } diff --git a/apps/rush-lib/src/cli/actions/InstallAction.ts b/apps/rush-lib/src/cli/actions/InstallAction.ts index 399c4d20077..51ed463fc6b 100644 --- a/apps/rush-lib/src/cli/actions/InstallAction.ts +++ b/apps/rush-lib/src/cli/actions/InstallAction.ts @@ -4,8 +4,14 @@ import { BaseInstallAction } from './BaseInstallAction'; import { IInstallManagerOptions } from '../../logic/base/BaseInstallManager'; import { RushCommandLineParser } from '../RushCommandLineParser'; +import { CommandLineStringListParameter } from '@rushstack/ts-command-line'; export class InstallAction extends BaseInstallAction { + protected _fromFlag: CommandLineStringListParameter; + protected _toFlag: CommandLineStringListParameter; + protected _fromVersionPolicy: CommandLineStringListParameter; + protected _toVersionPolicy: CommandLineStringListParameter; + public constructor(parser: RushCommandLineParser) { super({ actionName: 'install', @@ -24,6 +30,36 @@ export class InstallAction extends BaseInstallAction { }); } + protected onDefineParameters(): void { + super.onDefineParameters(); + this._toFlag = this.defineStringListParameter({ + parameterLongName: '--to', + parameterShortName: '-t', + argumentName: 'PROJECT1', + description: 'Run install in the specified project and all of its dependencies. "." can be used as shorthand ' + + 'to specify the project in the current working directory.' + }); + this._fromVersionPolicy = this.defineStringListParameter({ + parameterLongName: '--from-version-policy', + argumentName: 'VERSION_POLICY_NAME', + description: 'Run install in all projects with the specified version policy ' + + 'and all projects that directly or indirectly depend on projects with the specified version policy' + }); + this._toVersionPolicy = this.defineStringListParameter({ + parameterLongName: '--to-version-policy', + argumentName: 'VERSION_POLICY_NAME', + description: 'Run install in all projects with the specified version policy and all of their dependencies' + }); + this._fromFlag = this.defineStringListParameter({ + parameterLongName: '--from', + parameterShortName: '-f', + argumentName: 'PROJECT2', + description: 'Run install in all projects that directly or indirectly depend on the specified project. ' + + '"." can be used as shorthand to specify the project in the current working directory.' + }); + + } + protected buildInstallOptions(): IInstallManagerOptions { return { debug: this.parser.isDebug, @@ -37,7 +73,9 @@ export class InstallAction extends BaseInstallAction { variant: this._variant.value, // Because the 'defaultValue' option on the _maxInstallAttempts parameter is set, // it is safe to assume that the value is not null - maxInstallAttempts: this._maxInstallAttempts.value! + maxInstallAttempts: this._maxInstallAttempts.value!, + toFlags: this.mergeProjectsWithVersionPolicy(this._toFlag, this._toVersionPolicy), + fromFlags: this.mergeProjectsWithVersionPolicy(this._fromFlag, this._fromVersionPolicy) }; } } diff --git a/apps/rush-lib/src/cli/actions/UpdateAction.ts b/apps/rush-lib/src/cli/actions/UpdateAction.ts index e4b9d5121af..42ea4844f60 100644 --- a/apps/rush-lib/src/cli/actions/UpdateAction.ts +++ b/apps/rush-lib/src/cli/actions/UpdateAction.ts @@ -65,7 +65,9 @@ export class UpdateAction extends BaseInstallAction { variant: this._variant.value, // Because the 'defaultValue' option on the _maxInstallAttempts parameter is set, // it is safe to assume that the value is not null - maxInstallAttempts: this._maxInstallAttempts.value! + maxInstallAttempts: this._maxInstallAttempts.value!, + toFlags: [], // Do not support partial updates + fromFlags: [] // Do not support partial updates }; } } diff --git a/apps/rush-lib/src/cli/scriptActions/BulkScriptAction.ts b/apps/rush-lib/src/cli/scriptActions/BulkScriptAction.ts index bcc32af5744..c9f855f4d5d 100644 --- a/apps/rush-lib/src/cli/scriptActions/BulkScriptAction.ts +++ b/apps/rush-lib/src/cli/scriptActions/BulkScriptAction.ts @@ -10,11 +10,7 @@ import { CommandLineStringListParameter, CommandLineParameterKind } from '@rushstack/ts-command-line'; -import { - FileSystem, - PackageJsonLookup, - IPackageJson -} from '@rushstack/node-core-library'; +import { FileSystem } from '@rushstack/node-core-library'; import { Event } from '../../index'; import { SetupChecks } from '../../logic/SetupChecks'; @@ -105,8 +101,8 @@ export class BulkScriptAction extends BaseScriptAction { const taskSelector: TaskSelector = new TaskSelector({ rushConfiguration: this.rushConfiguration, - toFlags: this._mergeProjectsWithVersionPolicy(this._toFlag, this._toVersionPolicy), - fromFlags: this._mergeProjectsWithVersionPolicy(this._fromFlag, this._fromVersionPolicy), + toFlags: this.mergeProjectsWithVersionPolicy(this._toFlag, this._toVersionPolicy), + fromFlags: this.mergeProjectsWithVersionPolicy(this._fromFlag, this._fromVersionPolicy), commandToRun: this._commandToRun, customParameterValues, isQuietMode: isQuietMode, @@ -203,55 +199,6 @@ export class BulkScriptAction extends BaseScriptAction { this.defineScriptParameters(); } - private _mergeProjectsWithVersionPolicy( - projectsParameters: CommandLineStringListParameter, - versionPoliciesParameters: CommandLineStringListParameter - ): string[] { - const packageJsonLookup: PackageJsonLookup = new PackageJsonLookup(); - - const projects: string[] = []; - for (const projectParameter of projectsParameters.values) { - if (projectParameter === '.') { - const packageJson: IPackageJson | undefined = packageJsonLookup.tryLoadPackageJsonFor(process.cwd()); - if (packageJson) { - const projectName: string = packageJson.name; - if (this.rushConfiguration.projectsByName.has(projectName)) { - projects.push(projectName); - } else { - console.log(colors.red( - 'Rush is not currently running in a project directory specified in rush.json. ' + - `The "." value for the ${this._toFlag.longName} parameter or the ${this._fromFlag.longName} parameter ` + - 'is not allowed.' - )); - throw new AlreadyReportedError(); - } - } else { - console.log(colors.red( - 'Rush is not currently running in a project directory. ' + - `The "." value for the ${this._toFlag.longName} parameter or the ${this._fromFlag.longName} parameter ` + - 'is not allowed.' - )); - throw new AlreadyReportedError(); - } - } else { - projects.push(projectParameter); - } - } - - if (versionPoliciesParameters.values && versionPoliciesParameters.values.length > 0) { - this.rushConfiguration.projects.forEach(project => { - const matches: boolean = versionPoliciesParameters.values.some(policyName => { - return project.versionPolicyName === policyName; - }); - if (matches) { - projects.push(project.packageName); - } - }); - } - - return projects; - } - private _doBeforeTask(): void { if (this.actionName !== RushConstants.buildCommandName && this.actionName !== RushConstants.rebuildCommandName) { // Only collects information for built-in tasks like build or rebuild. diff --git a/apps/rush-lib/src/logic/PackageJsonUpdater.ts b/apps/rush-lib/src/logic/PackageJsonUpdater.ts index d42e6ab7346..e0ae0247ef1 100644 --- a/apps/rush-lib/src/logic/PackageJsonUpdater.ts +++ b/apps/rush-lib/src/logic/PackageJsonUpdater.ts @@ -144,7 +144,9 @@ export class PackageJsonUpdater { networkConcurrency: undefined, collectLogFile: false, variant: variant, - maxInstallAttempts: RushConstants.defaultMaxInstallAttempts + maxInstallAttempts: RushConstants.defaultMaxInstallAttempts, + toFlags: [], + fromFlags: [] }; const installManager: BaseInstallManager = InstallManagerFactory.getInstallManager( this._rushConfiguration, diff --git a/apps/rush-lib/src/logic/WorkspaceInstallManager.ts b/apps/rush-lib/src/logic/WorkspaceInstallManager.ts index e02834cdfc5..c3fabe89e0f 100644 --- a/apps/rush-lib/src/logic/WorkspaceInstallManager.ts +++ b/apps/rush-lib/src/logic/WorkspaceInstallManager.ts @@ -94,7 +94,7 @@ export class WorkspaceInstallManager extends BaseInstallManager { + 'the shrinkwrap file.' )); throw new AlreadyReportedError(); - } + } } // dependency name --> version specifier @@ -180,7 +180,7 @@ export class WorkspaceInstallManager extends BaseInstallManager { } else if (dependencySpecifier.specifierType === 'workspace') { // Already specified as a local project. Allow the package manager to validate this continue; - } + } // PNPM does not specify peer dependencies for workspaces in the shrinkwrap, so skip validating these if (this.rushConfiguration.packageManager === 'pnpm' && dependencyType === DependencyType.Peer) { @@ -380,7 +380,31 @@ export class WorkspaceInstallManager extends BaseInstallManager { if (this.rushConfiguration.packageManager === 'pnpm') { args.push('--recursive'); args.push('--link-workspace-packages', 'false'); + + const toPackages: Set = this._findPackageNames(options.toFlags); + for (const toPackage of toPackages) { + args.push('--filter', `${toPackage}...`); + } + + const fromPackages: Set = this._findPackageNames(options.fromFlags); + for (const fromPackage of fromPackages) { + args.push('--filter', `...${fromPackage}`); + } + } + } + + private _findPackageNames(shorthandNames: ReadonlyArray): Set { + + const dependencies: Set = new Set(); + for (const name of shorthandNames) { + const project: RushConfigurationProject | undefined = + this.rushConfiguration.findProjectByShorthandName(name); + if (!project) { + throw new Error(`The project '${name}' does not exist in rush.json`); + } + dependencies.add(project.packageName); } + return dependencies; } /** diff --git a/apps/rush-lib/src/logic/base/BaseInstallManager.ts b/apps/rush-lib/src/logic/base/BaseInstallManager.ts index a52e63a15fe..76c80b63aea 100644 --- a/apps/rush-lib/src/logic/base/BaseInstallManager.ts +++ b/apps/rush-lib/src/logic/base/BaseInstallManager.ts @@ -101,6 +101,16 @@ export interface IInstallManagerOptions { * Retry the install the specified number of times */ maxInstallAttempts: number + + /** + * Run install in the specified projects as well as all dependencies + */ + toFlags: ReadonlyArray; + + /** + * Run install in the specified projects as well as all dependent projects + */ + fromFlags: ReadonlyArray; } /**