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] Patch for subspaces feature #4500

Merged
merged 9 commits into from
Jan 31, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Fix issues with subspace targeted installs",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
4 changes: 3 additions & 1 deletion common/reviews/api/rush-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1375,13 +1375,15 @@ export class Subspace {
export class SubspacesConfiguration {
// @internal
static _convertNameToEnvironmentVariable(subspaceName: string, splitWorkspaceCompatibility: boolean): string;
readonly enabled: boolean;
static explainIfInvalidSubspaceName(subspaceName: string, splitWorkspaceCompatibility?: boolean): string | undefined;
readonly preventSelectingAllSubspaces: boolean;
static requireValidSubspaceName(subspaceName: string, splitWorkspaceCompatibility?: boolean): void;
readonly splitWorkspaceCompatibility: boolean;
readonly subspaceJsonFilePath: string;
readonly subspaceNames: ReadonlySet<string>;
// (undocumented)
readonly subspacesEnabled: boolean;
// (undocumented)
static tryLoadFromConfigurationFile(subspaceJsonFilePath: string): SubspacesConfiguration | undefined;
// (undocumented)
static tryLoadFromDefaultLocation(rushConfiguration: RushConfiguration): SubspacesConfiguration | undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
/**
* This is the main configuration file for Rush.
* This configuration file manages the experimental "subspaces" feature for Rush,
* which allows multiple PNPM lockfiles to be used in a single Rush workspace.
* For full documentation, please see https://rushjs.io
*/
{
{
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/subspaces.schema.json",

/**
* Temporarily use the default schema until the subspaces schema is uploaded
* Set this flag to "true" to enable usage of subspaces.
*/
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/subspaces.schema.json",
"subspacesEnabled": false,

/**
* This specifies whether or not to use the "subspaces" feature in rush. This allows grouping of
* projects into discrete workspaces known as "subspaces".
* (DEPRECATED) This is a temporary workaround for migrating from an earlier prototype
* of this feature: https://github.com/microsoft/rushstack/pull/3481
* It allows subspaces with only one project to store their config files in the project folder.
*/
"enabled": false,
"splitWorkspaceCompatibility": false,

/**
* (DEPRECATED) A property that allows subspace configuration files to be stored under project folders directly.
* Used for migrating from a split-workspace format where there may be a large number of individual lockfiles and
* starting subspaces.
* When a command such as "rush update" is invoked without the "--subspace" or "--to"
* parameters, Rush will install all subspaces. In a huge monorepo with numerous subspaces,
* this would be extremely slow. Set "preventSelectingAllSubspaces" to true to avoid this
* mistake by always requiring selection parameters for commands such as "rush update".
*/
"splitWorkspaceCompatibility": false,
"preventSelectingAllSubspaces": false,

/**
* The next field is an object describing the available subspaces in this workspace. Each individual subspace
* must be defined in this array.
*/
/**
* The list of subspace names, which should be lowercase alphanumeric words separated by
* hyphens, for example "my-subspace". The corresponding config files will have paths
* such as "common/config/subspaces/my-subspace/package-lock.yaml".
*/
"subspaceNames": []

}
4 changes: 2 additions & 2 deletions libraries/rush-lib/src/api/RushConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ export class RushConfiguration {

// Try getting a subspace configuration
this.subspacesConfiguration = SubspacesConfiguration.tryLoadFromDefaultLocation(this);
this.subspacesFeatureEnabled = !!this.subspacesConfiguration?.enabled;
this.subspacesFeatureEnabled = !!this.subspacesConfiguration?.subspacesEnabled;

this._subspacesByName = new Map();

Expand Down Expand Up @@ -860,7 +860,7 @@ export class RushConfiguration {
// Build the subspaces map
const subspaceNames: string[] = [];
let splitWorkspaceCompatibility: boolean = false;
if (this.subspacesConfiguration?.enabled) {
if (this.subspacesConfiguration?.subspacesEnabled) {
splitWorkspaceCompatibility = this.subspacesConfiguration.splitWorkspaceCompatibility;

subspaceNames.push(...this.subspacesConfiguration.subspaceNames);
Expand Down
17 changes: 12 additions & 5 deletions libraries/rush-lib/src/api/SubspacesConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ export const SPLIT_WORKSPACE_SUBSPACE_NAME_REGEXP: RegExp = /^[a-z0-9][+_\-a-z0-
* See subspace.schema.json for documentation.
*/
interface ISubspacesConfigurationJson {
enabled: boolean;
subspacesEnabled: boolean;
splitWorkspaceCompatibility?: boolean;
preventSelectingAllSubspaces?: boolean;
subspaceNames: string[];
}

Expand All @@ -39,28 +40,34 @@ export class SubspacesConfiguration {
*/
public readonly subspaceJsonFilePath: string;

/**
/*
* Determines if the subspace feature is enabled
*/
public readonly enabled: boolean;
public readonly subspacesEnabled: boolean;

/**
* This determines if the subspaces feature supports adding configuration files under the project folder itself
*/
public readonly splitWorkspaceCompatibility: boolean;

/**
* This determines if selectors are required when installing and building
*/
public readonly preventSelectingAllSubspaces: boolean;

/**
* A set of the available subspaces
*/
public readonly subspaceNames: ReadonlySet<string>;

private constructor(configuration: Readonly<ISubspacesConfigurationJson>, subspaceJsonFilePath: string) {
this.subspaceJsonFilePath = subspaceJsonFilePath;
this.enabled = configuration.enabled;
this.subspacesEnabled = configuration.subspacesEnabled;
this.splitWorkspaceCompatibility = !!configuration.splitWorkspaceCompatibility;
this.preventSelectingAllSubspaces = !!configuration.preventSelectingAllSubspaces;
const subspaceNames: Set<string> = new Set();
for (const subspaceName of configuration.subspaceNames) {
SubspacesConfiguration.requireValidSubspaceName(subspaceName, this.enabled);
SubspacesConfiguration.requireValidSubspaceName(subspaceName, this.subspacesEnabled);

subspaceNames.add(subspaceName);
}
Expand Down
83 changes: 42 additions & 41 deletions libraries/rush-lib/src/cli/actions/BaseInstallAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,50 +117,57 @@ export abstract class BaseInstallAction extends BaseRushAction {

protected getTargetSubspace(): Subspace {
const parameterValue: string | undefined = this._subspaceParameter.value;

if (this.rushConfiguration.subspacesFeatureEnabled) {
if (!parameterValue) {
// Temporarily ensure that a subspace is provided
// eslint-disable-next-line no-console
console.log();
// eslint-disable-next-line no-console
console.log(
colors.red(
`The subspaces feature currently only supports installing for a specified set of subspace,` +
` passed by the "--subspace" parameter or selected from targeted projects using any project selector.`
)
);
throw new AlreadyReportedError();
}
return this.rushConfiguration.getSubspace(parameterValue);
} else {
if (parameterValue) {
// eslint-disable-next-line no-console
console.log();
// eslint-disable-next-line no-console
console.log(
colors.red(
`The "--subspace" parameter can only be passed if the "enabled" option is enabled in subspaces.json.`
)
);
throw new AlreadyReportedError();
}
return this.rushConfiguration.defaultSubspace;
if (parameterValue && !this.rushConfiguration.subspacesFeatureEnabled) {
// eslint-disable-next-line no-console
console.log();
// eslint-disable-next-line no-console
console.log(
colors.red(
`The "--subspace" parameter can only be passed if the "enabled" option is enabled in subspaces.json.`
)
);
throw new AlreadyReportedError();
}
const selectedSubspace: Subspace | undefined = parameterValue
? this.rushConfiguration.getSubspace(parameterValue)
: this.rushConfiguration.defaultSubspace;
return selectedSubspace;
}

protected async runAsync(): Promise<void> {
const installManagerOptions: IInstallManagerOptions = await this.buildInstallOptionsAsync();

// If we are doing a filtered install and subspaces is enabled, we need to find the affected subspaces and install for all of them.
let selectedSubspaces: ReadonlySet<Subspace> | undefined;
if (installManagerOptions.pnpmFilterArguments.length && this.rushConfiguration.subspacesFeatureEnabled) {
const selectedProjects: Set<RushConfigurationProject> | undefined =
await this._selectionParameters?.getSelectedProjectsAsync(this._terminal);
if (selectedProjects) {
selectedSubspaces = this.rushConfiguration.getSubspacesForProjects(selectedProjects);
if (this.rushConfiguration.subspacesFeatureEnabled) {
if (installManagerOptions.pnpmFilterArguments.length) {
// Selecting a set of subspaces
const selectedProjects: Set<RushConfigurationProject> | undefined =
await this._selectionParameters?.getSelectedProjectsAsync(this._terminal);
if (selectedProjects) {
selectedSubspaces = this.rushConfiguration.getSubspacesForProjects(selectedProjects);
} else {
throw new Error('The specified filter arguments resulted in no projects being selected.');
}
} else if (this._subspaceParameter.value) {
// Selecting a single subspace
const selectedSubspace: Subspace = this.rushConfiguration.getSubspace(this._subspaceParameter.value);
selectedSubspaces = new Set<Subspace>([selectedSubspace]);
} else {
throw new Error('The specified filter arguments resulted in no projects being selected.');
// Selecting all subspaces if preventSelectingAllSubspaces is not enabled in subspaces.json
if (!this.rushConfiguration.subspacesConfiguration?.preventSelectingAllSubspaces) {
selectedSubspaces = new Set<Subspace>(this.rushConfiguration.subspaces);
} else {
// eslint-disable-next-line no-console
console.log();
// eslint-disable-next-line no-console
console.log(
colors.red(
`The subspaces preventSelectingAllSubspaces configuration is enabled, which enforces installation for a specified set of subspace,` +
` passed by the "--subspace" parameter or selected from targeted projects using any project selector.`
)
);
}
}
}

Expand All @@ -172,12 +179,6 @@ export abstract class BaseInstallAction extends BaseRushAction {
subspace
});
}
} else if (this._subspaceParameter.value) {
const subspace: Subspace = this.rushConfiguration.getSubspace(this._subspaceParameter.value);
VersionMismatchFinder.ensureConsistentVersions(this.rushConfiguration, this._terminal, {
variant: this._variant.value,
subspace: subspace
});
} else {
VersionMismatchFinder.ensureConsistentVersions(this.rushConfiguration, this._terminal, {
variant: this._variant.value
Expand Down
1 change: 1 addition & 0 deletions libraries/rush-lib/src/cli/actions/InitAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export class InitAction extends BaseConfiglessRushAction {
'common/config/rush/experiments.json',
'common/config/rush/pnpm-config.json',
'common/config/rush/rush-plugins.json',
'common/config/rush/subspaces.json',
'common/config/rush/version-policies.json',

'common/git-hooks/commit-msg.sample',
Expand Down
8 changes: 6 additions & 2 deletions libraries/rush-lib/src/schemas/subspaces.schema.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Rush subspace config file.",
"description": "The configuration file for enabling the subspaces feature in rush. This is an EXPERIMENTAL feature which is not yet fully implemented. To opt into the experiemnt, simply toggle the 'enabled' property in this file.",
"description": "The configuration file for enabling the subspaces feature in rush. This is an EXPERIMENTAL feature which is not yet fully implemented. To opt into the experiment, simply toggle the 'enabled' property in this file.",
"type": "object",

"properties": {
"$schema": {
"description": "Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.",
"type": "string"
},
"enabled": {
"subspacesEnabled": {
"description": "If true, rush will use the subspaces configuration.",
"type": "boolean"
},
"splitWorkspaceCompatibility": {
"description": "(DEPRECATED) Allows individual subspaces to be configured at the package level if that package is the only project in the subspace. Used to help migrate from a split workspace state.",
"type": "boolean"
},
"preventSelectingAllSubspaces": {
"description": "If true, requires a selector for a subspace or set of subspaces when installing.",
"type": "boolean"
},
"subspaceNames": {
"description": "Individual subspace configurations.",
"type": "array",
Expand Down
Loading