Skip to content

Commit

Permalink
feat: add installRemotely and versionRange new parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
Pedro authored and pedro-gomes-92 committed Nov 21, 2024
1 parent 0056279 commit 77f5c20
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 67 deletions.
2 changes: 2 additions & 0 deletions common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,8 @@ export class RushConfigurationProject {
get versionPolicy(): VersionPolicy | undefined;
// @beta
readonly versionPolicyName: string | undefined;
// @beta
readonly versionRange: string | undefined;
}

// @beta
Expand Down
28 changes: 24 additions & 4 deletions libraries/rush-lib/src/api/RushConfigurationProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface IRushConfigurationProjectJson {
tags?: string[];
subspaceName?: string;
installRemotely?: boolean;
versionRange?: string;
}

/**
Expand Down Expand Up @@ -59,9 +60,14 @@ export interface IRushConfigurationProjectOptions {
subspace: Subspace;

/**
* If specified, it will be downloaded from the external database or workspace.
* If specified, package will be downloaded by NPM registry.
*/
installRemotely?: boolean;

/**
* If specified, it will be downloaded according to the NPM version range (ignored if installRemotely=false).
*/
versionRange?: boolean;
}

/**
Expand Down Expand Up @@ -133,11 +139,19 @@ export class RushConfigurationProject {
/**
*
* Indicates how this project should be installed by rush add.
* Default value is "false".
* Default value is "true".
*
* @beta
*/
public readonly installRemotely: boolean = true;

/**
*
* Indicates which version should be installed by rush add. Ignored if installRemotely=false.
*
* @beta
*/
public readonly installRemotely: boolean = false;
public readonly versionRange: string | undefined;

/**
* A list of local projects that appear as devDependencies for this project, but cannot be
Expand Down Expand Up @@ -224,11 +238,17 @@ export class RushConfigurationProject {
/** @internal */
public constructor(options: IRushConfigurationProjectOptions) {
const { projectJson, rushConfiguration, tempProjectName, allowedProjectTags } = options;
const { packageName, projectFolder: projectRelativeFolder, installRemotely = false } = projectJson;
const {
packageName,
projectFolder: projectRelativeFolder,
installRemotely = true,
versionRange
} = projectJson;
this.rushConfiguration = rushConfiguration;
this.packageName = packageName;
this.projectRelativeFolder = projectRelativeFolder;
this.installRemotely = installRemotely;
this.versionRange = versionRange;

validateRelativePathField(projectRelativeFolder, 'projectFolder', rushConfiguration.rushJsonFile);

Expand Down
163 changes: 101 additions & 62 deletions libraries/rush-lib/src/logic/PackageJsonUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,86 @@ export class PackageJsonUpdater {
}
}

private async _getRemoteLatestVersionAsync(packageName: string): Promise<string> {
let selectedVersion: string | undefined;
this._terminal.writeLine(`Querying NPM registry for latest version of "${packageName}"...`);

let commandArgs: string[];
if (this._rushConfiguration.packageManager === 'yarn') {
commandArgs = ['info', packageName, 'dist-tags.latest', '--silent'];
} else {
commandArgs = ['view', `${packageName}@latest`, 'version'];
}

selectedVersion = (
await Utilities.executeCommandAndCaptureOutputAsync(
this._rushConfiguration.packageManagerToolFilename,
commandArgs,
this._rushConfiguration.commonTempFolder
)
).trim();

this._terminal.writeLine();
this._terminal.writeLine(`Found latest version: ${Colorize.cyan(selectedVersion)}`);

return selectedVersion;
}

private async _getRemoteSpecifiedVersionAsync(
packageName: string,
initialSpec: string = 'latest'
): Promise<string> {
let selectedVersion: string | undefined;
this._terminal.writeLine(`Querying registry for all versions of "${packageName}"...`);

let commandArgs: string[];
if (this._rushConfiguration.packageManager === 'yarn') {
commandArgs = ['info', packageName, 'versions', '--json'];
} else {
commandArgs = ['view', packageName, 'versions', '--json'];
}

const allVersions: string = await Utilities.executeCommandAndCaptureOutputAsync(
this._rushConfiguration.packageManagerToolFilename,
commandArgs,
this._rushConfiguration.commonTempFolder
);

let versionList: string[];
if (this._rushConfiguration.packageManager === 'yarn') {
versionList = JSON.parse(allVersions).data;
} else {
versionList = JSON.parse(allVersions);
}

this._terminal.writeLine(Colorize.gray(`Found ${versionList.length} available versions.`));

for (const version of versionList) {
if (semver.satisfies(version, initialSpec)) {
selectedVersion = initialSpec;
this._terminal.writeLine(`Found a version that satisfies ${initialSpec}: ${Colorize.cyan(version)}`);
break;
}
}

if (!selectedVersion) {
throw new Error(
`Unable to find a version of "${packageName}" that satisfies` +
` the version specifier "${initialSpec}"`
);
}

return selectedVersion;
}

