Skip to content

Commit

Permalink
Merge pull request #1938 from D4N14L/supportWorkspaces2
Browse files Browse the repository at this point in the history
[rush] Support PNPM workspaces
  • Loading branch information
iclanton authored Jul 3, 2020
2 parents ee9e9c5 + 0e8d05a commit 2b06f26
Show file tree
Hide file tree
Showing 84 changed files with 4,305 additions and 1,931 deletions.
4 changes: 2 additions & 2 deletions apps/api-extractor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"colors": "~1.2.1",
"lodash": "~4.17.15",
"resolve": "~1.17.0",
"semver": "~5.3.0",
"semver": "~7.3.0",
"source-map": "~0.6.1",
"typescript": "~3.9.5"
},
Expand All @@ -49,7 +49,7 @@
"@rushstack/eslint-config": "1.0.2",
"@types/jest": "25.2.1",
"@types/lodash": "4.14.116",
"@types/semver": "5.3.33",
"@types/semver": "~7.3.1",
"@types/node": "10.17.13",
"@types/resolve": "1.17.1",
"gulp": "~4.0.2"
Expand Down
20 changes: 18 additions & 2 deletions apps/rush-lib/assets/rush-init/rush.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* Specify one of: "pnpmVersion", "npmVersion", or "yarnVersion". See the Rush documentation
* for details about these alternatives.
*/
"pnpmVersion": "4.12.5",
"pnpmVersion": "4.14.4",

/*[LINE "HYPOTHETICAL"]*/ "npmVersion": "4.5.0",
/*[LINE "HYPOTHETICAL"]*/ "yarnVersion": "1.9.4",
Expand Down Expand Up @@ -94,7 +94,23 @@
*
* The default value is false.
*/
/*[LINE "HYPOTHETICAL"]*/ "preventManualShrinkwrapChanges": true
/*[LINE "HYPOTHETICAL"]*/ "preventManualShrinkwrapChanges": true,

/**
* If true, then `rush install` will use the PNPM workspaces feature to perform the
* install.
*
* This feature uses PNPM to peform the entire monorepo install. When using workspaces, Rush will
* generate a "pnpm-workspace.yaml" file referencing all local projects to install. Rush will
* also generate a "pnpmfile.js" which is used to provide preferred versions support. When install
* is run, this pnpmfile will be used to replace dependency version ranges with a smaller subset
* of the original range. If the preferred version is not fully a subset of the original version
* range, it will be left as-is. After this, the pnpmfile.js provided in the repository (if one
* exists) will be called to further modify package dependencies.
*
* The default value is false.
*/
/*[LINE "HYPOTHETICAL"]*/ "useWorkspaces": true
},

/**
Expand Down
4 changes: 2 additions & 2 deletions apps/rush-lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"npm-packlist": "~2.1.2",
"read-package-tree": "~5.1.5",
"resolve": "~1.17.0",
"semver": "~5.3.0",
"semver": "~7.3.0",
"strict-uri-encode": "~2.0.0",
"tar": "~5.0.5",
"true-case-path": "~2.2.1",
Expand All @@ -64,7 +64,7 @@
"@types/npm-packlist": "~1.1.1",
"@types/read-package-tree": "5.1.0",
"@types/resolve": "1.17.1",
"@types/semver": "5.3.33",
"@types/semver": "~7.3.1",
"@types/strict-uri-encode": "2.0.0",
"@types/tar": "4.0.3",
"@types/wordwrap": "1.0.0",
Expand Down
21 changes: 20 additions & 1 deletion apps/rush-lib/src/api/CommonVersionsConfiguration.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import * as crypto from 'crypto';
import * as path from 'path';
import {
JsonFile,
JsonSchema,
MapExtensions,
ProtectableMap,
FileSystem
FileSystem,
Sort
} from '@rushstack/node-core-library';
import { PackageNameParsers } from './PackageNameParsers';
import { JsonSchemaUrls } from '../logic/JsonSchemaUrls';
Expand Down Expand Up @@ -148,6 +150,23 @@ export class CommonVersionsConfiguration {
return this._filePath;
}

/**
* Get a sha1 hash of the preferred versions.
*/
public getPreferredVersionsHash(): string {
// Sort so that the hash is stable
const orderedPreferredVersions: Map<string, string> = new Map<string, string>(
this._preferredVersions.protectedView
);
Sort.sortMapKeys(orderedPreferredVersions);

// JSON.stringify does not support maps, so we need to convert to an object first
const preferredVersionsObj: { [dependency: string]: string } = MapExtensions.toObject(
orderedPreferredVersions
);
return crypto.createHash('sha1').update(JSON.stringify(preferredVersionsObj)).digest('hex');
}

