Skip to content

Commit

Permalink
Merge pull request #2610 from D4N14L/user/danade/pnpm6
Browse files Browse the repository at this point in the history
[rush] Support PNPM 6 and Removal of pnpm-lock.yaml Rewriting
  • Loading branch information
D4N14L authored Apr 20, 2021
2 parents 96e4e16 + c8f72c3 commit d4babde
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 211 deletions.
28 changes: 17 additions & 11 deletions apps/rush-lib/src/logic/base/BaseInstallManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ export abstract class BaseInstallManager {

const { shrinkwrapIsUpToDate, variantIsUpToDate } = await this.prepareAsync();

console.log(
os.EOL + colors.bold(`Checking installation in "${this.rushConfiguration.commonTempFolder}"`)
);

// This marker file indicates that the last "rush install" completed successfully.
// Always perform a clean install if filter flags were provided. Additionally, if
// "--purge" was specified, or if the last install was interrupted, then we will
Expand Down Expand Up @@ -220,12 +224,7 @@ export abstract class BaseInstallManager {
// Perform the actual install
await this.installAsync(cleanInstall);

const usePnpmFrozenLockfile: boolean =
this._rushConfiguration.packageManager === 'pnpm' &&
this._rushConfiguration.experimentsConfiguration.configuration.usePnpmFrozenLockfileForRushInstall ===
true;

if (this.options.allowShrinkwrapUpdates && (usePnpmFrozenLockfile || !shrinkwrapIsUpToDate)) {
if (this.options.allowShrinkwrapUpdates && !shrinkwrapIsUpToDate) {
// Copy (or delete) common\temp\pnpm-lock.yaml --> common\config\rush\pnpm-lock.yaml
Utilities.syncFile(
this._rushConfiguration.tempShrinkwrapFilename,
Expand All @@ -250,6 +249,8 @@ export abstract class BaseInstallManager {
if (!isFilteredInstall) {
this._commonTempInstallFlag.create();
}
} else {
console.log('Installation is already up-to-date.');
}

// Perform any post-install work the install manager requires
Expand Down Expand Up @@ -403,6 +404,8 @@ export abstract class BaseInstallManager {
let { shrinkwrapIsUpToDate, shrinkwrapWarnings } = await this.prepareCommonTempAsync(shrinkwrapFile);
shrinkwrapIsUpToDate = shrinkwrapIsUpToDate && !this.options.recheckShrinkwrap;

this._syncTempShrinkwrap(shrinkwrapFile);

// Write out the reported warnings
if (shrinkwrapWarnings.length > 0) {
console.log();
Expand All @@ -420,8 +423,6 @@ export abstract class BaseInstallManager {
console.log();
}

this._syncTempShrinkwrap(shrinkwrapFile);

// Force update if the shrinkwrap is out of date
if (!shrinkwrapIsUpToDate) {
if (!this.options.allowShrinkwrapUpdates) {
Expand Down Expand Up @@ -649,9 +650,14 @@ export abstract class BaseInstallManager {

private _syncTempShrinkwrap(shrinkwrapFile: BaseShrinkwrapFile | undefined): void {
if (shrinkwrapFile) {
// If we have a (possibly incomplete) shrinkwrap file, save it as the temporary file.
shrinkwrapFile.save(this.rushConfiguration.tempShrinkwrapFilename);
shrinkwrapFile.save(this.rushConfiguration.tempShrinkwrapPreinstallFilename);
Utilities.syncFile(
this._rushConfiguration.getCommittedShrinkwrapFilename(this.options.variant),
this.rushConfiguration.tempShrinkwrapFilename
);
Utilities.syncFile(
this._rushConfiguration.getCommittedShrinkwrapFilename(this.options.variant),
this.rushConfiguration.tempShrinkwrapPreinstallFilename
);
} else {
// Otherwise delete the temporary file
FileSystem.deleteFile(this.rushConfiguration.tempShrinkwrapFilename);
Expand Down
2 changes: 1 addition & 1 deletion apps/rush-lib/src/logic/base/BaseLinkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export abstract class BaseLinkManager {
* if true, this option forces the links to be recreated.
*/
public async createSymlinksForProjects(force: boolean): Promise<void> {
console.log('Linking projects together...');
console.log(os.EOL + colors.bold('Linking local projects'));
const stopwatch: Stopwatch = Stopwatch.start();

await this._linkProjects();
Expand Down
17 changes: 3 additions & 14 deletions apps/rush-lib/src/logic/base/BaseShrinkwrapFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import colors from 'colors/safe';
import * as semver from 'semver';
import { FileSystem } from '@rushstack/node-core-library';

import { RushConstants } from '../../logic/RushConstants';
import { DependencySpecifier, DependencySpecifierType } from '../DependencySpecifier';
Expand All @@ -25,13 +24,6 @@ export abstract class BaseShrinkwrapFile {
return undefined;
}

/**
* Serializes and saves the shrinkwrap file to specified location
*/
public save(filePath: string): void {
FileSystem.writeFile(filePath, this.serialize());
}

/**
* Validate the shrinkwrap using the provided policy options.
*
Expand Down Expand Up @@ -81,13 +73,11 @@ export abstract class BaseShrinkwrapFile {
*/
public tryEnsureCompatibleDependency(
dependencySpecifier: DependencySpecifier,
tempProjectName: string,
tryReusingPackageVersionsFromShrinkwrap: boolean = true
tempProjectName: string
): boolean {
const shrinkwrapDependency: DependencySpecifier | undefined = this.tryEnsureDependencyVersion(
dependencySpecifier,
tempProjectName,
tryReusingPackageVersionsFromShrinkwrap
tempProjectName
);
if (!shrinkwrapDependency) {
return false;
Expand All @@ -107,8 +97,7 @@ export abstract class BaseShrinkwrapFile {
/** @virtual */
protected abstract tryEnsureDependencyVersion(
dependencySpecifier: DependencySpecifier,
tempProjectName: string,
tryReusingPackageVersionsFromShrinkwrap: boolean
tempProjectName: string
): DependencySpecifier | undefined;

/** @virtual */
Expand Down
4 changes: 2 additions & 2 deletions apps/rush-lib/src/logic/installManager/InstallHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ export class InstallHelpers {
`${packageManager}-local`
);

console.log(os.EOL + 'Symlinking "' + localPackageManagerToolFolder + '"');
console.log(' --> "' + packageManagerToolFolder + '"');
console.log(os.EOL + `Symlinking "${localPackageManagerToolFolder}"`);
console.log(` --> "${packageManagerToolFolder}"`);

// We cannot use FileSystem.exists() to test the existence of a symlink, because it will
// return false for broken symlinks. There is no way to test without catching an exception.
Expand Down
145 changes: 51 additions & 94 deletions apps/rush-lib/src/logic/installManager/RushInstallManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,28 +69,6 @@ export class RushInstallManager extends BaseInstallManager {
this._tempProjectHelper = new TempProjectHelper(this.rushConfiguration);
}

protected async prepareAsync(): Promise<{ variantIsUpToDate: boolean; shrinkwrapIsUpToDate: boolean }> {
const result: { variantIsUpToDate: boolean; shrinkwrapIsUpToDate: boolean } = await super.prepareAsync();

// We have already done prep work to ensure that the package.json files are "up to date". Some changes
// (such as local package version bumps, or adding a reference to another existing local package) do
// not need a "rush update" to be run, and as such can be changed manually in the temp shrinkwrap. These
// changes will eventually be picked up during a "rush update".
if (
this.rushConfiguration.packageManager === 'pnpm' &&
!this.options.allowShrinkwrapUpdates &&
this.rushConfiguration.experimentsConfiguration.configuration.usePnpmFrozenLockfileForRushInstall
) {
const tempShrinkwrap: PnpmShrinkwrapFile | undefined = PnpmShrinkwrapFile.loadFromFile(
this.rushConfiguration.tempShrinkwrapFilename,
this.rushConfiguration.pnpmOptions
);
await this._updatePnpmShrinkwrapTarballIntegritiesAsync(tempShrinkwrap);
}

return result;
}

/**
* Regenerates the common/package.json and all temp_modules projects.
* If shrinkwrapFile is provided, this function also validates whether it contains
Expand Down Expand Up @@ -271,27 +249,14 @@ export class RushInstallManager extends BaseInstallManager {
// We will NOT locally link this package; add it as a regular dependency.
tempPackageJson.dependencies![packageName] = packageVersion;

let tryReusingPackageVersionsFromShrinkwrap: boolean = true;

if (this.rushConfiguration.packageManager === 'pnpm') {
// Shrinkwrap churn optimization doesn't make sense when --frozen-lockfile is true
tryReusingPackageVersionsFromShrinkwrap = !this.rushConfiguration.experimentsConfiguration
.configuration.usePnpmFrozenLockfileForRushInstall;
}

if (shrinkwrapFile) {
if (
!shrinkwrapFile.tryEnsureCompatibleDependency(
dependencySpecifier,
rushProject.tempProjectName,
tryReusingPackageVersionsFromShrinkwrap
)
) {
shrinkwrapWarnings.push(
`Missing dependency "${packageName}" (${packageVersion}) required by "${rushProject.packageName}"`
);
shrinkwrapIsUpToDate = false;
}
if (
shrinkwrapFile &&
!shrinkwrapFile.tryEnsureCompatibleDependency(dependencySpecifier, rushProject.tempProjectName)
) {
shrinkwrapWarnings.push(
`Missing dependency "${packageName}" (${packageVersion}) required by "${rushProject.packageName}"`
);
shrinkwrapIsUpToDate = false;
}
}

Expand Down Expand Up @@ -344,14 +309,23 @@ export class RushInstallManager extends BaseInstallManager {
}
}

// Remove the workspace file if it exists
if (this.rushConfiguration.packageManager === 'pnpm') {
const workspaceFilePath: string = path.join(
this.rushConfiguration.commonTempFolder,
'pnpm-workspace.yaml'
// When using frozen shrinkwrap, we need to validate that the tarball integrities are up-to-date
// with the shrinkwrap file, since these will cause install to fail.
if (
shrinkwrapFile &&
this.rushConfiguration.packageManager === 'pnpm' &&
this.rushConfiguration.experimentsConfiguration.configuration.usePnpmFrozenLockfileForRushInstall
) {
const pnpmShrinkwrapFile: PnpmShrinkwrapFile = shrinkwrapFile as PnpmShrinkwrapFile;
const tarballIntegrityValid: boolean = await this._validateRushProjectTarballIntegrityAsync(
pnpmShrinkwrapFile,
rushProject
);
if (FileSystem.exists(workspaceFilePath)) {
FileSystem.deleteFile(workspaceFilePath);
if (!tarballIntegrityValid) {
shrinkwrapIsUpToDate = false;
shrinkwrapWarnings.push(
`Invalid or missing tarball integrity hash in shrinkwrap for "${rushProject.packageName}"`
);
}
}

Expand All @@ -366,6 +340,21 @@ export class RushInstallManager extends BaseInstallManager {
}
}

// Remove the workspace file if it exists
if (this.rushConfiguration.packageManager === 'pnpm') {
const workspaceFilePath: string = path.join(
this.rushConfiguration.commonTempFolder,
'pnpm-workspace.yaml'
);
try {
await FileSystem.deleteFileAsync(workspaceFilePath);
} catch (e) {
if (!FileSystem.isNotExistError(e)) {
throw e;
}
}
}

// Write the common package.json
InstallHelpers.generateCommonPackageJson(this.rushConfiguration, commonDependencies);

Expand Down Expand Up @@ -396,54 +385,30 @@ export class RushInstallManager extends BaseInstallManager {
return true;
}

private async _updatePnpmShrinkwrapTarballIntegritiesAsync(
tempShrinkwrapFile: PnpmShrinkwrapFile | undefined
): Promise<void> {
if (!tempShrinkwrapFile) {
return;
}

const tempProjectHelper: TempProjectHelper = new TempProjectHelper(this.rushConfiguration);

console.log(
`Checking shrinkwrap local dependency tarball hashes in ${tempShrinkwrapFile.shrinkwrapFilename}`
);

let shrinkwrapFileUpdated: boolean = false;
for (const rushProject of this.rushConfiguration.projects) {
const tempProjectDependencyKey: string | undefined = tempShrinkwrapFile.getTempProjectDependencyKey(
private async _validateRushProjectTarballIntegrityAsync(
shrinkwrapFile: PnpmShrinkwrapFile | undefined,
rushProject: RushConfigurationProject
): Promise<boolean> {
if (shrinkwrapFile) {
const tempProjectDependencyKey: string | undefined = shrinkwrapFile.getTempProjectDependencyKey(
rushProject.tempProjectName
);

if (!tempProjectDependencyKey) {
throw new Error(`Cannot get dependency key for temp project: ${rushProject.tempProjectName}`);
return false;
}

const parentShrinkwrapEntry:
| IPnpmShrinkwrapDependencyYaml
| undefined = tempShrinkwrapFile.getShrinkwrapEntryFromTempProjectDependencyKey(
const parentShrinkwrapEntry: IPnpmShrinkwrapDependencyYaml = shrinkwrapFile.getShrinkwrapEntryFromTempProjectDependencyKey(
tempProjectDependencyKey
);
if (!parentShrinkwrapEntry) {
throw new InternalError(
`Cannot find shrinkwrap entry using dependency key for temp project: ${rushProject.tempProjectName}`
);
}

)!;
const newIntegrity: string = (
await ssri.fromStream(fs.createReadStream(tempProjectHelper.getTarballFilePath(rushProject)))
await ssri.fromStream(fs.createReadStream(this._tempProjectHelper.getTarballFilePath(rushProject)))
).toString();

if (parentShrinkwrapEntry.resolution.integrity !== newIntegrity) {
shrinkwrapFileUpdated = true;
parentShrinkwrapEntry.resolution.integrity = newIntegrity;
return false;
}
}

tempShrinkwrapFile.save(tempShrinkwrapFile.shrinkwrapFilename);
if (shrinkwrapFileUpdated) {
console.log('Shrinkwrap local dependency tarball hashes were updated.');
}
return true;
}

/**
Expand All @@ -452,14 +417,6 @@ export class RushInstallManager extends BaseInstallManager {
* @override
*/
protected canSkipInstall(lastModifiedDate: Date): boolean {
console.log(
os.EOL +
colors.bold(
`Checking ${RushConstants.nodeModulesFolderName} in ${this.rushConfiguration.commonTempFolder}`
) +
os.EOL
);

// Based on timestamps, can we skip this install entirely?
const potentiallyChangedFiles: string[] = [];

Expand Down
14 changes: 0 additions & 14 deletions apps/rush-lib/src/logic/installManager/WorkspaceInstallManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { PackageJsonEditor, DependencyType, PackageJsonDependency } from '../../
import { PnpmWorkspaceFile } from '../pnpm/PnpmWorkspaceFile';
import { RushConfigurationProject } from '../../api/RushConfigurationProject';
import { RushConstants } from '../../logic/RushConstants';
import { Stopwatch } from '../../utilities/Stopwatch';
import { Utilities } from '../../utilities/Utilities';
import { InstallHelpers } from './InstallHelpers';
import { CommonVersionsConfiguration } from '../../api/CommonVersionsConfiguration';
Expand Down Expand Up @@ -65,8 +64,6 @@ export class WorkspaceInstallManager extends BaseInstallManager {
protected async prepareCommonTempAsync(
shrinkwrapFile: BaseShrinkwrapFile | undefined
): Promise<{ shrinkwrapIsUpToDate: boolean; shrinkwrapWarnings: string[] }> {
const stopwatch: Stopwatch = Stopwatch.start();

// Block use of the RUSH_TEMP_FOLDER environment variable
if (EnvironmentConfiguration.rushTempFolderOverride !== undefined) {
throw new Error(
Expand Down Expand Up @@ -264,21 +261,10 @@ export class WorkspaceInstallManager extends BaseInstallManager {
// since "rush install" will consider this timestamp
workspaceFile.save(workspaceFile.workspaceFilename, { onlyIfChanged: true });

stopwatch.stop();
console.log(`Finished creating workspace (${stopwatch.toString()})`);

return { shrinkwrapIsUpToDate, shrinkwrapWarnings };
}

protected canSkipInstall(lastModifiedDate: Date): boolean {
console.log(
os.EOL +
colors.bold(
`Checking ${RushConstants.nodeModulesFolderName} in ${this.rushConfiguration.commonTempFolder}`
) +
os.EOL
);

// Based on timestamps, can we skip this install entirely?
const potentiallyChangedFiles: string[] = [];

Expand Down
Loading

0 comments on commit d4babde

Please sign in to comment.