Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@microsoft/rush",
"comment": "Fix an issue with validation of the `pnpm-lock.yaml` `packageExtensionsChecksum` field in pnpm v10.",
"type": "none"
}
],
"packageName": "@microsoft/rush"
}
4 changes: 4 additions & 0 deletions common/config/rush/nonbrowser-approved-packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,10 @@
"name": "npm-packlist",
"allowedCategories": [ "libraries" ]
},
{
"name": "object-hash",
"allowedCategories": [ "libraries" ]
},
{
"name": "open",
"allowedCategories": [ "libraries" ]
Expand Down
5 changes: 5 additions & 0 deletions common/config/subspaces/build-tests-subspace/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush.
{
"pnpmShrinkwrapHash": "9eaf55ba29de8d94e17af33463f3b6ce0480400b",
"pnpmShrinkwrapHash": "f2c06df75a96061e31624e1a556e34660a569623",
"preferredVersionsHash": "550b4cee0bef4e97db6c6aad726df5149d20e7d9",
"packageJsonInjectedDependenciesHash": "5f2a311100f4ad0bc6735377670f5d7cfd664127"
"packageJsonInjectedDependenciesHash": "14f4881943e5d03a361f079944eb76e1501b3e18"
}
15 changes: 15 additions & 0 deletions common/config/subspaces/default/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion common/config/subspaces/default/repo-state.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush.
{
"pnpmShrinkwrapHash": "b14e05fb6e67142fd6a574fe462bee93cb828511",
"pnpmShrinkwrapHash": "c643d2cc7e2c60ef034a6557fc89760140d42f66",
"preferredVersionsHash": "61cd419c533464b580f653eb5f5a7e27fe7055ca"
}
6 changes: 4 additions & 2 deletions libraries/rush-lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"js-yaml": "~4.1.0",
"npm-check": "~6.0.1",
"npm-package-arg": "~6.1.0",
"object-hash": "3.0.0",
"pnpm-sync-lib": "0.3.2",
"read-package-tree": "~5.1.5",
"rxjs": "~6.6.7",
Expand All @@ -70,8 +71,6 @@
"devDependencies": {
"@pnpm/lockfile.types": "~1.0.3",
"@pnpm/logger": "4.0.0",
"eslint": "~9.25.1",
"local-node-rig": "workspace:*",
"@rushstack/heft-webpack5-plugin": "workspace:*",
"@rushstack/heft": "workspace:*",
"@rushstack/operation-graph": "workspace:*",
Expand All @@ -81,12 +80,15 @@
"@types/inquirer": "7.3.1",
"@types/js-yaml": "4.0.9",
"@types/npm-package-arg": "6.1.0",
"@types/object-hash": "~3.0.6",
"@types/read-package-tree": "5.1.0",
"@types/semver": "7.5.0",
"@types/ssri": "~7.1.0",
"@types/strict-uri-encode": "2.0.0",
"@types/tar": "6.1.6",
"@types/webpack-env": "1.18.8",
"eslint": "~9.25.1",
"local-node-rig": "workspace:*",
"webpack": "~5.98.0"
},
"publishOnlyDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,11 +399,41 @@ export class WorkspaceInstallManager extends BaseInstallManager {
}

// Check if packageExtensionsChecksum matches globalPackageExtension's hash
const packageExtensionsChecksum: string | undefined = this._getPackageExtensionChecksum(
pnpmOptions.globalPackageExtensions
);
let packageExtensionsChecksum: string | undefined;
let existingPackageExtensionsChecksum: string | undefined;
if (shrinkwrapFile) {
existingPackageExtensionsChecksum = shrinkwrapFile.packageExtensionsChecksum;
let packageExtensionsChecksumAlgorithm: string | undefined;
if (existingPackageExtensionsChecksum) {
const dashIndex: number = existingPackageExtensionsChecksum.indexOf('-');
if (dashIndex === -1) {
packageExtensionsChecksumAlgorithm = existingPackageExtensionsChecksum.substring(0, dashIndex);
}

if (packageExtensionsChecksumAlgorithm && packageExtensionsChecksumAlgorithm !== 'sha256') {
this._terminal.writeErrorLine(
`The existing packageExtensionsChecksum algorithm "${packageExtensionsChecksumAlgorithm}" is not supported. ` +
`This may indicate that the shrinkwrap was created with a newer version of PNPM than Rush supports.`
);
throw new AlreadyReportedError();
}
}

const globalPackageExtensions: Record<string, unknown> | undefined =
pnpmOptions.globalPackageExtensions;
// https://github.com/pnpm/pnpm/blob/ba9409ffcef0c36dc1b167d770a023c87444822d/pkg-manager/core/src/install/index.ts#L331
if (globalPackageExtensions && Object.keys(globalPackageExtensions).length !== 0) {
if (packageExtensionsChecksumAlgorithm) {
// In PNPM v10, the algorithm changed to SHA256 and the digest changed from hex to base64
packageExtensionsChecksum = await createObjectChecksumAsync(globalPackageExtensions);
} else {
packageExtensionsChecksum = createObjectChecksumLegacy(globalPackageExtensions);
}
}
}