private async _getRemoteVersionAsync(packageName: string, initialSpec: string = 'latest'): Promise<string> {
if (initialSpec === 'latest') {
return this._getRemoteLatestVersionAsync(packageName);
}

return this._getRemoteSpecifiedVersionAsync(packageName, initialSpec);
}

/**
* Selects an appropriate version number for a particular package, given an optional initial SemVer spec.
* If ensureConsistentVersions, tries to pick a version that will be consistent.
Expand Down Expand Up @@ -659,7 +739,9 @@ export class PackageJsonUpdater {
if (semver.satisfies(version, initialSpec)) {
// For workspaces, assume that specifying the exact version means you always want to consume
// the local project. Otherwise, use the exact local package version
if (useWorkspaces && !localProject.installRemotely) {
if (localProject.installRemotely) {
selectedVersion = await this._getRemoteVersionAsync(packageName, initialSpec);
} else if (useWorkspaces) {
selectedVersion = initialSpec === version ? '*' : initialSpec;
selectedVersionPrefix = workspacePrefix;
} else {
Expand All @@ -675,52 +757,26 @@ export class PackageJsonUpdater {
);
}
} else {
this._terminal.writeLine(`Querying registry for all versions of "${packageName}"...`);

let commandArgs: string[];
if (this._rushConfiguration.packageManager === 'yarn') {
commandArgs = ['info', packageName, 'versions', '--json'];
} else {
commandArgs = ['view', packageName, 'versions', '--json'];
}

const allVersions: string = await Utilities.executeCommandAndCaptureOutputAsync(
this._rushConfiguration.packageManagerToolFilename,
commandArgs,
this._rushConfiguration.commonTempFolder
);

let versionList: string[];
if (this._rushConfiguration.packageManager === 'yarn') {
versionList = JSON.parse(allVersions).data;
} else {
versionList = JSON.parse(allVersions);
}

this._terminal.writeLine(Colorize.gray(`Found ${versionList.length} available versions.`));

for (const version of versionList) {
if (semver.satisfies(version, initialSpec)) {
selectedVersion = initialSpec;
this._terminal.writeLine(
`Found a version that satisfies ${initialSpec}: ${Colorize.cyan(version)}`
);
break;
}
}

if (!selectedVersion) {
throw new Error(
`Unable to find a version of "${packageName}" that satisfies` +
` the version specifier "${initialSpec}"`
);
}
// if the package is not a project in the local repository, then we need to query the registry
// to find the latest version that satisfies the spec
selectedVersion = await this._getRemoteVersionAsync(packageName, initialSpec);
}
} else {
if (localProject !== undefined) {
// For workspaces, assume that no specified version range means you always want to consume
// the local project. Otherwise, use the exact local package version
if (useWorkspaces && !localProject.installRemotely) {
if (localProject.installRemotely) {
selectedVersion = await this._getRemoteVersionAsync(packageName, localProject.versionRange);
this._terminal.writeLine(
Colorize.green('Assigning "') +
Colorize.cyan(selectedVersion) +
Colorize.green(
`" for "${packageName}" because it is the preferred version defined by ${RushConstants.rushJsonFilename}.`
)
);

return selectedVersion;
} else if (useWorkspaces) {
selectedVersion = '*';
selectedVersionPrefix = workspacePrefix;
} else {
Expand All @@ -736,27 +792,10 @@ export class PackageJsonUpdater {
this._terminal.writeLine();
}

this._terminal.writeLine(`Querying NPM registry for latest version of "${packageName}"...`);

let commandArgs: string[];
if (this._rushConfiguration.packageManager === 'yarn') {
commandArgs = ['info', packageName, 'dist-tags.latest', '--silent'];
} else {
commandArgs = ['view', `${packageName}@latest`, 'version'];
}

selectedVersion = (
await Utilities.executeCommandAndCaptureOutputAsync(
this._rushConfiguration.packageManagerToolFilename,
commandArgs,
this._rushConfiguration.commonTempFolder
)
).trim();
// if the package is not a project in the local repository with no spec defined, then we need to
// query the registry to find the latest version
selectedVersion = await this._getRemoteVersionAsync(packageName);
}

this._terminal.writeLine();

this._terminal.writeLine(`Found latest version: ${Colorize.cyan(selectedVersion)}`);
}

this._terminal.writeLine();
Expand Down
6 changes: 5 additions & 1 deletion libraries/rush-lib/src/schemas/rush.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,13 @@
"type": "string"
},
"installRemotely": {
"description": "Specifies the installation type: \"remote\" or \"workspace\". The default value is false.",
"description": "If true, then this project will be installed by NPM registry. The default value is true.",
"type": "boolean"
},
"versionRange": {
"description": "Specifies the package version, using version range syntax.",
"type": "string"
},
"tags": {
"description": "An optional set of custom tags that can be used to select this project. For example, adding \"my-custom-tag\" will allow this project to be selected by the command \"rush list --only tag:my-custom-tag\". The tag name must be one or more words separated by hyphens or slashes, where a word may contain lowercase ASCII letters, digits, \".\", and \"@\" characters.",
"type": "array",
Expand Down

0 comments on commit 77f5c20

Please sign in to comment.