Skip to content

Commit dbbe8d9

Browse files
authored
Merge pull request microsoft#1772 from microsoft/sachinjoseph/support-custom-environment-variables-for-install-manager
[rush] Support custom environment variables for the underlying package manager install process, expose maxInstallAttempts as a parameter for rush install/update.
2 parents b294c28 + 4299c5e commit dbbe8d9

File tree

12 files changed

+347
-46
lines changed

12 files changed

+347
-46
lines changed

apps/rush-lib/src/api/RushConfiguration.ts

Lines changed: 117 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,58 @@ export interface IRushRepositoryJson {
105105
*/
106106
export type PnpmStoreOptions = 'local' | 'global';
107107

108+
/**
109+
* Options for the package manager.
110+
* @public
111+
*/
112+
export interface IPackageManagerOptionsJsonBase {
113+
/**
114+
* Enviroment variables for the package manager
115+
*/
116+
environmentVariables?: IConfigurationEnvironment
117+
}
118+
119+
/**
120+
* A collection of environment variables
121+
* @public
122+
*/
123+
export interface IConfigurationEnvironment {
124+
/**
125+
* Environment variables
126+
*/
127+
[environmentVariableName: string]: IConfigurationEnvironmentVariable;
128+
}
129+
130+
/**
131+
* Represents the value of an environment variable, and if the value should be overridden if the variable is set
132+
* in the parent environment.
133+
* @public
134+
*/
135+
export interface IConfigurationEnvironmentVariable {
136+
/**
137+
* Value of the environment variable
138+
*/
139+
value: string;
140+
141+
/**
142+
* Set to true to override the environment variable even if it is set in the parent environment.
143+
* The default value is false.
144+
*/
145+
override?: boolean;
146+
}
147+
108148
/**
109149
* Part of IRushConfigurationJson.
110150
* @internal
111151
*/
112-
export interface IPnpmOptionsJson {
152+
export interface INpmOptionsJson extends IPackageManagerOptionsJsonBase {
153+
}
154+
155+
/**
156+
* Part of IRushConfigurationJson.
157+
* @internal
158+
*/
159+
export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase {
113160
/**
114161
* The store resolution method for PNPM to use
115162
*/
@@ -126,8 +173,16 @@ export interface IPnpmOptionsJson {
126173

127174
/**
128175
* Part of IRushConfigurationJson.
176+
* @internal
129177
*/
130-
export interface IYarnOptionsJson {
178+
export interface IYarnOptionsJson extends IPackageManagerOptionsJsonBase {
179+
/**
180+
* If true, then Rush will add the "--ignore-engines" option when invoking Yarn.
181+
* This allows "rush install" to succeed if there are dependencies with engines defined in
182+
* package.json which do not match the current environment.
183+
*
184+
* The default value is false.
185+
*/
131186
ignoreEngines?: boolean;
132187
}
133188

@@ -160,6 +215,7 @@ export interface IRushConfigurationJson {
160215
projects: IRushConfigurationProjectJson[];
161216
eventHooks?: IEventHooksJson;
162217
hotfixChangeEnabled?: boolean;
218+
npmOptions?: INpmOptionsJson;
163219
pnpmOptions?: IPnpmOptionsJson;
164220
yarnOptions?: IYarnOptionsJson;
165221
ensureConsistentVersions?: boolean;
@@ -182,6 +238,39 @@ export interface ICurrentVariantJson {
182238
variant: string | null; // Use `null` instead of `undefined` because `undefined` is not handled by JSON.
183239
}
184240

241+
/**
242+
* Options that all package managers share.
243+
*
244+
* @public
245+
*/
246+
export abstract class PackageManagerOptionsConfigurationBase implements IPackageManagerOptionsJsonBase {
247+
/**
248+
* Enviroment variables for the package manager
249+
*/
250+
public readonly environmentVariables?: IConfigurationEnvironment
251+
252+
/** @internal */
253+
protected constructor(json: IPackageManagerOptionsJsonBase) {
254+
this.environmentVariables = json.environmentVariables;
255+
}
256+
}
257+
258+
/**
259+
* Options that are only used when the NPM package manager is selected.
260+
*
261+
* @remarks
262+
* It is valid to define these options in rush.json even if the NPM package manager
263+
* is not being used.
264+
*
265+
* @public
266+
*/
267+
export class NpmOptionsConfiguration extends PackageManagerOptionsConfigurationBase {
268+
/** @internal */
269+
public constructor(json: INpmOptionsJson) {
270+
super(json);
271+
}
272+
}
273+
185274
/**
186275
* Options that are only used when the PNPM package manager is selected.
187276
*
@@ -191,7 +280,7 @@ export interface ICurrentVariantJson {
191280
*
192281
* @public
193282
*/
194-
export class PnpmOptionsConfiguration {
283+
export class PnpmOptionsConfiguration extends PackageManagerOptionsConfigurationBase {
195284
/**
196285
* The method used to resolve the store used by PNPM.
197286
*
@@ -240,6 +329,7 @@ export class PnpmOptionsConfiguration {
240329

241330
/** @internal */
242331
public constructor(json: IPnpmOptionsJson, commonTempFolder: string) {
332+
super(json);
243333
this.pnpmStore = json.pnpmStore || 'local';
244334
if (EnvironmentConfiguration.pnpmStorePathOverride) {
245335
this.pnpmStorePath = EnvironmentConfiguration.pnpmStorePathOverride;
@@ -262,7 +352,7 @@ export class PnpmOptionsConfiguration {
262352
*
263353
* @public
264354
*/
265-
export class YarnOptionsConfiguration {
355+
export class YarnOptionsConfiguration extends PackageManagerOptionsConfigurationBase {
266356
/**
267357
* If true, then Rush will add the "--ignore-engines" option when invoking Yarn.
268358
* This allows "rush install" to succeed if there are dependencies with engines defined in
@@ -274,6 +364,7 @@ export class YarnOptionsConfiguration {
274364

275365
/** @internal */
276366
public constructor(json: IYarnOptionsJson) {
367+
super(json);
277368
this.ignoreEngines = !!json.ignoreEngines;
278369
}
279370
}
@@ -351,6 +442,7 @@ export class RushConfiguration {
351442
private _repositoryDefaultBranch: string;
352443
private _repositoryDefaultRemote: string;
353444

445+
private _npmOptions: NpmOptionsConfiguration;
354446
private _pnpmOptions: PnpmOptionsConfiguration;
355447
private _yarnOptions: YarnOptionsConfiguration;
356448

@@ -420,6 +512,7 @@ export class RushConfiguration {
420512
);
421513
this._experimentsConfiguration = new ExperimentsConfiguration(experimentsConfigFile);
422514

515+
this._npmOptions = new NpmOptionsConfiguration(rushConfigurationJson.npmOptions || {});
423516
this._pnpmOptions = new PnpmOptionsConfiguration(rushConfigurationJson.pnpmOptions || {},
424517
this._commonTempFolder);
425518
this._yarnOptions = new YarnOptionsConfiguration(rushConfigurationJson.yarnOptions || {});
@@ -463,10 +556,10 @@ export class RushConfiguration {
463556
this._shrinkwrapFilename = this._packageManagerWrapper.shrinkwrapFilename;
464557

465558
this._tempShrinkwrapFilename = path.join(
466-
this._commonTempFolder, this._shrinkwrapFilename
559+
this._commonTempFolder, this._shrinkwrapFilename
467560
);
468561
this._packageManagerToolFilename = path.resolve(path.join(
469-
this._commonTempFolder, `${this.packageManager}-local`, 'node_modules', '.bin', `${this.packageManager}`
562+
this._commonTempFolder, `${this.packageManager}-local`, 'node_modules', '.bin', `${this.packageManager}`
470563
));
471564

472565
/// From "C:\repo\common\temp\pnpm-lock.yaml" --> "C:\repo\common\temp\pnpm-lock-preinstall.yaml"
@@ -475,9 +568,9 @@ export class RushConfiguration {
475568
parsedPath.name + '-preinstall' + parsedPath.ext);
476569

477570
RushConfiguration._validateCommonRushConfigFolder(
478-
this._commonRushConfigFolder,
479-
this.packageManager,
480-
this._shrinkwrapFilename
571+
this._commonRushConfigFolder,
572+
this.packageManager,
573+
this._shrinkwrapFilename
481574
);
482575

483576
this._projectFolderMinDepth = rushConfigurationJson.projectFolderMinDepth !== undefined
@@ -642,11 +735,11 @@ export class RushConfiguration {
642735
if (semver.major(Rush.version) !== semver.major(expectedRushVersion)
643736
|| semver.minor(Rush.version) !== semver.minor(expectedRushVersion)) {
644737

645-
// If the major/minor are different, then make sure it's an older version.
646-
if (semver.lt(Rush.version, expectedRushVersion)) {
647-
throw new Error(`Unable to load ${rushJsonBaseName} because its RushVersion is`
648-
+ ` ${rushConfigurationJson.rushVersion}, whereas @microsoft/rush-lib is version ${Rush.version}.`
649-
+ ` Consider upgrading the library.`);
738+
// If the major/minor are different, then make sure it's an older version.
739+
if (semver.lt(Rush.version, expectedRushVersion)) {
740+
throw new Error(`Unable to load ${rushJsonBaseName} because its RushVersion is`
741+
+ ` ${rushConfigurationJson.rushVersion}, whereas @microsoft/rush-lib is version ${Rush.version}.`
742+
+ ` Consider upgrading the library.`);
650743
}
651744
}
652745
}
@@ -741,9 +834,9 @@ export class RushConfiguration {
741834
* recognized config files.
742835
*/
743836
private static _validateCommonRushConfigFolder(
744-
commonRushConfigFolder: string,
745-
packageManager: PackageManagerName,
746-
shrinkwrapFilename: string
837+
commonRushConfigFolder: string,
838+
packageManager: PackageManagerName,
839+
shrinkwrapFilename: string
747840
): void {
748841
if (!FileSystem.exists(commonRushConfigFolder)) {
749842
console.log(`Creating folder: ${commonRushConfigFolder}`);
@@ -1136,6 +1229,13 @@ export class RushConfiguration {
11361229
return this._projectsByName;
11371230
}
11381231

1232+
/**
1233+
* {@inheritDoc NpmOptionsConfiguration}
1234+
*/
1235+
public get npmOptions(): NpmOptionsConfiguration {
1236+
return this._npmOptions;
1237+
}
1238+
11391239
/**
11401240
* {@inheritDoc PnpmOptionsConfiguration}
11411241
*/

apps/rush-lib/src/cli/actions/BaseInstallAction.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { StandardScriptUpdater } from '../../logic/StandardScriptUpdater';
1919
import { Stopwatch } from '../../utilities/Stopwatch';
2020
import { VersionMismatchFinder } from '../../logic/versionMismatch/VersionMismatchFinder';
2121
import { Variants } from '../../api/Variants';
22+
import { RushConstants } from '../../logic/RushConstants';
2223

2324
/**
2425
* This is the common base class for InstallAction and UpdateAction.
@@ -30,6 +31,7 @@ export abstract class BaseInstallAction extends BaseRushAction {
3031
protected _noLinkParameter: CommandLineFlagParameter;
3132
protected _networkConcurrencyParameter: CommandLineIntegerParameter;
3233
protected _debugPackageManagerParameter: CommandLineFlagParameter;
34+
protected _maxInstallAttempts: CommandLineIntegerParameter;
3335

3436
protected onDefineParameters(): void {
3537
this._purgeParameter = this.defineFlagParameter({
@@ -59,6 +61,12 @@ export abstract class BaseInstallAction extends BaseRushAction {
5961
description: 'Activates verbose logging for the package manager. You will probably want to pipe'
6062
+ ' the output of Rush to a file when using this command.'
6163
});
64+
this._maxInstallAttempts = this.defineIntegerParameter({
65+
parameterLongName: '--max-install-attempts',
66+
argumentName: 'NUMBER',
67+
description: `Overrides the default maximum number of install attempts.`,
68+
defaultValue: RushConstants.defaultMaxInstallAttempts
69+
});
6270
this._variant = this.defineStringParameter(Variants.VARIANT_PARAMETER);
6371
}
6472

@@ -96,6 +104,12 @@ export abstract class BaseInstallAction extends BaseRushAction {
96104
}
97105
}
98106

107+
// Because the 'defautltValue' option on the _maxInstallAttempts parameter is set,
108+
// it is safe to assume that the value is not null
109+
if (this._maxInstallAttempts.value! < 1) {
110+
throw new Error(`The value of "${this._maxInstallAttempts.longName}" must be positive and nonzero.`);
111+
}
112+
99113
const installManagerOptions: IInstallManagerOptions = this.buildInstallOptions();
100114

101115
const installManager: InstallManager = new InstallManager(

apps/rush-lib/src/cli/actions/InstallAction.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ export class InstallAction extends BaseInstallAction {
3434
recheckShrinkwrap: false,
3535
networkConcurrency: this._networkConcurrencyParameter.value,
3636
collectLogFile: this._debugPackageManagerParameter.value!,
37-
variant: this._variant.value
37+
variant: this._variant.value,
38+
// Because the 'defautltValue' option on the _maxInstallAttempts parameter is set,
39+
// it is safe to assume that the value is not null
40+
maxInstallAttempts: this._maxInstallAttempts.value!
3841
};
3942
}
4043
}

apps/rush-lib/src/cli/actions/UpdateAction.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ export class UpdateAction extends BaseInstallAction {
6262
recheckShrinkwrap: this._recheckParameter.value!,
6363
networkConcurrency: this._networkConcurrencyParameter.value,
6464
collectLogFile: this._debugPackageManagerParameter.value!,
65-
variant: this._variant.value
65+
variant: this._variant.value,
66+
// Because the 'defautltValue' option on the _maxInstallAttempts parameter is set,
67+
// it is safe to assume that the value is not null
68+
maxInstallAttempts: this._maxInstallAttempts.value!
6669
};
6770
}
6871
}

apps/rush-lib/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ Optional arguments:
302302
exports[`CommandLineHelp prints the help for each action: install 1`] = `
303303
"usage: rush install [-h] [-p] [--bypass-policy] [--no-link]
304304
[--network-concurrency COUNT] [--debug-package-manager]
305-
[--variant VARIANT]
305+
[--max-install-attempts NUMBER] [--variant VARIANT]
306306
307307
308308
The \\"rush install\\" command installs package dependencies for all your
@@ -336,6 +336,9 @@ Optional arguments:
336336
Activates verbose logging for the package manager.
337337
You will probably want to pipe the output of Rush to
338338
a file when using this command.
339+
--max-install-attempts NUMBER
340+
Overrides the default maximum number of install
341+
attempts. The default value is 3.
339342
--variant VARIANT Run command using a variant installation
340343
configuration. This parameter may alternatively
341344
specified via the RUSH_VARIANT environment variable.
@@ -563,7 +566,8 @@ Optional arguments:
563566
exports[`CommandLineHelp prints the help for each action: update 1`] = `
564567
"usage: rush update [-h] [-p] [--bypass-policy] [--no-link]
565568
[--network-concurrency COUNT] [--debug-package-manager]
566-
[--variant VARIANT] [--full] [--recheck]
569+
[--max-install-attempts NUMBER] [--variant VARIANT]
570+
[--full] [--recheck]
567571
568572
569573
The \\"rush update\\" command installs the dependencies described in your package.
@@ -596,6 +600,9 @@ Optional arguments:
596600
Activates verbose logging for the package manager.
597601
You will probably want to pipe the output of Rush to
598602
a file when using this command.
603+
--max-install-attempts NUMBER
604+
Overrides the default maximum number of install
605+
attempts. The default value is 3.
599606
--variant VARIANT Run command using a variant installation
600607
configuration. This parameter may alternatively
601608
specified via the RUSH_VARIANT environment variable.

apps/rush-lib/src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,16 @@ export {
1414
RushConfiguration,
1515
ITryFindRushJsonLocationOptions,
1616
ResolutionStrategy,
17-
PnpmOptionsConfiguration,
17+
IPackageManagerOptionsJsonBase,
18+
IConfigurationEnvironment,
19+
IConfigurationEnvironmentVariable,
20+
INpmOptionsJson as _INpmOptionsJson,
1821
IPnpmOptionsJson as _IPnpmOptionsJson,
22+
IYarnOptionsJson as _IYarnOptionsJson,
1923
PnpmStoreOptions,
24+
PackageManagerOptionsConfigurationBase,
25+
PnpmOptionsConfiguration,
26+
NpmOptionsConfiguration,
2027
YarnOptionsConfiguration
2128
} from './api/RushConfiguration';
2229

0 commit comments

Comments
 (0)