-
Notifications
You must be signed in to change notification settings - Fork 609
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] Add support for RUSH_OVERRIDE_PATH environment variable #5003
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,69 +24,83 @@ export class RushVersionSelector { | |
|
||
public async ensureRushVersionInstalledAsync( | ||
version: string, | ||
overridePath: string | undefined, | ||
configuration: MinimalRushConfiguration | undefined, | ||
executeOptions: ILaunchOptions | ||
): Promise<void> { | ||
const isLegacyRushVersion: boolean = semver.lt(version, '4.0.0'); | ||
const expectedRushPath: string = path.join(this._rushGlobalFolder.nodeSpecificPath, `rush-${version}`); | ||
|
||
const installMarker: _FlagFile = new _FlagFile(expectedRushPath, 'last-install', { | ||
node: process.versions.node | ||
}); | ||
let rushPath: string; | ||
|
||
let installIsValid: boolean = await installMarker.isValidAsync(); | ||
if (!installIsValid) { | ||
// Need to install Rush | ||
console.log(`Rush version ${version} is not currently installed. Installing...`); | ||
|
||
const resourceName: string = `rush-${version}`; | ||
|
||
console.log(`Trying to acquire lock for ${resourceName}`); | ||
if (overridePath) { | ||
rushPath = overridePath; | ||
} else { | ||
const expectedRushPath: string = path.join(this._rushGlobalFolder.nodeSpecificPath, `rush-${version}`); | ||
|
||
const installMarker: _FlagFile = new _FlagFile(expectedRushPath, 'last-install', { | ||
node: process.versions.node | ||
}); | ||
|
||
let installIsValid: boolean = await installMarker.isValidAsync(); | ||
if (!installIsValid) { | ||
// Need to install Rush | ||
console.log(`Rush version ${version} is not currently installed. Installing...`); | ||
|
||
const resourceName: string = `rush-${version}`; | ||
|
||
console.log(`Trying to acquire lock for ${resourceName}`); | ||
|
||
const lock: LockFile = await LockFile.acquire(expectedRushPath, resourceName); | ||
installIsValid = await installMarker.isValidAsync(); | ||
if (installIsValid) { | ||
console.log('Another process performed the installation.'); | ||
} else { | ||
await Utilities.installPackageInDirectoryAsync({ | ||
directory: expectedRushPath, | ||
packageName: isLegacyRushVersion ? '@microsoft/rush' : '@microsoft/rush-lib', | ||
version: version, | ||
tempPackageTitle: 'rush-local-install', | ||
maxInstallAttempts: MAX_INSTALL_ATTEMPTS, | ||
// This is using a local configuration to install a package in a shared global location. | ||
// Generally that's a bad practice, but in this case if we can successfully install | ||
// the package at all, we can reasonably assume it's good for all the repositories. | ||
// In particular, we'll assume that two different NPM registries cannot have two | ||
// different implementations of the same version of the same package. | ||
// This was needed for: https://github.com/microsoft/rushstack/issues/691 | ||
commonRushConfigFolder: configuration ? configuration.commonRushConfigFolder : undefined, | ||
suppressOutput: true | ||
}); | ||
|
||
console.log(`Successfully installed Rush version ${version} in ${expectedRushPath}.`); | ||
|
||
// If we've made it here without exception, write the flag file | ||
await installMarker.createAsync(); | ||
|
||
lock.release(); | ||
} | ||
} | ||
|
||
const lock: LockFile = await LockFile.acquire(expectedRushPath, resourceName); | ||
installIsValid = await installMarker.isValidAsync(); | ||
if (installIsValid) { | ||
console.log('Another process performed the installation.'); | ||
if (semver.lt(version, '4.0.0')) { | ||
rushPath = path.join(expectedRushPath, 'node_modules', '@microsoft', 'rush'); | ||
} else { | ||
await Utilities.installPackageInDirectoryAsync({ | ||
directory: expectedRushPath, | ||
packageName: isLegacyRushVersion ? '@microsoft/rush' : '@microsoft/rush-lib', | ||
version: version, | ||
tempPackageTitle: 'rush-local-install', | ||
maxInstallAttempts: MAX_INSTALL_ATTEMPTS, | ||
// This is using a local configuration to install a package in a shared global location. | ||
// Generally that's a bad practice, but in this case if we can successfully install | ||
// the package at all, we can reasonably assume it's good for all the repositories. | ||
// In particular, we'll assume that two different NPM registries cannot have two | ||
// different implementations of the same version of the same package. | ||
// This was needed for: https://github.com/microsoft/rushstack/issues/691 | ||
commonRushConfigFolder: configuration ? configuration.commonRushConfigFolder : undefined, | ||
suppressOutput: true | ||
}); | ||
|
||
console.log(`Successfully installed Rush version ${version} in ${expectedRushPath}.`); | ||
|
||
// If we've made it here without exception, write the flag file | ||
await installMarker.createAsync(); | ||
|
||
lock.release(); | ||
rushPath = path.join(expectedRushPath, 'node_modules', '@microsoft', 'rush-lib'); | ||
} | ||
} | ||
|
||
if (semver.lt(version, '3.0.20')) { | ||
// In old versions, requiring the entry point invoked the command-line parser immediately, | ||
// so fail if "rushx" or "rush-pnpm" was used | ||
RushCommandSelector.failIfNotInvokedAsRush(version); | ||
require(path.join(expectedRushPath, 'node_modules', '@microsoft', 'rush', 'lib', 'rush')); | ||
require(path.join(rushPath, 'lib', 'rush')); | ||
} else if (semver.lt(version, '4.0.0')) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This condition is checked twice now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually looks like it's checked in a few places. |
||
// In old versions, requiring the entry point invoked the command-line parser immediately, | ||
// so fail if "rushx" or "rush-pnpm" was used | ||
RushCommandSelector.failIfNotInvokedAsRush(version); | ||
require(path.join(expectedRushPath, 'node_modules', '@microsoft', 'rush', 'lib', 'start')); | ||
require(path.join(rushPath, 'lib', 'start')); | ||
} else { | ||
// For newer rush-lib, RushCommandSelector can test whether "rushx" is supported or not | ||
const rushCliEntrypoint: {} = require( | ||
path.join(expectedRushPath, 'node_modules', '@microsoft', 'rush-lib', 'lib', 'index') | ||
path.join(rushPath, 'lib', 'index') | ||
); | ||
RushCommandSelector.execute(this._currentPackageVersion, rushCliEntrypoint, executeOptions); | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -21,34 +21,90 @@ const alreadyReportedNodeTooNewError: boolean = NodeJsCompatibility.warnAboutVer | |||||||||
import * as os from 'os'; | ||||||||||
import * as semver from 'semver'; | ||||||||||
|
||||||||||
import { Text, PackageJsonLookup } from '@rushstack/node-core-library'; | ||||||||||
import { Colorize, ConsoleTerminalProvider, type ITerminalProvider } from '@rushstack/terminal'; | ||||||||||
import { Text, PackageJsonLookup, type IPackageJson } from '@rushstack/node-core-library'; | ||||||||||
import { PrintUtilities, Colorize, ConsoleTerminalProvider, type ITerminalProvider, Terminal } from '@rushstack/terminal'; | ||||||||||
import { EnvironmentVariableNames } from '@microsoft/rush-lib'; | ||||||||||
import * as rushLib from '@microsoft/rush-lib'; | ||||||||||
|
||||||||||
import { RushCommandSelector } from './RushCommandSelector'; | ||||||||||
import { RushVersionSelector } from './RushVersionSelector'; | ||||||||||
import { MinimalRushConfiguration } from './MinimalRushConfiguration'; | ||||||||||
|
||||||||||
const terminalProvider: ITerminalProvider = new ConsoleTerminalProvider(); | ||||||||||
|
||||||||||
const terminal: Terminal = new Terminal(terminalProvider); | ||||||||||
|
||||||||||
// Load the configuration | ||||||||||
const configuration: MinimalRushConfiguration | undefined = | ||||||||||
MinimalRushConfiguration.loadFromDefaultLocation(); | ||||||||||
|
||||||||||
const currentPackageVersion: string = PackageJsonLookup.loadOwnPackageJson(__dirname).version; | ||||||||||
|
||||||||||
let rushVersionToLoad: string | undefined = undefined; | ||||||||||
let rushVersionToLoadInfo: { | ||||||||||
version: string; | ||||||||||
path?: string; | ||||||||||
} | undefined = undefined; | ||||||||||
|
||||||||||
const overridePath: string | undefined = process.env[EnvironmentVariableNames.RUSH_OVERRIDE_PATH]; | ||||||||||
|
||||||||||
const previewVersion: string | undefined = process.env[EnvironmentVariableNames.RUSH_PREVIEW_VERSION]; | ||||||||||
|
||||||||||
if (previewVersion) { | ||||||||||
if (overridePath) { | ||||||||||
const overridePackageJson: IPackageJson | undefined = PackageJsonLookup.instance.tryLoadPackageJsonFor(overridePath); | ||||||||||
|
||||||||||
if (overridePackageJson === undefined) { | ||||||||||
terminal.writeErrorLine(`Cannot use version specified with "RUSH_OVERRIDE_PATH" environment variable as it doesn't point to valid Rush package: ${overridePath}`); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
process.exit(1); | ||||||||||
} | ||||||||||
|
||||||||||
const overrideVersion: string = overridePackageJson.version; | ||||||||||
|
||||||||||
// If we are overriding with an older Rush that doesn't understand the RUSH_OVERRIDE_PATH variable, | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
// then unset it. | ||||||||||
if (semver.lt(overrideVersion, '5.141.0')) { | ||||||||||
delete process.env[EnvironmentVariableNames.RUSH_OVERRIDE_PATH]; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we just always delete the env var here? |
||||||||||
} | ||||||||||
|
||||||||||
PrintUtilities.printMessageInBox( | ||||||||||
[ | ||||||||||
`WARNING! THE "RUSH_OVERRIDE_PATH" ENVIRONMENT VARIABLE IS SET.`, | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
``, | ||||||||||
`You are using Rush@${overrideVersion} from ${overridePath}`, | ||||||||||
``, | ||||||||||
...( | ||||||||||
configuration | ||||||||||
? [ | ||||||||||
`The rush.json configuration asks for: @${configuration.rushVersion}`, | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
``, | ||||||||||
] | ||||||||||
: [] | ||||||||||
), | ||||||||||
`To restore the normal behavior, unset the "RUSH_OVERRIDE_PATH" environment variable.`, | ||||||||||
].join(os.EOL), | ||||||||||
terminal, | ||||||||||
); | ||||||||||
|
||||||||||
rushVersionToLoadInfo = { | ||||||||||
version: overrideVersion, | ||||||||||
path: overridePath, | ||||||||||
}; | ||||||||||
} else if (previewVersion) { | ||||||||||
if (!semver.valid(previewVersion, false)) { | ||||||||||
console.error( | ||||||||||
Colorize.red(`Invalid value for RUSH_PREVIEW_VERSION environment variable: "${previewVersion}"`) | ||||||||||
); | ||||||||||
process.exit(1); | ||||||||||
} | ||||||||||
|
||||||||||
rushVersionToLoad = previewVersion; | ||||||||||
// If we are previewing an older Rush that doesn't understand the RUSH_PREVIEW_VERSION variable, | ||||||||||
// then unset it. | ||||||||||
if (semver.lt(previewVersion, '5.0.0-dev.18')) { | ||||||||||
delete process.env[EnvironmentVariableNames.RUSH_PREVIEW_VERSION]; | ||||||||||
} | ||||||||||
|
||||||||||
rushVersionToLoadInfo = { | ||||||||||
version: previewVersion, | ||||||||||
}; | ||||||||||
|
||||||||||
const lines: string[] = []; | ||||||||||
lines.push( | ||||||||||
|
@@ -71,28 +127,22 @@ if (previewVersion) { | |||||||||
|
||||||||||
console.error(lines.map((line) => Colorize.black(Colorize.yellowBackground(line))).join(os.EOL)); | ||||||||||
} else if (configuration) { | ||||||||||
rushVersionToLoad = configuration.rushVersion; | ||||||||||
} | ||||||||||
|
||||||||||
// If we are previewing an older Rush that doesn't understand the RUSH_PREVIEW_VERSION variable, | ||||||||||
// then unset it. | ||||||||||
if (rushVersionToLoad && semver.lt(rushVersionToLoad, '5.0.0-dev.18')) { | ||||||||||
delete process.env[EnvironmentVariableNames.RUSH_PREVIEW_VERSION]; | ||||||||||
rushVersionToLoadInfo = { | ||||||||||
version: configuration.rushVersion, | ||||||||||
}; | ||||||||||
} | ||||||||||
|
||||||||||
// Rush is "managed" if its version and configuration are dictated by a repo's rush.json | ||||||||||
const isManaged: boolean = !!configuration; | ||||||||||
|
||||||||||
const terminalProvider: ITerminalProvider = new ConsoleTerminalProvider(); | ||||||||||
|
||||||||||
const launchOptions: rushLib.ILaunchOptions = { isManaged, alreadyReportedNodeTooNewError, terminalProvider }; | ||||||||||
|
||||||||||
// If we're inside a repo folder, and it's requesting a different version, then use the RushVersionManager to | ||||||||||
// install it | ||||||||||
if (rushVersionToLoad && rushVersionToLoad !== currentPackageVersion) { | ||||||||||
if (rushVersionToLoadInfo && (rushVersionToLoadInfo.version !== currentPackageVersion || rushVersionToLoadInfo.path !== undefined)) { | ||||||||||
const versionSelector: RushVersionSelector = new RushVersionSelector(currentPackageVersion); | ||||||||||
versionSelector | ||||||||||
.ensureRushVersionInstalledAsync(rushVersionToLoad, configuration, launchOptions) | ||||||||||
.ensureRushVersionInstalledAsync(rushVersionToLoadInfo.version, rushVersionToLoadInfo.path, configuration, launchOptions) | ||||||||||
.catch((error: Error) => { | ||||||||||
console.log(Colorize.red('Error: ' + error.message)); | ||||||||||
}); | ||||||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,10 @@ | ||||||
{ | ||||||
"changes": [ | ||||||
{ | ||||||
"packageName": "@microsoft/rush", | ||||||
"comment": "Add support for overriding rush with custom installation path through RUSH_OVERRIDE_PATH environment variable", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
"type": "none" | ||||||
} | ||||||
], | ||||||
"packageName": "@microsoft/rush" | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.