const packageExtensionsChecksumAreEqual: boolean =
packageExtensionsChecksum === shrinkwrapFile?.packageExtensionsChecksum;
packageExtensionsChecksum === existingPackageExtensionsChecksum;

if (!packageExtensionsChecksumAreEqual) {
shrinkwrapWarnings.push("The package extension hash doesn't match the current shrinkwrap.");
Expand All @@ -420,18 +450,6 @@ export class WorkspaceInstallManager extends BaseInstallManager {
return { shrinkwrapIsUpToDate, shrinkwrapWarnings };
}

private _getPackageExtensionChecksum(
packageExtensions: Record<string, unknown> | undefined
): string | undefined {
// https://github.com/pnpm/pnpm/blob/ba9409ffcef0c36dc1b167d770a023c87444822d/pkg-manager/core/src/install/index.ts#L331
const packageExtensionsChecksum: string | undefined =
Object.keys(packageExtensions ?? {}).length === 0
? undefined
: createObjectChecksum(packageExtensions!);

return packageExtensionsChecksum;
}

protected async canSkipInstallAsync(
lastModifiedDate: Date,
subspace: Subspace,
Expand Down Expand Up @@ -793,10 +811,36 @@ export class WorkspaceInstallManager extends BaseInstallManager {

/**
* Source: https://github.com/pnpm/pnpm/blob/ba9409ffcef0c36dc1b167d770a023c87444822d/pkg-manager/core/src/install/index.ts#L821-L824
* @param obj
* @returns
*/
function createObjectChecksum(obj: Record<string, unknown>): string {
function createObjectChecksumLegacy(obj: Record<string, unknown>): string {
const s: string = JSON.stringify(Sort.sortKeys(obj));
return createHash('md5').update(s).digest('hex');
}

/**
* Source: https://github.com/pnpm/pnpm/blob/bdbd31aa4fa6546d65b6eee50a79b51879340d40/crypto/object-hasher/src/index.ts#L8-L12
*/
const defaultOptions: import('object-hash').NormalOption = {
respectType: false,
algorithm: 'sha256',
encoding: 'base64'
};

/**
* https://github.com/pnpm/pnpm/blob/bdbd31aa4fa6546d65b6eee50a79b51879340d40/crypto/object-hasher/src/index.ts#L21-L26
*/
const withSortingOptions: import('object-hash').NormalOption = {
...defaultOptions,
unorderedArrays: true,
unorderedObjects: true,
unorderedSets: true
};

/**
* Source: https://github.com/pnpm/pnpm/blob/bdbd31aa4fa6546d65b6eee50a79b51879340d40/crypto/object-hasher/src/index.ts#L45-L49
*/
async function createObjectChecksumAsync(obj: Record<string, unknown>): Promise<string> {
const { default: hash } = await import('object-hash');
const packageExtensionsChecksum: string = hash(obj, withSortingOptions);
return `${defaultOptions.algorithm}-${packageExtensionsChecksum}`;
}
4 changes: 2 additions & 2 deletions libraries/rush-lib/src/logic/pnpm/PnpmShrinkwrapFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {
*/
private _parseDependencyPath(packagePath: string): string {
let depPath: string = packagePath;
if (this.shrinkwrapFileMajorVersion >= 6) {
if (this.shrinkwrapFileMajorVersion >= ShrinkwrapFileMajorVersion.V6) {
depPath = this._convertLockfileV6DepPathToV5DepPath(packagePath);
}
const pkgInfo: ReturnType<typeof dependencyPathLockfilePreV9.parse> =
Expand Down Expand Up @@ -998,7 +998,7 @@ export class PnpmShrinkwrapFile extends BaseShrinkwrapFile {

const allDependencies: PackageJsonDependency[] = [...dependencyList, ...devDependencyList];

if (this.shrinkwrapFileMajorVersion < 6) {
if (this.shrinkwrapFileMajorVersion < ShrinkwrapFileMajorVersion.V6) {
// PNPM <= v7

// Then get the unique package names and map them to package versions.
Expand Down