From 82fc9903cc344820c08c33034c138328b11bc48a Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 12 Mar 2025 15:05:08 +0000 Subject: [PATCH] fix(ng-dev): check pnpm lock for latest `ng-dev` version when running release tool Currently we only check `yarn.lock`, but in e.g. CLI we already removed the yarn lock file. --- ng-dev/utils/BUILD.bazel | 1 + ng-dev/utils/constants.ts | 3 -- ng-dev/utils/version-check.ts | 67 ++++++++++++++++++++++++++--------- package.json | 1 + tools/local-dev.sh | 2 +- yarn.lock | 59 ++++++++++++++++++++++++++++-- 6 files changed, 110 insertions(+), 23 deletions(-) diff --git a/ng-dev/utils/BUILD.bazel b/ng-dev/utils/BUILD.bazel index 6c98e5e7e..9c1993db8 100644 --- a/ng-dev/utils/BUILD.bazel +++ b/ng-dev/utils/BUILD.bazel @@ -53,6 +53,7 @@ ts_library( "@npm//@octokit/request-error", "@npm//@octokit/rest", "@npm//@octokit/types", + "@npm//@pnpm/dependency-path", "@npm//@types/node", "@npm//@types/semver", "@npm//@types/supports-color", diff --git a/ng-dev/utils/constants.ts b/ng-dev/utils/constants.ts index 7a20a024c..9ddac8b9e 100644 --- a/ng-dev/utils/constants.ts +++ b/ng-dev/utils/constants.ts @@ -11,6 +11,3 @@ export const ngDevNpmPackageName = '@angular/ng-dev'; /** Workspace-relative path for the "package.json" file. */ export const workspaceRelativePackageJsonPath = 'package.json'; - -/** Workspace-relative path for the "yarn.lock" file. */ -export const workspaceRelativeYarnLockFilePath = 'yarn.lock'; diff --git a/ng-dev/utils/version-check.ts b/ng-dev/utils/version-check.ts index c2c1f0afc..91e83aebd 100644 --- a/ng-dev/utils/version-check.ts +++ b/ng-dev/utils/version-check.ts @@ -10,19 +10,16 @@ import * as path from 'path'; import * as fs from 'fs'; import lockfile from '@yarnpkg/lockfile'; import {parse as parseYaml} from 'yaml'; -import { - ngDevNpmPackageName, - workspaceRelativePackageJsonPath, - workspaceRelativeYarnLockFilePath, -} from './constants.js'; +import {ngDevNpmPackageName, workspaceRelativePackageJsonPath} from './constants.js'; import {Log} from './logging.js'; +import {tryGetPackageId} from '@pnpm/dependency-path'; /** * Verifies that the `ng-dev` tool is up-to-date in the workspace. The check will compare * the local version of the tool against the requested version in the workspace lock file. * * This check is helpful ensuring that the caretaker does not accidentally run with an older - * local version of `ng-dev` due to not running `yarn` after checking out new revisions. + * local version of `ng-dev` due to not running `yarn`/`pnpm` after checking out new revisions. * * @returns a boolean indicating success or failure. */ @@ -30,8 +27,28 @@ export async function verifyNgDevToolIsUpToDate(workspacePath: string): Promise< // The placeholder will be replaced by the `pkg_npm` substitutions. const localVersion = `0.0.0-{SCM_HEAD_SHA}`; const workspacePackageJsonFile = path.join(workspacePath, workspaceRelativePackageJsonPath); - const workspaceDirLockFile = path.join(workspacePath, workspaceRelativeYarnLockFilePath); + const pnpmLockFile = path.join(workspacePath, 'pnpm-lock.yaml'); + const yarnLockFile = path.join(workspacePath, 'yarn.lock'); + // TODO: Clean up this logic when fully dropping Yarn + const isPnpmMigrated = fs.existsSync(pnpmLockFile) && !fs.existsSync(yarnLockFile); + const expectedVersion = isPnpmMigrated + ? getExpectedVersionFromPnpmLock(workspacePackageJsonFile, pnpmLockFile) + : getExpectedVersionFromYarnLock(workspacePackageJsonFile, yarnLockFile); + + Log.debug(`Expecting the following ng-dev version: ${expectedVersion}`); + + if (localVersion !== expectedVersion) { + Log.error(' ✘ Your locally installed version of the `ng-dev` tool is outdated and not'); + Log.error(' matching with the version in the `package.json` file.'); + Log.error(' Re-install the dependencies to ensure you are using the correct version.'); + return false; + } + + return true; +} + +function getExpectedVersionFromYarnLock(workspacePackageJsonFile: string, lockFilePath: string) { try { const packageJson = JSON.parse(fs.readFileSync(workspacePackageJsonFile, 'utf8')) as any; // If we are operating in the actual dev-infra repo, always return `true`. @@ -39,7 +56,7 @@ export async function verifyNgDevToolIsUpToDate(workspacePath: string): Promise< return true; } - const lockFileContent = fs.readFileSync(workspaceDirLockFile, 'utf8'); + const lockFileContent = fs.readFileSync(lockFilePath, 'utf8'); let lockFileObject: Record; try { @@ -57,17 +74,33 @@ export async function verifyNgDevToolIsUpToDate(workspacePath: string): Promise< packageJson?.dependencies?.[ngDevNpmPackageName] ?? packageJson?.devDependencies?.[ngDevNpmPackageName] ?? packageJson?.optionalDependencies?.[ngDevNpmPackageName]; - const expectedVersion = lockFileObject[`${ngDevNpmPackageName}@${devInfraPkgVersion}`].version; + return lockFileObject[`${ngDevNpmPackageName}@${devInfraPkgVersion}`].version; + } catch (e) { + Log.debug('Could not find expected ng-dev version from `yarn.lock` file:', e); + return null; + } +} - if (localVersion !== expectedVersion) { - Log.error(' ✘ Your locally installed version of the `ng-dev` tool is outdated and not'); - Log.error(' matching with the version in the `package.json` file.'); - Log.error(' Re-install the dependencies to ensure you are using the correct version.'); - return false; +function getExpectedVersionFromPnpmLock(workspacePackageJsonFile: string, lockFilePath: string) { + try { + const packageJson = JSON.parse(fs.readFileSync(workspacePackageJsonFile, 'utf8')) as any; + // If we are operating in the actual dev-infra repo, always return `true`. + if (packageJson.name === ngDevNpmPackageName) { + return true; } - return true; + + const lockFileContent = fs.readFileSync(lockFilePath, 'utf8'); + const lockFile = parseYaml(lockFileContent); + const importers = lockFile['importers']['.']; + const depEntry = + importers.dependencies?.['@angular/ng-dev'] ?? + importers.devDependencies?.['@angular/ng-dev'] ?? + importers.optionalDependencies?.['@angular/ng-dev']; + const packageId = tryGetPackageId(depEntry.version); + + return lockFile['packages'][`@angular/ng-dev@${packageId}`].version; } catch (e) { - Log.error(e); - return false; + Log.debug('Could not find expected ng-dev version from `pnpm-lock.yaml` file:', e); + return null; } } diff --git a/package.json b/package.json index ca81dc753..e5aa55702 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "@octokit/webhooks-definitions": "3.67.3", "@octokit/webhooks-types": "7.6.1", "@openid/appauth": "^1.3.1", + "@pnpm/dependency-path": "^1000.0.5", "@rollup/plugin-commonjs": "^28.0.0", "@rollup/plugin-node-resolve": "^16.0.0", "@types/babel__core": "^7.1.19", diff --git a/tools/local-dev.sh b/tools/local-dev.sh index 37deb84fa..e7b279379 100755 --- a/tools/local-dev.sh +++ b/tools/local-dev.sh @@ -22,4 +22,4 @@ export TS_NODE_PROJECT=${PWD}/.ng-dev/tsconfig.json # Execute the built ng-dev command in the current working directory # and pass-through arguments unmodified. -node node_modules/.bin/tsx ${ngDevBinFile} ${@} +node ${devInfraProjectDir}/node_modules/.bin/tsx ${ngDevBinFile} ${@} diff --git a/yarn.lock b/yarn.lock index fcab6e809..960283160 100644 --- a/yarn.lock +++ b/yarn.lock @@ -271,6 +271,7 @@ __metadata: "@octokit/webhooks-definitions": "npm:3.67.3" "@octokit/webhooks-types": "npm:7.6.1" "@openid/appauth": "npm:^1.3.1" + "@pnpm/dependency-path": "npm:^1000.0.5" "@rollup/plugin-commonjs": "npm:^28.0.0" "@rollup/plugin-node-resolve": "npm:^16.0.0" "@types/babel__core": "npm:^7.1.19" @@ -3878,6 +3879,44 @@ __metadata: languageName: node linkType: hard +"@pnpm/crypto.hash@npm:1000.1.1": + version: 1000.1.1 + resolution: "@pnpm/crypto.hash@npm:1000.1.1" + dependencies: + "@pnpm/crypto.polyfill": "npm:1000.1.0" + "@pnpm/graceful-fs": "npm:1000.0.0" + ssri: "npm:10.0.5" + checksum: 10c0/1ff392d1434abf81c9dcefa21d3ef844c1d371cd971b037f2425c67990b71302b390117cb353b4ea9ce2158fd31eb1c78de9b385647e9b795129854ed35da7e1 + languageName: node + linkType: hard + +"@pnpm/crypto.polyfill@npm:1000.1.0": + version: 1000.1.0 + resolution: "@pnpm/crypto.polyfill@npm:1000.1.0" + checksum: 10c0/894d7e7579c0c26d3e55ff4d52db9a65c01742728c8e341f04d676803d44e37c22b8d075517100268e513fc44d97d5250d4c5b9fdf57df96c046710fd4cd25ad + languageName: node + linkType: hard + +"@pnpm/dependency-path@npm:^1000.0.5": + version: 1000.0.5 + resolution: "@pnpm/dependency-path@npm:1000.0.5" + dependencies: + "@pnpm/crypto.hash": "npm:1000.1.1" + "@pnpm/types": "npm:1000.2.1" + semver: "npm:^7.7.1" + checksum: 10c0/a2605fd321284e5f81b355843fe6eb48983cab3d94364a3f2547b0cb6f6ef19cb7efba6df0851420fbb78242cf42c1b8117ec5a527778f98da76cde65d27d0d2 + languageName: node + linkType: hard + +"@pnpm/graceful-fs@npm:1000.0.0": + version: 1000.0.0 + resolution: "@pnpm/graceful-fs@npm:1000.0.0" + dependencies: + graceful-fs: "npm:^4.2.11" + checksum: 10c0/544e00660c901260417b0b5086c5589491b8c33fc79bb3206173e8e4572c9e800dc80153e07f7f22ee28fda267b112d97e6e390edaa22428bbde275568438698 + languageName: node + linkType: hard + "@pnpm/network.ca-file@npm:^1.0.1": version: 1.0.2 resolution: "@pnpm/network.ca-file@npm:1.0.2" @@ -3898,6 +3937,13 @@ __metadata: languageName: node linkType: hard +"@pnpm/types@npm:1000.2.1": + version: 1000.2.1 + resolution: "@pnpm/types@npm:1000.2.1" + checksum: 10c0/e5df4dd9c63f242b478e26957d4603d6c0aaa460441afc7461d4ca4aa3ae05bdb572f65873dbe24e1f8257776d413be88cea63f3bf1377aa600f181231a2ee01 + languageName: node + linkType: hard + "@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": version: 1.1.2 resolution: "@protobufjs/aspromise@npm:1.1.2" @@ -8691,7 +8737,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.10, graceful-fs@npm:^4.2.6": +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.10, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 @@ -13218,7 +13264,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.7.1, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4": +"semver@npm:7.7.1, semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.7.1": version: 7.7.1 resolution: "semver@npm:7.7.1" bin: @@ -13790,6 +13836,15 @@ __metadata: languageName: node linkType: hard +"ssri@npm:10.0.5": + version: 10.0.5 + resolution: "ssri@npm:10.0.5" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/b091f2ae92474183c7ac5ed3f9811457e1df23df7a7e70c9476eaa9a0c4a0c8fc190fb45acefbf023ca9ee864dd6754237a697dc52a0fb182afe65d8e77443d8 + languageName: node + linkType: hard + "ssri@npm:^10.0.0": version: 10.0.6 resolution: "ssri@npm:10.0.6"