/**
* Writes the "common-versions.json" file to disk, using the filename that was passed to loadFromFile().
*/
Expand Down
8 changes: 7 additions & 1 deletion apps/rush-lib/src/api/PackageJsonEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,13 @@ export class PackageJsonEditor {
this._onChange.bind(this)
);

if (dependencyType === DependencyType.Regular || dependencyType === DependencyType.Optional) {
// Rush collapses everything that isn't a devDependency into the dependencies
// field, so we need to set the value dependening on dependency type
if (
dependencyType === DependencyType.Regular ||
dependencyType === DependencyType.Optional ||
dependencyType === DependencyType.Peer
) {
this._dependencies.set(packageName, dependency);
} else {
this._devDependencies.set(packageName, dependency);
Expand Down
39 changes: 38 additions & 1 deletion apps/rush-lib/src/api/RushConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { YarnPackageManager } from './packageManager/YarnPackageManager';
import { PnpmPackageManager } from './packageManager/PnpmPackageManager';
import { ExperimentsConfiguration } from './ExperimentsConfiguration';
import { PackageNameParsers } from './PackageNameParsers';
import { RepoStateFile } from '../logic/RepoStateFile';

const MINIMUM_SUPPORTED_RUSH_JSON_VERSION: string = '0.0.0';
const DEFAULT_BRANCH: string = 'master';
Expand All @@ -38,6 +39,7 @@ const knownRushConfigFilenames: string[] = [
'.npmrc-publish',
RushConstants.pinnedVersionsFilename,
RushConstants.commonVersionsFilename,
RushConstants.repoStateFilename,
RushConstants.browserApprovedPackagesFilename,
RushConstants.nonbrowserApprovedPackagesFilename,
RushConstants.versionPoliciesFilename,
Expand Down Expand Up @@ -168,6 +170,10 @@ export interface IPnpmOptionsJson extends IPackageManagerOptionsJsonBase {
* {@inheritDoc PnpmOptionsConfiguration.preventManualShrinkwrapChanges}
*/
preventManualShrinkwrapChanges?: boolean;
/**
* {@inheritDoc PnpmOptionsConfiguration.useWorkspaces}
*/
useWorkspaces?: boolean;
}

/**
Expand Down Expand Up @@ -334,7 +340,7 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration
* @remarks
* This feature protects against accidental inconsistencies that may be introduced
* if the PNPM shrinkwrap file (`pnpm-lock.yaml`) is manually edited. When this
* feature is enabled, `rush update` will append a hash to the file as a YAML comment,
* feature is enabled, `rush update` will write a hash of the shrinkwrap contents to repo-state.json,
* and then `rush update` and `rush install` will validate the hash. Note that this does not prohibit
* manual modifications, but merely requires `rush update` be run
* afterwards, ensuring that PNPM can report or repair any potential inconsistencies.
Expand All @@ -346,6 +352,14 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration
*/
public readonly preventManualShrinkwrapChanges: boolean;

/**
* If true, then Rush will use the workspaces feature to install and link packages when invoking PNPM.
*
* @remarks
* The default value is false. (For now.)
*/
public readonly useWorkspaces: boolean;

/** @internal */
public constructor(json: IPnpmOptionsJson, commonTempFolder: string) {
super(json);
Expand All @@ -360,6 +374,7 @@ export class PnpmOptionsConfiguration extends PackageManagerOptionsConfiguration
this.strictPeerDependencies = !!json.strictPeerDependencies;
this.resolutionStrategy = json.resolutionStrategy || 'fewer-dependencies';
this.preventManualShrinkwrapChanges = !!json.preventManualShrinkwrapChanges;
this.useWorkspaces = !!json.useWorkspaces;
}
}

Expand Down Expand Up @@ -1425,6 +1440,28 @@ export class RushConfiguration {
return CommonVersionsConfiguration.loadFromFile(commonVersionsFilename);
}

/**
* Gets the path to the repo-state.json file for a specific variant.
* @param variant - The name of the current variant in use by the active command.
*/
public getRepoStateFilePath(variant?: string | undefined): string {
const repoStateFilename: string = path.join(
this.commonRushConfigFolder,
...(variant ? [RushConstants.rushVariantsFolderName, variant] : []),
RushConstants.repoStateFilename
);
return repoStateFilename;
}

/**
* Gets the contents from the repo-state.json file for a specific variant.
* @param variant - The name of the current variant in use by the active command.
*/
public getRepoState(variant?: string | undefined): RepoStateFile {
const repoStateFilename: string = this.getRepoStateFilePath(variant);
return RepoStateFile.loadFromFile(repoStateFilename, variant);
}

/**
* Gets the committed shrinkwrap file name for a specific variant.
* @param variant - The name of the current variant in use by the active command.
Expand Down
14 changes: 9 additions & 5 deletions apps/rush-lib/src/cli/actions/AddAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { RushConfigurationProject } from '../../api/RushConfigurationProject';
import { BaseRushAction } from './BaseRushAction';
import { RushCommandLineParser } from '../RushCommandLineParser';
import { PackageJsonUpdater, SemVerStyle } from '../../logic/PackageJsonUpdater';
import { DependencySpecifier } from '../../logic/DependencySpecifier';

export class AddAction extends BaseRushAction {
private _allFlag: CommandLineFlagParameter;
Expand All @@ -25,9 +26,9 @@ export class AddAction extends BaseRushAction {
'Adds a specified package as a dependency of the current project (as determined by the current working directory)' +
' and then runs "rush update". If no version is specified, a version will be automatically detected (typically' +
' either the latest version or a version that won\'t break the "ensureConsistentVersions" policy). If a version' +
' range is specified, the latest version in the range will be used. The version will be automatically prepended' +
' with a tilde, unless the "--exact" or "--caret" flags are used. The "--make-consistent" flag can be used to' +
' update all packages with the dependency.'
' range (or a workspace range) is specified, the latest version in the range will be used. The version will be' +
' automatically prepended with a tilde, unless the "--exact" or "--caret" flags are used. The "--make-consistent"' +
' flag can be used to update all packages with the dependency.'
];
super({
actionName: 'add',
Expand Down Expand Up @@ -128,8 +129,11 @@ export class AddAction extends BaseRushAction {
throw new Error(`The package name "${packageName}" is not valid.`);
}

if (version && version !== 'latest' && !semver.validRange(version) && !semver.valid(version)) {
throw new Error(`The SemVer specifier "${version}" is not valid.`);
if (version && version !== 'latest') {
const specifier: DependencySpecifier = new DependencySpecifier(packageName, version);
if (!semver.validRange(specifier.versionSpecifier) && !semver.valid(specifier.versionSpecifier)) {
throw new Error(`The SemVer specifier "${version}" is not valid.`);
}
}

const updater: PackageJsonUpdater = new PackageJsonUpdater(this.rushConfiguration, this.rushGlobalFolder);
Expand Down
5 changes: 3 additions & 2 deletions apps/rush-lib/src/cli/actions/BaseInstallAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {

import { BaseRushAction } from './BaseRushAction';
import { Event } from '../../api/EventHooks';
import { InstallManager, IInstallManagerOptions } from '../../logic/InstallManager';
import { BaseInstallManager, IInstallManagerOptions } from '../../logic/base/BaseInstallManager';
import { InstallManagerFactory } from '../../logic/InstallManagerFactory';
import { PurgeManager } from '../../logic/PurgeManager';
import { SetupChecks } from '../../logic/SetupChecks';
import { StandardScriptUpdater } from '../../logic/StandardScriptUpdater';
Expand Down Expand Up @@ -117,7 +118,7 @@ export abstract class BaseInstallAction extends BaseRushAction {

const installManagerOptions: IInstallManagerOptions = this.buildInstallOptions();

const installManager: InstallManager = new InstallManager(
const installManager: BaseInstallManager = InstallManagerFactory.getInstallManager(
this.rushConfiguration,
this.rushGlobalFolder,
purgeManager,
Expand Down
2 changes: 1 addition & 1 deletion apps/rush-lib/src/cli/actions/InstallAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// See LICENSE in the project root for license information.

import { BaseInstallAction } from './BaseInstallAction';
import { IInstallManagerOptions } from '../../logic/InstallManager';
import { IInstallManagerOptions } from '../../logic/base/BaseInstallManager';
import { RushCommandLineParser } from '../RushCommandLineParser';

export class InstallAction extends BaseInstallAction {
Expand Down
2 changes: 1 addition & 1 deletion apps/rush-lib/src/cli/actions/UpdateAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { CommandLineFlagParameter } from '@rushstack/ts-command-line';

import { BaseInstallAction } from './BaseInstallAction';
import { IInstallManagerOptions } from '../../logic/InstallManager';
import { IInstallManagerOptions } from '../../logic/base/BaseInstallManager';
import { RushCommandLineParser } from '../RushCommandLineParser';

export class UpdateAction extends BaseInstallAction {
Expand Down
10 changes: 8 additions & 2 deletions apps/rush-lib/src/cli/actions/VersionAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,16 @@ export class VersionAction extends BaseRushAction {
const newPolicyVersion: semver.SemVer = new semver.SemVer(policy.version);
if (newPolicyVersion.prerelease.length) {
// Update 1.5.0-alpha.10 to 1.5.0-beta.10
newPolicyVersion.prerelease[0] = this._prereleaseIdentifier.value;
// For example, if we are parsing "1.5.0-alpha.10" then the newPolicyVersion.prerelease array
// would contain [ "alpha", 10 ], so we would replace "alpha" with "beta"
newPolicyVersion.prerelease = [
this._prereleaseIdentifier.value,
...newPolicyVersion.prerelease.slice(1)
];
} else {
// Update 1.5.0 to 1.5.0-beta
newPolicyVersion.prerelease.push(this._prereleaseIdentifier.value);
// Since there is no length, we can just set to a new array
newPolicyVersion.prerelease = [this._prereleaseIdentifier.value];
}
newVersion = newPolicyVersion.format();
}
Expand Down
16 changes: 7 additions & 9 deletions apps/rush-lib/src/cli/scriptActions/GlobalScriptAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { BaseScriptAction, IBaseScriptActionOptions } from './BaseScriptAction';
import { Utilities } from '../../utilities/Utilities';
import { AlreadyReportedError } from '../../utilities/AlreadyReportedError';
import { FileSystem, LockFile, IPackageJson, JsonFile, PackageName } from '@rushstack/node-core-library';
import { InstallHelpers } from '../../logic/InstallHelpers';
import { InstallHelpers } from '../../logic/installManager/InstallHelpers';
import { RushConstants } from '../../logic/RushConstants';
import { LastInstallFlag } from '../../api/LastInstallFlag';

Expand Down Expand Up @@ -138,14 +138,12 @@ export class GlobalScriptAction extends BaseScriptAction {

console.log(`Installing dependencies under ${this._autoinstallerNameFullPath}...\n`);

Utilities.executeCommand(
this.rushConfiguration.packageManagerToolFilename,
['install', '--frozen-lockfile'],
this._autoinstallerNameFullPath,
undefined,
/* suppressOutput */ false,
/* keepEnvironment */ true
);
Utilities.executeCommand({
command: this.rushConfiguration.packageManagerToolFilename,
args: ['install', '--frozen-lockfile'],
workingDirectory: this._autoinstallerNameFullPath,
keepEnvironment: true
});

// Create file: ../common/autoinstallers/my-task/.rush/temp/last-install.flag
lastInstallFlag.create();
Expand Down
7 changes: 6 additions & 1 deletion apps/rush-lib/src/cli/test/Cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ describe('CLI', () => {
const startPath: string = path.resolve(path.join(__dirname, '../../start.js'));

expect(() => {
Utilities.executeCommand('node', [startPath], workingDir, undefined, true);
Utilities.executeCommand({
command: 'node',
args: [startPath],
workingDirectory: workingDir,
suppressOutput: true
});
}).not.toThrow();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ Adds a specified package as a dependency of the current project (as
determined by the current working directory) and then runs \\"rush update\\". If
no version is specified, a version will be automatically detected (typically
either the latest version or a version that won't break the
\\"ensureConsistentVersions\\" policy). If a version range is specified, the
latest version in the range will be used. The version will be automatically
prepended with a tilde, unless the \\"--exact\\" or \\"--caret\\" flags are used. The
\\"--make-consistent\\" flag can be used to update all packages with the
dependency.
\\"ensureConsistentVersions\\" policy). If a version range (or a workspace range)
is specified, the latest version in the range will be used. The version will
be automatically prepended with a tilde, unless the \\"--exact\\" or \\"--caret\\"
flags are used. The \\"--make-consistent\\" flag can be used to update all
packages with the dependency.
Optional arguments:
-h, --help Show this help message and exit.
Expand Down
2 changes: 2 additions & 0 deletions apps/rush-lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export { CommonVersionsConfiguration } from './api/CommonVersionsConfiguration';

export { PackageJsonEditor, PackageJsonDependency, DependencyType } from './api/PackageJsonEditor';

export { RepoStateFile } from './logic/RepoStateFile';

export { EventHooks, Event } from './api/EventHooks';

export { ChangeManager } from './api/ChangeManager';
Expand Down
Loading

0 comments on commit 2b06f26

Please sign in to comment.