diff --git a/common/changes/@microsoft/rush/copilot-fix-resolutions-dependency-hash_2025-11-25-21-27.json b/common/changes/@microsoft/rush/copilot-fix-resolutions-dependency-hash_2025-11-25-21-27.json new file mode 100644 index 00000000000..43883d32b8d --- /dev/null +++ b/common/changes/@microsoft/rush/copilot-fix-resolutions-dependency-hash_2025-11-25-21-27.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "Hash full shrinkwrap entry to detect sub-dependency resolution changes", + "type": "minor", + "packageName": "@microsoft/rush" + } + ], + "packageName": "@microsoft/rush", + "email": "198982749+Copilot@users.noreply.github.com" +} diff --git a/libraries/rush-lib/src/logic/pnpm/PnpmProjectShrinkwrapFile.ts b/libraries/rush-lib/src/logic/pnpm/PnpmProjectShrinkwrapFile.ts index 64308b19fa8..19e3899804c 100644 --- a/libraries/rush-lib/src/logic/pnpm/PnpmProjectShrinkwrapFile.ts +++ b/libraries/rush-lib/src/logic/pnpm/PnpmProjectShrinkwrapFile.ts @@ -143,21 +143,16 @@ export class PnpmProjectShrinkwrapFile extends BaseProjectShrinkwrapFile { }); }); + describe('getIntegrityForImporter', () => { + it('produces different hashes when sub-dependency resolutions change', () => { + // This test verifies that changes to sub-dependency resolutions are detected. + // The issue is that if package A depends on B, and B's resolution of C changes + // (e.g., from C@1.3 to C@1.2), the integrity hash for A should change. + // This is important for build orchestrators that rely on shrinkwrap-deps.json + // to detect changes to resolution and invalidate caches appropriately. + + // Two shrinkwrap files with the same package but different sub-dependency resolutions + const shrinkwrapContent1: string = ` +lockfileVersion: '9.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false +importers: + .: + dependencies: + foo: + specifier: ~1.0.0 + version: 1.0.0 +packages: + foo@1.0.0: + resolution: + integrity: sha512-abc123== + dependencies: + bar: 1.3.0 + bar@1.3.0: + resolution: + integrity: sha512-bar130== +snapshots: + foo@1.0.0: + dependencies: + bar: 1.3.0 + bar@1.3.0: {} +`; + + const shrinkwrapContent2: string = ` +lockfileVersion: '9.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false +importers: + .: + dependencies: + foo: + specifier: ~1.0.0 + version: 1.0.0 +packages: + foo@1.0.0: + resolution: + integrity: sha512-abc123== + dependencies: + bar: 1.2.0 + bar@1.2.0: + resolution: + integrity: sha512-bar120== +snapshots: + foo@1.0.0: + dependencies: + bar: 1.2.0 + bar@1.2.0: {} +`; + + const shrinkwrapFile1 = PnpmShrinkwrapFile.loadFromString(shrinkwrapContent1); + const shrinkwrapFile2 = PnpmShrinkwrapFile.loadFromString(shrinkwrapContent2); + + // Clear cache to ensure fresh computation + PnpmShrinkwrapFile.clearCache(); + + const integrityMap1 = shrinkwrapFile1.getIntegrityForImporter('.'); + const integrityMap2 = shrinkwrapFile2.getIntegrityForImporter('.'); + + // Both should have integrity maps + expect(integrityMap1).toBeDefined(); + expect(integrityMap2).toBeDefined(); + + // The integrity for 'foo@1.0.0' should be different because bar's resolution changed + const fooIntegrity1 = integrityMap1!.get('foo@1.0.0'); + const fooIntegrity2 = integrityMap2!.get('foo@1.0.0'); + + expect(fooIntegrity1).toBeDefined(); + expect(fooIntegrity2).toBeDefined(); + + // This is the key assertion: the integrity hashes should be different + // because the sub-dependency (bar) resolved to different versions + expect(fooIntegrity1).not.toEqual(fooIntegrity2); + }); + }); + describe('Check is workspace project modified', () => { describe('pnpm lockfile major version 5', () => { it('can detect not modified', async () => { diff --git a/libraries/rush-lib/src/logic/test/__snapshots__/ShrinkwrapFile.test.ts.snap b/libraries/rush-lib/src/logic/test/__snapshots__/ShrinkwrapFile.test.ts.snap index c4f9d4453dd..19e3d2ae2fd 100644 --- a/libraries/rush-lib/src/logic/test/__snapshots__/ShrinkwrapFile.test.ts.snap +++ b/libraries/rush-lib/src/logic/test/__snapshots__/ShrinkwrapFile.test.ts.snap @@ -5,9 +5,9 @@ Array [ Array [ Object { "../../project1": "../../project1:D5ar2j+w6/zH15/eOoF37Nkdbamt2tX47iijyj7LVXk=:", - "/jquery/1.12.3": "sha512-FzM42/Ew+Hb8ha2OlhHRBLgWIZS32gZ0+NvWTf+ZvVvGaIlJkOiXQyb7VBjv4L6fJfmTrRf3EsAmbfsHDhfemw==", - "/pad-left/1.0.2": "sha512-saxSV1EYAytuZDtQYEwi0DPzooG6aN18xyHrnJtzwjVwmMauzkEecd7hynVJGolNGk1Pl9tltmZqfze4TZTCxg==", - "/repeat-string/1.6.1": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "/jquery/1.12.3": "/jquery/1.12.3:Y74h7210GWDRLFidqe7W0rGD9dEVjPuIpExxLG3ql7U=:", + "/pad-left/1.0.2": "/pad-left/1.0.2:fNuxq+VtdNt2R9HJ6ip7x62AjQvQK7tiTHVLF6JGjpE=:", + "/repeat-string/1.6.1": "/repeat-string/1.6.1:FSrgyzed38htiD39oWRz9lAr9wD9AxuRmBW5tC28Jvc=:", }, "project1/.rush/temp/shrinkwrap-deps.json", Object { @@ -22,8 +22,8 @@ Array [ Array [ Object { "../../project2": "../../project2:PQ2FvyHHwmt/FIUaiJBVAfpHv6hj9EGJrAw69+0IY50=:", - "/jquery/2.2.4": "sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==", - "/q/1.5.1": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "/jquery/2.2.4": "/jquery/2.2.4:PIfhtCRWsOQOCNHXI0s+2Ssbxc/U0IZ1ZXD+YcrHwi4=:", + "/q/1.5.1": "/q/1.5.1:A6WReS0f6nc67cF0NQHN3YGoUP6sLNfcWK/7orfz1J4=:", }, "project2/.rush/temp/shrinkwrap-deps.json", Object { @@ -38,8 +38,8 @@ Array [ Array [ Object { "../../project3": "../../project3:jomsZKvXG32qqYOfW3HUBGfWWSw6ybFV1WDf9c/kiP4=:", - "/q/1.5.1": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "/repeat-string/1.6.1": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "/q/1.5.1": "/q/1.5.1:A6WReS0f6nc67cF0NQHN3YGoUP6sLNfcWK/7orfz1J4=:", + "/repeat-string/1.6.1": "/repeat-string/1.6.1:FSrgyzed38htiD39oWRz9lAr9wD9AxuRmBW5tC28Jvc=:", "example.pkgs.visualstudio.com/@scope/testDep/2.1.0": "example.pkgs.visualstudio.com/@scope/testDep/2.1.0:i5jbUOp/IxUgiN0dHYsTRzmimOVBwu7f9908rRaC9VY=:", }, "project3/.rush/temp/shrinkwrap-deps.json", @@ -55,9 +55,9 @@ Array [ Array [ Object { "../../project1": "../../project1:D5ar2j+w6/zH15/eOoF37Nkdbamt2tX47iijyj7LVXk=:", - "/jquery/1.12.3": "sha512-FzM42/Ew+Hb8ha2OlhHRBLgWIZS32gZ0+NvWTf+ZvVvGaIlJkOiXQyb7VBjv4L6fJfmTrRf3EsAmbfsHDhfemw==", - "/pad-left/1.0.2": "sha512-saxSV1EYAytuZDtQYEwi0DPzooG6aN18xyHrnJtzwjVwmMauzkEecd7hynVJGolNGk1Pl9tltmZqfze4TZTCxg==", - "/repeat-string/1.6.1": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "/jquery/1.12.3": "/jquery/1.12.3:Y74h7210GWDRLFidqe7W0rGD9dEVjPuIpExxLG3ql7U=:", + "/pad-left/1.0.2": "/pad-left/1.0.2:fNuxq+VtdNt2R9HJ6ip7x62AjQvQK7tiTHVLF6JGjpE=:", + "/repeat-string/1.6.1": "/repeat-string/1.6.1:FSrgyzed38htiD39oWRz9lAr9wD9AxuRmBW5tC28Jvc=:", }, "project1/.rush/temp/shrinkwrap-deps.json", Object { @@ -72,8 +72,8 @@ Array [ Array [ Object { "../../project2": "../../project2:PQ2FvyHHwmt/FIUaiJBVAfpHv6hj9EGJrAw69+0IY50=:", - "/jquery/2.2.4": "sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==", - "/q/1.5.1": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "/jquery/2.2.4": "/jquery/2.2.4:PIfhtCRWsOQOCNHXI0s+2Ssbxc/U0IZ1ZXD+YcrHwi4=:", + "/q/1.5.1": "/q/1.5.1:A6WReS0f6nc67cF0NQHN3YGoUP6sLNfcWK/7orfz1J4=:", }, "project2/.rush/temp/shrinkwrap-deps.json", Object { @@ -88,8 +88,8 @@ Array [ Array [ Object { "../../project3": "../../project3:jomsZKvXG32qqYOfW3HUBGfWWSw6ybFV1WDf9c/kiP4=:", - "/q/1.5.1": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "/repeat-string/1.6.1": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "/q/1.5.1": "/q/1.5.1:A6WReS0f6nc67cF0NQHN3YGoUP6sLNfcWK/7orfz1J4=:", + "/repeat-string/1.6.1": "/repeat-string/1.6.1:FSrgyzed38htiD39oWRz9lAr9wD9AxuRmBW5tC28Jvc=:", "example.pkgs.visualstudio.com/@scope/testDep/2.1.0": "example.pkgs.visualstudio.com/@scope/testDep/2.1.0:i5jbUOp/IxUgiN0dHYsTRzmimOVBwu7f9908rRaC9VY=:", }, "project3/.rush/temp/shrinkwrap-deps.json", @@ -105,9 +105,9 @@ Array [ Array [ Object { "../../project1": "../../project1:6yFTI2g+Ny0Au80xpo6zIY61TCNDUuLUd6EgLlbOBtc=:", - "jquery@1.12.3": "sha512-FzM42/Ew+Hb8ha2OlhHRBLgWIZS32gZ0+NvWTf+ZvVvGaIlJkOiXQyb7VBjv4L6fJfmTrRf3EsAmbfsHDhfemw==", - "pad-left@1.0.2": "sha512-saxSV1EYAytuZDtQYEwi0DPzooG6aN18xyHrnJtzwjVwmMauzkEecd7hynVJGolNGk1Pl9tltmZqfze4TZTCxg==", - "repeat-string@1.6.1": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "jquery@1.12.3": "jquery@1.12.3:nkmD9jsJt8eUeR2cOltYXX5TFnlK30nBsVeOz047iPo=:", + "pad-left@1.0.2": "pad-left@1.0.2:R80aACjIFOqWnDG/5JgPO4SM4jLta89Xjp5el1RQm+g=:", + "repeat-string@1.6.1": "repeat-string@1.6.1:YqQsoCDmP4kj4raEmb5SYE4GsoFGxpBoRbOA/U9rqB4=:", }, "project1/.rush/temp/shrinkwrap-deps.json", Object { @@ -122,8 +122,8 @@ Array [ Array [ Object { "../../project2": "../../project2:l6v/HWUhScMI0m4k6D5qHiCOFj3Z0GoIFJEcp4I63w0=:", - "jquery@2.2.4": "sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q==", - "q@1.5.0": "sha512-VVMcd+HnuWZalHPycK7CsbVJ+sSrrrnCvHcW38YJVK9Tywnb5DUWJjONi81bLUj7aqDjIXnePxBl5t1r/F/ncg==", + "jquery@2.2.4": "jquery@2.2.4:e3VqitHw5v+hfYoCAwnNmSwfWqvOCOLGdwIKR1fzqhM=:", + "q@1.5.0": "q@1.5.0:lSldncUZjX1nP6wk6WAWwPXA6wLli1dIBnuA3SPeIE4=:", }, "project2/.rush/temp/shrinkwrap-deps.json", Object { @@ -139,8 +139,8 @@ Array [ Object { "../../project3": "../../project3:vMoje8cXfsHYOc6EXbxEw/qyBGXGUL1RApmNfwl7oA8=:", "pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0": "pad-left@https://github.com/jonschlinkert/pad-left/tarball/2.1.0:bKrL+SvVYubL0HwTq/GOOXq1d05LTQ+HGqlXabzGEAU=:", - "q@1.5.0": "sha512-VVMcd+HnuWZalHPycK7CsbVJ+sSrrrnCvHcW38YJVK9Tywnb5DUWJjONi81bLUj7aqDjIXnePxBl5t1r/F/ncg==", - "repeat-string@1.6.1": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "q@1.5.0": "q@1.5.0:lSldncUZjX1nP6wk6WAWwPXA6wLli1dIBnuA3SPeIE4=:", + "repeat-string@1.6.1": "repeat-string@1.6.1:YqQsoCDmP4kj4raEmb5SYE4GsoFGxpBoRbOA/U9rqB4=:", }, "project3/.rush/temp/shrinkwrap-deps.json", Object {