From 8737784af31b3514d77c0f7c06c9c99f3ac14ac2 Mon Sep 17 00:00:00 2001 From: Rhys Arkins Date: Sun, 27 Aug 2023 08:45:12 +0200 Subject: [PATCH] feat(lerna)!: remove explicit lerna support (#23907) --- docs/usage/self-hosted-configuration.md | 2 +- lib/config/options/index.ts | 2 +- .../extract/__snapshots__/index.spec.ts.snap | 513 ------------------ lib/modules/manager/npm/extract/index.spec.ts | 122 ----- lib/modules/manager/npm/extract/index.ts | 28 - .../manager/npm/extract/monorepo.spec.ts | 146 +---- lib/modules/manager/npm/extract/monorepo.ts | 32 +- .../__snapshots__/lerna.spec.ts.snap | 260 --------- .../manager/npm/post-update/index.spec.ts | 132 +---- lib/modules/manager/npm/post-update/index.ts | 181 +----- .../manager/npm/post-update/lerna.spec.ts | 244 --------- lib/modules/manager/npm/post-update/lerna.ts | 154 ------ lib/modules/manager/npm/post-update/types.ts | 1 - lib/modules/manager/npm/readme.md | 4 + lib/modules/manager/npm/types.ts | 3 - lib/util/exec/containerbase.ts | 5 - .../update/branch/lock-files/index.spec.ts | 2 - 17 files changed, 18 insertions(+), 1813 deletions(-) delete mode 100644 lib/modules/manager/npm/post-update/__snapshots__/lerna.spec.ts.snap delete mode 100644 lib/modules/manager/npm/post-update/lerna.spec.ts delete mode 100644 lib/modules/manager/npm/post-update/lerna.ts diff --git a/docs/usage/self-hosted-configuration.md b/docs/usage/self-hosted-configuration.md index b1147e32aae310..edf4a96c13abcd 100644 --- a/docs/usage/self-hosted-configuration.md +++ b/docs/usage/self-hosted-configuration.md @@ -830,7 +830,7 @@ Secret names must start with an upper or lower case character and can have only By default, Renovate will use the most efficient approach to updating package files and lock files, which in most cases skips the need to perform a full module install by the bot. If this is set to false, then a full install of modules will be done. -This is currently applicable to `npm` and `lerna`/`npm` only, and only used in cases where bugs in `npm` result in incorrect lock files being updated. +This is currently applicable to `npm` only, and only used in cases where bugs in `npm` result in incorrect lock files being updated. ## token diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts index 1a1cf8ef1e416c..bb1a4cb9331fd9 100644 --- a/lib/config/options/index.ts +++ b/lib/config/options/index.ts @@ -1014,7 +1014,7 @@ const options: RenovateOptions[] = [ { name: 'updateInternalDeps', description: - 'Whether to update internal dep versions in a monorepo. Works on Lerna or Yarn Workspaces.', + 'Whether to update internal dep versions in a monorepo. Works on Yarn Workspaces.', type: 'boolean', default: false, stage: 'package', diff --git a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap index b7232f8442cd34..1b491e3190e7b4 100644 --- a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap +++ b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap @@ -13,9 +13,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() catches invalid "extractedConstraints": {}, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": undefined, "pnpmShrinkwrap": undefined, @@ -135,9 +132,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() extracts engine }, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": undefined, "pnpmShrinkwrap": undefined, @@ -315,9 +309,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() extracts non-np "extractedConstraints": {}, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": undefined, "pnpmShrinkwrap": undefined, @@ -364,9 +355,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() extracts npm pa "extractedConstraints": {}, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": "package-lock.json", "packageJsonName": undefined, "pnpmShrinkwrap": undefined, @@ -398,9 +386,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() extracts packag }, "managerData": { "hasPackageManager": true, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": undefined, "pnpmShrinkwrap": undefined, @@ -473,9 +458,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() extracts volta }, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": undefined, "pnpmShrinkwrap": undefined, @@ -527,9 +509,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() extracts volta }, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": undefined, "pnpmShrinkwrap": undefined, @@ -543,278 +522,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() extracts volta } `; -exports[`modules/manager/npm/extract/index .extractPackageFile() finds "npmClient":"npm" in lerna.json 1`] = ` -{ - "deps": [ - { - "currentValue": "6.5.0", - "datasource": "npm", - "depName": "autoprefixer", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "~1.6.0", - "datasource": "npm", - "depName": "bower", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "13.1.0", - "datasource": "npm", - "depName": "browserify", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "0.9.2", - "datasource": "npm", - "depName": "browserify-css", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "=0.22.0", - "datasource": "npm", - "depName": "cheerio", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "1.21.0", - "datasource": "npm", - "depName": "config", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "depName": "enabled", - "depType": "devDependencies", - "prettyDepType": "devDependency", - "skipReason": "invalid-value", - }, - { - "currentValue": "^1.5.8", - "datasource": "npm", - "depName": "angular", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "1.5.8", - "datasource": "npm", - "depName": "angular-touch", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "1.5.8", - "datasource": "npm", - "depName": "angular-sanitize", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "4.0.0-beta.1", - "datasource": "npm", - "depName": "@angular/core", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "1.21.0", - "datasource": "npm", - "depName": "config", - "depType": "resolutions", - "prettyDepType": "resolutions", - }, - { - "currentValue": "8.0.0", - "datasource": "npm", - "depName": "@angular/cli", - "depType": "resolutions", - "managerData": { - "key": "**/@angular/cli", - }, - "prettyDepType": "resolutions", - }, - { - "currentValue": "1.33.0", - "datasource": "npm", - "depName": "angular", - "depType": "resolutions", - "managerData": { - "key": "**/angular", - }, - "prettyDepType": "resolutions", - }, - { - "currentValue": "1.0.0", - "datasource": "npm", - "depName": "glob", - "depType": "resolutions", - "managerData": { - "key": "config/glob", - }, - "prettyDepType": "resolutions", - }, - ], - "extractedConstraints": {}, - "managerData": { - "hasPackageManager": false, - "lernaClient": "npm", - "lernaJsonFile": "lerna.json", - "lernaPackages": undefined, - "npmLock": undefined, - "packageJsonName": "renovate", - "pnpmShrinkwrap": undefined, - "workspacesPackages": undefined, - "yarnLock": undefined, - "yarnZeroInstall": false, - }, - "npmrc": undefined, - "packageFileVersion": "1.0.0", - "skipInstalls": true, -} -`; - -exports[`modules/manager/npm/extract/index .extractPackageFile() finds "npmClient":"yarn" in lerna.json 1`] = ` -{ - "deps": [ - { - "currentValue": "6.5.0", - "datasource": "npm", - "depName": "autoprefixer", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "~1.6.0", - "datasource": "npm", - "depName": "bower", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "13.1.0", - "datasource": "npm", - "depName": "browserify", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "0.9.2", - "datasource": "npm", - "depName": "browserify-css", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "=0.22.0", - "datasource": "npm", - "depName": "cheerio", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "1.21.0", - "datasource": "npm", - "depName": "config", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "depName": "enabled", - "depType": "devDependencies", - "prettyDepType": "devDependency", - "skipReason": "invalid-value", - }, - { - "currentValue": "^1.5.8", - "datasource": "npm", - "depName": "angular", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "1.5.8", - "datasource": "npm", - "depName": "angular-touch", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "1.5.8", - "datasource": "npm", - "depName": "angular-sanitize", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "4.0.0-beta.1", - "datasource": "npm", - "depName": "@angular/core", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "1.21.0", - "datasource": "npm", - "depName": "config", - "depType": "resolutions", - "prettyDepType": "resolutions", - }, - { - "currentValue": "8.0.0", - "datasource": "npm", - "depName": "@angular/cli", - "depType": "resolutions", - "managerData": { - "key": "**/@angular/cli", - }, - "prettyDepType": "resolutions", - }, - { - "currentValue": "1.33.0", - "datasource": "npm", - "depName": "angular", - "depType": "resolutions", - "managerData": { - "key": "**/angular", - }, - "prettyDepType": "resolutions", - }, - { - "currentValue": "1.0.0", - "datasource": "npm", - "depName": "glob", - "depType": "resolutions", - "managerData": { - "key": "config/glob", - }, - "prettyDepType": "resolutions", - }, - ], - "extractedConstraints": {}, - "managerData": { - "hasPackageManager": false, - "lernaClient": "yarn", - "lernaJsonFile": "lerna.json", - "lernaPackages": undefined, - "npmLock": undefined, - "packageJsonName": "renovate", - "pnpmShrinkwrap": undefined, - "workspacesPackages": undefined, - "yarnLock": undefined, - "yarnZeroInstall": false, - }, - "npmrc": undefined, - "packageFileVersion": "1.0.0", - "skipInstalls": true, -} -`; - exports[`modules/manager/npm/extract/index .extractPackageFile() finds a lock file 1`] = ` { "deps": [ @@ -935,9 +642,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() finds a lock fi "extractedConstraints": {}, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": "renovate", "pnpmShrinkwrap": undefined, @@ -951,214 +655,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() finds a lock fi } `; -exports[`modules/manager/npm/extract/index .extractPackageFile() finds complex yarn workspaces 1`] = ` -{ - "deps": [], - "extractedConstraints": {}, - "managerData": { - "hasPackageManager": false, - "lernaClient": "npm", - "lernaJsonFile": "lerna.json", - "lernaPackages": undefined, - "npmLock": undefined, - "packageJsonName": "@a/b", - "pnpmShrinkwrap": undefined, - "workspacesPackages": [ - "packages/*", - ], - "yarnLock": undefined, - "yarnZeroInstall": false, - }, - "npmrc": undefined, - "packageFileVersion": "0.0.8", - "skipInstalls": true, -} -`; - -exports[`modules/manager/npm/extract/index .extractPackageFile() finds lerna 1`] = ` -{ - "deps": [ - { - "currentValue": "6.5.0", - "datasource": "npm", - "depName": "autoprefixer", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "~1.6.0", - "datasource": "npm", - "depName": "bower", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "13.1.0", - "datasource": "npm", - "depName": "browserify", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "0.9.2", - "datasource": "npm", - "depName": "browserify-css", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "=0.22.0", - "datasource": "npm", - "depName": "cheerio", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "currentValue": "1.21.0", - "datasource": "npm", - "depName": "config", - "depType": "dependencies", - "prettyDepType": "dependency", - }, - { - "depName": "enabled", - "depType": "devDependencies", - "prettyDepType": "devDependency", - "skipReason": "invalid-value", - }, - { - "currentValue": "^1.5.8", - "datasource": "npm", - "depName": "angular", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "1.5.8", - "datasource": "npm", - "depName": "angular-touch", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "1.5.8", - "datasource": "npm", - "depName": "angular-sanitize", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "4.0.0-beta.1", - "datasource": "npm", - "depName": "@angular/core", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - { - "currentValue": "1.21.0", - "datasource": "npm", - "depName": "config", - "depType": "resolutions", - "prettyDepType": "resolutions", - }, - { - "currentValue": "8.0.0", - "datasource": "npm", - "depName": "@angular/cli", - "depType": "resolutions", - "managerData": { - "key": "**/@angular/cli", - }, - "prettyDepType": "resolutions", - }, - { - "currentValue": "1.33.0", - "datasource": "npm", - "depName": "angular", - "depType": "resolutions", - "managerData": { - "key": "**/angular", - }, - "prettyDepType": "resolutions", - }, - { - "currentValue": "1.0.0", - "datasource": "npm", - "depName": "glob", - "depType": "resolutions", - "managerData": { - "key": "config/glob", - }, - "prettyDepType": "resolutions", - }, - ], - "extractedConstraints": {}, - "managerData": { - "hasPackageManager": false, - "lernaClient": "npm", - "lernaJsonFile": "lerna.json", - "lernaPackages": undefined, - "npmLock": undefined, - "packageJsonName": "renovate", - "pnpmShrinkwrap": undefined, - "workspacesPackages": undefined, - "yarnLock": undefined, - "yarnZeroInstall": false, - }, - "npmrc": undefined, - "packageFileVersion": "1.0.0", - "skipInstalls": true, -} -`; - -exports[`modules/manager/npm/extract/index .extractPackageFile() finds simple yarn workspaces 1`] = ` -{ - "deps": [], - "extractedConstraints": {}, - "managerData": { - "hasPackageManager": false, - "lernaClient": "npm", - "lernaJsonFile": "lerna.json", - "lernaPackages": undefined, - "npmLock": undefined, - "packageJsonName": "@a/b", - "pnpmShrinkwrap": undefined, - "workspacesPackages": [ - "packages/*", - ], - "yarnLock": undefined, - "yarnZeroInstall": false, - }, - "npmrc": undefined, - "packageFileVersion": "0.0.8", - "skipInstalls": true, -} -`; - -exports[`modules/manager/npm/extract/index .extractPackageFile() finds simple yarn workspaces with lerna.json and useWorkspaces: true 1`] = ` -{ - "deps": [], - "extractedConstraints": {}, - "managerData": { - "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, - "npmLock": undefined, - "packageJsonName": "@a/b", - "pnpmShrinkwrap": undefined, - "workspacesPackages": [ - "packages/*", - ], - "yarnLock": undefined, - "yarnZeroInstall": false, - }, - "npmrc": undefined, - "packageFileVersion": "0.0.8", - "skipInstalls": true, -} -`; - exports[`modules/manager/npm/extract/index .extractPackageFile() returns an array of dependencies 1`] = ` { "deps": [ @@ -1279,9 +775,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() returns an arra "extractedConstraints": {}, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": "renovate", "pnpmShrinkwrap": undefined, @@ -1397,9 +890,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() returns an arra "extractedConstraints": {}, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": "renovate", "pnpmShrinkwrap": undefined, @@ -1533,9 +1023,6 @@ exports[`modules/manager/npm/extract/index .extractPackageFile() sets skipInstal "extractedConstraints": {}, "managerData": { "hasPackageManager": false, - "lernaClient": undefined, - "lernaJsonFile": undefined, - "lernaPackages": undefined, "npmLock": undefined, "packageJsonName": "renovate", "pnpmShrinkwrap": undefined, diff --git a/lib/modules/manager/npm/extract/index.spec.ts b/lib/modules/manager/npm/extract/index.spec.ts index f9c34b2c4a7a34..cce7662f5a31ab 100644 --- a/lib/modules/manager/npm/extract/index.spec.ts +++ b/lib/modules/manager/npm/extract/index.spec.ts @@ -16,11 +16,6 @@ const defaultExtractConfig = { const input01Content = Fixtures.get('inputs/01.json', '..'); const input02Content = Fixtures.get('inputs/02.json', '..'); const input01GlobContent = Fixtures.get('inputs/01-glob.json', '..'); -const workspacesContent = Fixtures.get('inputs/workspaces.json', '..'); -const workspacesSimpleContent = Fixtures.get( - 'inputs/workspaces-simple.json', - '..' -); const vendorisedContent = Fixtures.get('is-object.json', '..'); const invalidNameContent = Fixtures.get('invalid-name.json', '..'); @@ -299,120 +294,6 @@ describe('modules/manager/npm/extract/index', () => { ).toBeArrayIncludingOnly(['https://registry.example.com']); }); - it('finds lerna', async () => { - fs.readLocalFile.mockImplementation((fileName): Promise => { - if (fileName === 'lerna.json') { - return Promise.resolve('{}'); - } - return Promise.resolve(null); - }); - const res = await npmExtract.extractPackageFile( - input01Content, - 'package.json', - defaultExtractConfig - ); - expect(res).toMatchSnapshot({ - managerData: { - lernaClient: 'npm', - lernaJsonFile: 'lerna.json', - lernaPackages: undefined, - }, - }); - }); - - it('finds "npmClient":"npm" in lerna.json', async () => { - fs.readLocalFile.mockImplementation((fileName): Promise => { - if (fileName === 'lerna.json') { - return Promise.resolve('{ "npmClient": "npm" }'); - } - return Promise.resolve(null); - }); - const res = await npmExtract.extractPackageFile( - input01Content, - 'package.json', - defaultExtractConfig - ); - expect(res).toMatchSnapshot({ - managerData: { - lernaClient: 'npm', - lernaJsonFile: 'lerna.json', - lernaPackages: undefined, - }, - }); - }); - - it('finds "npmClient":"yarn" in lerna.json', async () => { - fs.readLocalFile.mockImplementation((fileName): Promise => { - if (fileName === 'lerna.json') { - return Promise.resolve('{ "npmClient": "yarn" }'); - } - return Promise.resolve(null); - }); - const res = await npmExtract.extractPackageFile( - input01Content, - 'package.json', - defaultExtractConfig - ); - expect(res).toMatchSnapshot({ - managerData: { - lernaClient: 'yarn', - lernaJsonFile: 'lerna.json', - lernaPackages: undefined, - }, - }); - }); - - it('finds simple yarn workspaces', async () => { - fs.readLocalFile.mockImplementation((fileName): Promise => { - if (fileName === 'lerna.json') { - return Promise.resolve('{}'); - } - return Promise.resolve(null); - }); - const res = await npmExtract.extractPackageFile( - workspacesSimpleContent, - 'package.json', - defaultExtractConfig - ); - expect(res).toMatchSnapshot({ - managerData: { workspacesPackages: ['packages/*'] }, - }); - }); - - it('finds simple yarn workspaces with lerna.json and useWorkspaces: true', async () => { - fs.readLocalFile.mockImplementation((fileName): Promise => { - if (fileName === 'lerna.json') { - return Promise.resolve('{"useWorkspaces": true}'); - } - return Promise.resolve(null); - }); - const res = await npmExtract.extractPackageFile( - workspacesSimpleContent, - 'package.json', - defaultExtractConfig - ); - expect(res).toMatchSnapshot({ - managerData: { workspacesPackages: ['packages/*'] }, - }); - }); - - it('finds complex yarn workspaces', async () => { - fs.readLocalFile.mockImplementation((fileName): Promise => { - if (fileName === 'lerna.json') { - return Promise.resolve('{}'); - } - return Promise.resolve(null); - }); - const res = await npmExtract.extractPackageFile( - workspacesContent, - 'package.json', - defaultExtractConfig - ); - expect(res).toMatchSnapshot({ - managerData: { workspacesPackages: ['packages/*'] }, - }); - }); - it('extracts engines', async () => { const pJson = { dependencies: { @@ -935,9 +816,6 @@ describe('modules/manager/npm/extract/index', () => { extractedConstraints: {}, managerData: { hasPackageManager: false, - lernaClient: undefined, - lernaJsonFile: undefined, - lernaPackages: undefined, npmLock: undefined, packageJsonName: 'renovate', pnpmShrinkwrap: undefined, diff --git a/lib/modules/manager/npm/extract/index.ts b/lib/modules/manager/npm/extract/index.ts index 82b769d775841f..245d0c5c37a7c9 100644 --- a/lib/modules/manager/npm/extract/index.ts +++ b/lib/modules/manager/npm/extract/index.ts @@ -166,31 +166,7 @@ export async function extractPackageFile( yarnConfig = loadConfigFromLegacyYarnrc(repoLegacyYarnrc); } - let lernaJsonFile: string | undefined; - let lernaPackages: string[] | undefined; - let lernaClient: 'yarn' | 'npm' | undefined; let hasFancyRefs = false; - let lernaJson: - | { - packages: string[]; - npmClient: string; - useWorkspaces?: boolean; - } - | undefined; - try { - lernaJsonFile = getSiblingFileName(packageFile, 'lerna.json'); - // TODO #22198 - lernaJson = JSON.parse((await readLocalFile(lernaJsonFile, 'utf8'))!); - } catch (err) /* istanbul ignore next */ { - logger.debug({ err, lernaJsonFile }, 'Could not parse lerna.json'); - } - if (lernaJson && !lernaJson.useWorkspaces) { - lernaPackages = lernaJson.packages; - lernaClient = - lernaJson.npmClient === 'yarn' || lockFiles.yarnLock ? 'yarn' : 'npm'; - } else { - lernaJsonFile = undefined; - } const depTypes = { dependencies: 'dependency', @@ -478,7 +454,6 @@ export async function extractPackageFile( !!packageJsonName || !!packageFileVersion || !!npmrc || - !!lernaJsonFile || workspacesPackages ) ) { @@ -507,9 +482,6 @@ export async function extractPackageFile( npmrc, managerData: { ...lockFiles, - lernaClient, - lernaJsonFile, - lernaPackages, packageJsonName, yarnZeroInstall, hasPackageManager: is.nonEmptyStringAndNotWhitespace( diff --git a/lib/modules/manager/npm/extract/monorepo.spec.ts b/lib/modules/manager/npm/extract/monorepo.spec.ts index ce5c9e3e461ae1..47f9b4582a5d63 100644 --- a/lib/modules/manager/npm/extract/monorepo.spec.ts +++ b/lib/modules/manager/npm/extract/monorepo.spec.ts @@ -16,119 +16,12 @@ describe('modules/manager/npm/extract/monorepo', () => { expect(packageFiles).toHaveLength(1); }); - it('uses lerna package settings', async () => { - const packageFiles: Partial[] = [ - { - packageFile: 'package.json', - managerData: { - lernaJsonFile: 'lerna.json', - lernaPackages: ['packages/*'], - }, - deps: [ - { - depName: '@org/a', - }, - { - depName: '@org/b', - }, - { - depName: '@org/c', - }, - { - depName: 'lerna', - currentValue: '^6.0.0', - }, - ], - }, - { - packageFile: 'packages/a/package.json', - managerData: { packageJsonName: '@org/a' }, - deps: [ - { - depName: '@org/b', - }, - { - depName: '@org/c', - }, - { - depName: 'bar', - }, - ], - }, - { - packageFile: 'packages/b/package.json', - managerData: { packageJsonName: '@org/b' }, - }, - ]; - await detectMonorepos(packageFiles); - expect(packageFiles[1].managerData?.lernaJsonFile).toBe('lerna.json'); - expect( - packageFiles.some((packageFile) => - packageFile.deps?.some((dep) => dep.isInternal) - ) - ).toBeTrue(); - }); - - it('skips lerna package settings if v7 or later', async () => { - const packageFiles: Partial[] = [ - { - packageFile: 'package.json', - managerData: { - lernaJsonFile: 'lerna.json', - lernaPackages: ['packages/*'], - }, - deps: [ - { - depName: '@org/a', - }, - { - depName: '@org/b', - }, - { - depName: '@org/c', - }, - { - depName: 'lerna', - currentValue: '^7.0.0', - }, - ], - }, - { - packageFile: 'packages/a/package.json', - managerData: { packageJsonName: '@org/a' }, - deps: [ - { - depName: '@org/b', - }, - { - depName: '@org/c', - }, - { - depName: 'bar', - }, - ], - }, - { - packageFile: 'packages/b/package.json', - managerData: { packageJsonName: '@org/b' }, - }, - ]; - await detectMonorepos(packageFiles); - expect(packageFiles[1].managerData?.lernaJsonFile).toBeUndefined(); - expect( - packageFiles.some((packageFile) => - packageFile.deps?.some((dep) => dep.isInternal) - ) - ).toBeFalse(); - }); - it('updates internal packages', async () => { const packageFiles: Partial[] = [ { packageFile: 'package.json', managerData: { - lernaJsonFile: 'lerna.json', - lernaPackages: ['packages/*'], + workspacesPackages: ['packages/*'], }, deps: [ { @@ -140,10 +33,6 @@ describe('modules/manager/npm/extract/monorepo', () => { { depName: '@org/c', }, - { - depName: 'lerna', - currentValue: '6.1.0', - }, ], }, { @@ -167,7 +56,6 @@ describe('modules/manager/npm/extract/monorepo', () => { }, ]; await detectMonorepos(packageFiles); - expect(packageFiles[1].managerData?.lernaJsonFile).toBe('lerna.json'); expect( packageFiles.some((packageFile) => packageFile.deps?.some((dep) => dep.isInternal) @@ -175,37 +63,7 @@ describe('modules/manager/npm/extract/monorepo', () => { ).toBeTrue(); }); - it('uses yarn workspaces package settings with lerna', async () => { - const packageFiles: Partial[] = [ - { - packageFile: 'package.json', - managerData: { - lernaClient: 'yarn', - lernaJsonFile: 'lerna.json', - lernaPackages: ['oldpackages/*'], - workspacesPackages: ['packages/*'], - }, - deps: [ - { - depName: 'lerna', - currentValue: '^6.0.0', - }, - ], - }, - { - packageFile: 'packages/a/package.json', - managerData: { packageJsonName: '@org/a' }, - }, - { - packageFile: 'packages/b/package.json', - managerData: { packageJsonName: '@org/b' }, - }, - ]; - await detectMonorepos(packageFiles); - expect(packageFiles[1].managerData?.lernaJsonFile).toBe('lerna.json'); - }); - - it('uses yarn workspaces package settings without lerna', async () => { + it('uses yarn workspaces package settings', async () => { const packageFiles: Partial[] = [ { packageFile: 'package.json', diff --git a/lib/modules/manager/npm/extract/monorepo.ts b/lib/modules/manager/npm/extract/monorepo.ts index 3870a71dd7ffed..ad99e9f4d27925 100644 --- a/lib/modules/manager/npm/extract/monorepo.ts +++ b/lib/modules/manager/npm/extract/monorepo.ts @@ -1,5 +1,4 @@ import is from '@sindresorhus/is'; -import semver from 'semver'; import { logger } from '../../../../logger'; import { getParentDir, getSiblingFileName } from '../../../../util/fs'; import type { PackageFile } from '../../types'; @@ -12,34 +11,9 @@ export async function detectMonorepos( ): Promise { await detectPnpmWorkspaces(packageFiles); logger.debug('Detecting workspaces'); - // ignore lerna if using v7 or later by deleting all metadata - for (const p of packageFiles) { - if (p.managerData?.lernaJsonFile) { - const lernaConstraint = p.deps?.find( - (dep) => dep.depName === 'lerna' - )?.currentValue; - if ( - !lernaConstraint || - !semver.validRange(lernaConstraint) || - semver.intersects(lernaConstraint, '>=7.0.0') - ) { - logger.debug('Deleting lerna metadata as v7 or later is in use'); - delete p.managerData.lernaJsonFile; - delete p.managerData.lernaPackages; - delete p.managerData.lernaClient; - } else { - logger.warn( - 'Support for lerna <7 is now deprecated, please prioritize updating to v7' - ); - } - } - } for (const p of packageFiles) { const { packageFile, npmrc, managerData = {}, skipInstalls } = p; const { - lernaClient, - lernaJsonFile, - lernaPackages, npmLock, yarnZeroInstall, hasPackageManager, @@ -47,9 +21,7 @@ export async function detectMonorepos( yarnLock, } = managerData; - const packages = (workspacesPackages ?? lernaPackages) as - | string[] - | undefined; + const packages = workspacesPackages as string[] | undefined; if (packages?.length) { const internalPackagePatterns = ( is.array(packages) ? packages : [packages] @@ -75,10 +47,8 @@ export async function detectMonorepos( for (const subPackage of internalPackageFiles) { subPackage.managerData = subPackage.managerData ?? {}; - subPackage.managerData.lernaJsonFile = lernaJsonFile; subPackage.managerData.yarnZeroInstall = yarnZeroInstall; subPackage.managerData.hasPackageManager = hasPackageManager; - subPackage.managerData.lernaClient = lernaClient; subPackage.managerData.yarnLock ??= yarnLock; subPackage.managerData.npmLock ??= npmLock; subPackage.skipInstalls = skipInstalls && subPackage.skipInstalls; // skip if both are true diff --git a/lib/modules/manager/npm/post-update/__snapshots__/lerna.spec.ts.snap b/lib/modules/manager/npm/post-update/__snapshots__/lerna.spec.ts.snap deleted file mode 100644 index 342c3a66ed9f5c..00000000000000 --- a/lib/modules/manager/npm/post-update/__snapshots__/lerna.spec.ts.snap +++ /dev/null @@ -1,260 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`modules/manager/npm/post-update/lerna generateLockFiles() allows scripts for trust level high 1`] = ` -[ - { - "cmd": "lerna info || echo "Ignoring lerna info failure"", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "npm install --no-audit --package-lock-only", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "lerna bootstrap --no-ci -- --no-audit --package-lock-only", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/npm/post-update/lerna generateLockFiles() defaults to latest and skips bootstrap if lerna version unspecified 1`] = ` -[ - { - "cmd": "npm install --ignore-scripts --no-audit --package-lock-only", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/npm/post-update/lerna generateLockFiles() generates package-lock.json files 1`] = ` -[ - { - "cmd": "lerna info || echo "Ignoring lerna info failure"", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "npm install --ignore-scripts --no-audit --package-lock-only", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "lerna bootstrap --no-ci --ignore-scripts -- --ignore-scripts --no-audit --package-lock-only", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/npm/post-update/lerna generateLockFiles() generates yarn.lock files 1`] = ` -[ - { - "cmd": "lerna info || echo "Ignoring lerna info failure"", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "yarn install --ignore-scripts --ignore-engines --ignore-platform", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "lerna bootstrap --no-ci --ignore-scripts -- --ignore-scripts --ignore-engines --ignore-platform", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; - -exports[`modules/manager/npm/post-update/lerna generateLockFiles() performs full npm install 1`] = ` -[ - { - "cmd": "lerna info || echo "Ignoring lerna info failure"", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "npm install --ignore-scripts --no-audit", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, - { - "cmd": "lerna bootstrap --no-ci --ignore-scripts -- --ignore-scripts --no-audit", - "options": { - "cwd": "some-dir", - "encoding": "utf-8", - "env": { - "HOME": "/home/user", - "HTTPS_PROXY": "https://example.com", - "HTTP_PROXY": "http://example.com", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US", - "NO_PROXY": "localhost", - "PATH": "/tmp/path", - }, - "maxBuffer": 10485760, - "timeout": 900000, - }, - }, -] -`; diff --git a/lib/modules/manager/npm/post-update/index.spec.ts b/lib/modules/manager/npm/post-update/index.spec.ts index c8b883d84f43d7..eae6a632e6ae99 100644 --- a/lib/modules/manager/npm/post-update/index.spec.ts +++ b/lib/modules/manager/npm/post-update/index.spec.ts @@ -5,7 +5,6 @@ import { fs, git, logger, partial, scm } from '../../../../../test/util'; import { GlobalConfig } from '../../../../config/global'; import type { FileChange } from '../../../../util/git/types'; import type { PostUpdateConfig } from '../../types'; -import * as lerna from './lerna'; import * as npm from './npm'; import * as pnpm from './pnpm'; import type { AdditionalPackageFiles } from './types'; @@ -20,7 +19,6 @@ import { jest.mock('../../../../util/fs'); jest.mock('../../../../util/git'); -jest.mock('./lerna'); jest.mock('./npm'); jest.mock('./yarn'); jest.mock('./pnpm'); @@ -34,7 +32,6 @@ describe('modules/manager/npm/post-update/index', () => { { packageFile: 'packages/core/package.json', managerData: { - lernaJsonFile: 'lerna.json', npmLock: 'package-lock.json', }, npmrc: '#dummy', @@ -42,7 +39,6 @@ describe('modules/manager/npm/post-update/index', () => { { packageFile: 'packages/cli/package.json', managerData: { - lernaJsonFile: 'lerna.json', yarnLock: 'yarn.lock', }, }, @@ -77,7 +73,6 @@ describe('modules/manager/npm/post-update/index', () => { depName: 'postcss', isRemediation: true, managerData: { - lernaJsonFile: 'lerna.json', npmLock: 'package-lock.json', }, rangeStrategy: 'widen', @@ -86,7 +81,6 @@ describe('modules/manager/npm/post-update/index', () => { depName: 'core-js', isRemediation: true, managerData: { - lernaJsonFile: 'lerna.json', npmLock: 'randomFolder/package-lock.json', }, lockFiles: ['randomFolder/package-lock.json'], @@ -166,8 +160,7 @@ describe('modules/manager/npm/post-update/index', () => { additionalFiles ) ).toStrictEqual({ - lernaJsonFiles: ['lerna.json'], - npmLockDirs: ['package-lock.json'], + npmLockDirs: ['package-lock.json', 'randomFolder/package-lock.json'], pnpmShrinkwrapDirs: ['packages/pnpm/pnpm-lock.yaml'], yarnLockDirs: ['yarn.lock'], }); @@ -190,7 +183,6 @@ describe('modules/manager/npm/post-update/index', () => { {} ) ).toStrictEqual({ - lernaJsonFiles: [], npmLockDirs: [], pnpmShrinkwrapDirs: [], yarnLockDirs: ['yarn.lock'], @@ -400,13 +392,11 @@ describe('modules/manager/npm/post-update/index', () => { describe('getAdditionalFiles()', () => { const spyNpm = jest.spyOn(npm, 'generateLockFile'); - const spyLerna = jest.spyOn(lerna, 'generateLockFiles'); const spyYarn = jest.spyOn(yarn, 'generateLockFile'); const spyPnpm = jest.spyOn(pnpm, 'generateLockFile'); beforeEach(() => { spyNpm.mockResolvedValue({}); - spyLerna.mockResolvedValue({}); spyPnpm.mockResolvedValue({}); spyYarn.mockResolvedValue({}); }); @@ -451,6 +441,7 @@ describe('modules/manager/npm/post-update/index', () => { expect(fs.readLocalFile).toHaveBeenCalledWith('.npmrc', 'utf8'); expect(fs.writeLocalFile).toHaveBeenCalledWith('.npmrc', '# dummy'); expect(fs.deleteLocalFile.mock.calls).toMatchObject([ + ['randomFolder/.npmrc'], ['packages/pnpm/.npmrc'], ]); }); @@ -508,91 +499,6 @@ describe('modules/manager/npm/post-update/index', () => { expect(fs.deleteLocalFile).toHaveBeenCalled(); }); - it('works for lerna (yarn)', async () => { - git.getFile.mockImplementation((f) => { - if (f === 'yarn.lock') { - return Promise.resolve('# some contents'); - } - return Promise.resolve(null); - }); - expect( - await getAdditionalFiles( - { - ...updateConfig, - updateLockFiles: true, - reuseExistingBranch: true, - upgrades: [ - { - isRemediation: true, - packageFile: 'packages/core/package.json', - }, - ], - }, - additionalFiles - ) - ).toStrictEqual({ - artifactErrors: [], - updatedArtifacts: [ - { - type: 'addition', - path: 'packages/pnpm/pnpm-lock.yaml', - contents: undefined, - }, - { - type: 'addition', - path: 'yarn.lock', - contents: undefined, - }, - { - type: 'addition', - path: 'yarn.lock', - contents: undefined, - }, - ], - }); - expect(fs.deleteLocalFile).toHaveBeenCalled(); - }); - - it('works for lerna (npm)', async () => { - git.getFile.mockImplementation((f) => { - if (f === 'package-lock.json') { - return Promise.resolve('{}'); - } - return Promise.resolve(null); - }); - expect( - await getAdditionalFiles( - { - ...updateConfig, - updateLockFiles: true, - upgrades: [{}], - }, - { - npm: [ - { - packageFile: 'package.json', - managerData: { - lernaClient: 'npm', - lernaJsonFile: 'lerna.json', - npmLock: 'package-lock.json', - }, - }, - ], - } - ) - ).toStrictEqual({ - artifactErrors: [], - updatedArtifacts: [ - { - type: 'addition', - path: 'package-lock.json', - contents: undefined, - }, - ], - }); - expect(fs.deleteLocalFile).toHaveBeenCalled(); - }); - it('no npm files', async () => { expect(await getAdditionalFiles(baseConfig, {})).toStrictEqual({ artifactErrors: [], @@ -714,39 +620,5 @@ describe('modules/manager/npm/post-update/index', () => { updatedArtifacts: [], }); }); - - it('fails for lerna', async () => { - spyLerna.mockResolvedValueOnce({ stderr: 'some-error' }); - spyLerna.mockResolvedValueOnce({ stderr: 'some-error' }); - expect( - await getAdditionalFiles( - { - ...updateConfig, - managerData: { - npmLock: 'npm-shrinkwrap.json', - }, - updateLockFiles: true, - upgrades: [{}], - }, - { - npm: [ - { - packageFile: 'package.json', - managerData: { - lernaClient: 'npm', - lernaJsonFile: 'lerna.json', - npmLock: 'npm-shrinkwrap.json', - }, - }, - ], - } - ) - ).toStrictEqual({ - artifactErrors: [ - { lockFile: 'npm-shrinkwrap.json', stderr: 'some-error' }, - ], - updatedArtifacts: [], - }); - }); }); }); diff --git a/lib/modules/manager/npm/post-update/index.ts b/lib/modules/manager/npm/post-update/index.ts index 4fe3074a3c8960..3d2893555cbb63 100644 --- a/lib/modules/manager/npm/post-update/index.ts +++ b/lib/modules/manager/npm/post-update/index.ts @@ -3,14 +3,12 @@ import is from '@sindresorhus/is'; import deepmerge from 'deepmerge'; import { dump, load } from 'js-yaml'; import upath from 'upath'; -import { SYSTEM_INSUFFICIENT_DISK_SPACE } from '../../../../constants/error-messages'; import { logger } from '../../../../logger'; import { ExternalHostError } from '../../../../types/errors/external-host-error'; import { getChildProcessEnv } from '../../../../util/exec/env'; import { deleteLocalFile, ensureCacheDir, - getParentDir, getSiblingFileName, readLocalFile, writeLocalFile, @@ -26,7 +24,6 @@ import type { PackageFile, PostUpdateConfig, Upgrade } from '../../types'; import { getZeroInstallPaths } from '../extract/yarn'; import type { NpmManagerData } from '../types'; import { composeLockFile, parseLockFile } from '../utils'; -import * as lerna from './lerna'; import * as npm from './npm'; import * as pnpm from './pnpm'; import { processHostRules } from './rules'; @@ -50,18 +47,13 @@ export function determineLockFileDirs( const npmLockDirs: (string | undefined)[] = []; const yarnLockDirs: (string | undefined)[] = []; const pnpmShrinkwrapDirs: (string | undefined)[] = []; - const lernaJsonFiles: (string | undefined)[] = []; for (const upgrade of config.upgrades) { if (upgrade.updateType === 'lockFileMaintenance' || upgrade.isRemediation) { // Return every directory that contains a lockfile - if (upgrade.managerData?.lernaJsonFile && upgrade.managerData.npmLock) { - lernaJsonFiles.push(upgrade.managerData.lernaJsonFile); - } else { - yarnLockDirs.push(upgrade.managerData?.yarnLock); - npmLockDirs.push(upgrade.managerData?.npmLock); - pnpmShrinkwrapDirs.push(upgrade.managerData?.pnpmShrinkwrap); - } + yarnLockDirs.push(upgrade.managerData?.yarnLock); + npmLockDirs.push(upgrade.managerData?.npmLock); + pnpmShrinkwrapDirs.push(upgrade.managerData?.pnpmShrinkwrap); continue; } if (upgrade.isLockfileUpdate) { @@ -80,7 +72,6 @@ export function determineLockFileDirs( yarnLockDirs: getDirs(yarnLockDirs), npmLockDirs: getDirs(npmLockDirs), pnpmShrinkwrapDirs: getDirs(pnpmShrinkwrapDirs), - lernaJsonFiles: getDirs(lernaJsonFiles), }; } @@ -107,32 +98,16 @@ export function determineLockFileDirs( if (!packageFile.managerData) { continue; } - // lerna first - if ( - packageFile.managerData?.lernaJsonFile && - packageFile.managerData.npmLock - ) { - logger.debug(`${packageFile.packageFile} has lerna lock file`); - lernaJsonFiles.push(packageFile.managerData.lernaJsonFile); - } else if ( - packageFile.managerData?.lernaJsonFile && - packageFile.managerData.yarnLock && - !packageFile.managerData.workspacesPackages?.length - ) { - lernaJsonFiles.push(packageFile.managerData.lernaJsonFile); - } else { - // push full lock file names and convert them later - yarnLockDirs.push(packageFile.managerData.yarnLock); - npmLockDirs.push(packageFile.managerData.npmLock); - pnpmShrinkwrapDirs.push(packageFile.managerData.pnpmShrinkwrap); - } + // push full lock file names and convert them later + yarnLockDirs.push(packageFile.managerData.yarnLock); + npmLockDirs.push(packageFile.managerData.npmLock); + pnpmShrinkwrapDirs.push(packageFile.managerData.pnpmShrinkwrap); } return { yarnLockDirs: getDirs(yarnLockDirs), npmLockDirs: getDirs(npmLockDirs), pnpmShrinkwrapDirs: getDirs(pnpmShrinkwrapDirs), - lernaJsonFiles: getDirs(lernaJsonFiles), }; } @@ -734,147 +709,5 @@ export async function getAdditionalFiles( await resetNpmrcContent(lockFileDir, npmrcContent); } - for (const lernaJsonFile of dirs.lernaJsonFiles) { - let lockFile: string; - logger.debug(`Finding package.json for lerna location "${lernaJsonFile}"`); - const lernaPackageFile = packageFiles.npm.find( - // TODO #22198 - (p) => getParentDir(p.packageFile!) === getParentDir(lernaJsonFile) - ); - // istanbul ignore if: not sure how to test - if (!lernaPackageFile) { - logger.debug('No matching package.json found'); - throw new Error('lerna-no-lockfile'); - } - if (lernaPackageFile.managerData?.lernaClient === 'npm') { - lockFile = config.managerData?.npmLock ?? 'package-lock.json'; - } else { - lockFile = config.managerData?.yarnLock ?? 'yarn.lock'; - } - const skipInstalls = - lockFile === 'npm-shrinkwrap.json' ? false : config.skipInstalls; - const learnaFileDir = getParentDir(lernaJsonFile); - const npmrcContent = await getNpmrcContent(learnaFileDir); - await updateNpmrcContent( - learnaFileDir, - npmrcContent, - additionalNpmrcContent - ); - const res = await lerna.generateLockFiles( - lernaPackageFile, - getParentDir(lernaJsonFile), - config, - env, - skipInstalls - ); - if (res.stderr) { - // istanbul ignore if - if (res.stderr.includes('ENOSPC: no space left on device')) { - throw new Error(SYSTEM_INSUFFICIENT_DISK_SPACE); - } - for (const upgrade of config.upgrades) { - /* eslint-disable no-useless-escape */ - // istanbul ignore if: needs test - if ( - res.stderr.includes( - `Couldn't find any versions for \\\"${upgrade.depName}\\\"` - ) - ) { - logger.debug( - { dependency: upgrade.depName, type: 'yarn' }, - 'lock file failed for the dependency being updated - skipping branch creation' - ); - throw new ExternalHostError( - Error( - 'lock file failed for the dependency being updated - skipping branch creation' - ), - NpmDatasource.id - ); - } - /* eslint-enable no-useless-escape */ - // istanbul ignore if: needs test - if ( - res.stderr.includes( - `No matching version found for ${upgrade.depName}` - ) - ) { - logger.debug( - { dependency: upgrade.depName, type: 'npm' }, - 'lock file failed for the dependency being updated - skipping branch creation' - ); - throw new ExternalHostError( - Error( - 'lock file failed for the dependency being updated - skipping branch creation' - ), - NpmDatasource.id - ); - } - } - artifactErrors.push({ - lockFile, - stderr: res.stderr, - }); - } else { - for (const packageFile of packageFiles.npm) { - const filename = - packageFile.managerData?.npmLock ?? packageFile.managerData?.yarnLock; - // istanbul ignore if - if (!is.nonEmptyString(filename)) { - continue; - } - logger.trace(`Checking for ${filename}`); - const existingContent = await getFile( - // TODO #22198 - filename, - config.reuseExistingBranch ? config.branchName : config.baseBranch - ); - if (existingContent) { - logger.trace('Found lock file'); - // TODO #22198 - const lockFilePath = filename; - logger.trace('Checking against ' + lockFilePath); - try { - const newContent = - (await readLocalFile(lockFilePath, 'utf8')) ?? - (await readLocalFile( - lockFilePath.replace( - 'npm-shrinkwrap.json', - 'package-lock.json' - ), - 'utf8' - )); - // istanbul ignore if: needs test - if (newContent === existingContent) { - logger.trace('File is unchanged'); - } else { - logger.debug('File is updated: ' + lockFilePath); - updatedArtifacts.push({ - type: 'addition', - // TODO #22198 - path: filename, - contents: newContent, - }); - } - } catch (err) /* istanbul ignore next */ { - if (config.updateType === 'lockFileMaintenance') { - logger.debug( - { packageFile, lockFilePath }, - 'No lock file found after lerna lockFileMaintenance' - ); - } else { - logger.warn( - { packageFile, lockFilePath }, - 'No lock file found after lerna bootstrap' - ); - } - } - } else { - logger.trace('No lock file found'); - } - } - } - await resetNpmrcContent(learnaFileDir, npmrcContent); - } - return { artifactErrors, updatedArtifacts }; } diff --git a/lib/modules/manager/npm/post-update/lerna.spec.ts b/lib/modules/manager/npm/post-update/lerna.spec.ts deleted file mode 100644 index 8115050c2ac9ab..00000000000000 --- a/lib/modules/manager/npm/post-update/lerna.spec.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { envMock, mockExecAll } from '../../../../../test/exec-util'; -import { env, fs, mockedFunction, partial } from '../../../../../test/util'; -import { GlobalConfig } from '../../../../config/global'; -import type { RepoGlobalConfig } from '../../../../config/types'; -import type { PackageFileContent, PostUpdateConfig } from '../../types'; -import * as lernaHelper from './lerna'; -import { getNodeToolConstraint } from './node-version'; - -jest.mock('../../../../util/exec/env'); -jest.mock('../../../../util/fs'); -jest.mock('./node-version'); -jest.mock('../../../datasource'); - -process.env.CONTAINERBASE = 'true'; - -function lernaPkgFile(lernaClient: string): Partial { - return { - managerData: { lernaClient }, - }; -} - -const config = partial({ constraints: { lerna: '2.0.0' } }); - -describe('modules/manager/npm/post-update/lerna', () => { - const globalConfig: RepoGlobalConfig = { - localDir: '', - cacheDir: '/tmp/cache', - }; - - describe('generateLockFiles()', () => { - beforeEach(() => { - jest.resetAllMocks(); - jest.resetModules(); - env.getChildProcessEnv.mockReturnValue(envMock.basic); - GlobalConfig.set(globalConfig); - mockedFunction(getNodeToolConstraint).mockResolvedValueOnce({ - toolName: 'node', - constraint: '16.16.0', - }); - }); - - it('returns if no lernaClient', async () => { - const res = await lernaHelper.generateLockFiles( - {}, - 'some-dir', - config, - {} - ); - expect(res.error).toBeFalse(); - }); - - it('returns if invalid lernaClient', async () => { - const res = await lernaHelper.generateLockFiles( - lernaPkgFile('foo'), - 'some-dir', - config, - {} - ); - expect(res.error).toBeFalse(); - }); - - it('generates package-lock.json files', async () => { - const execSnapshots = mockExecAll(); - const skipInstalls = true; - const res = await lernaHelper.generateLockFiles( - lernaPkgFile('npm'), - 'some-dir', - config, - {}, - skipInstalls - ); - expect(res.error).toBeFalse(); - expect(execSnapshots).toMatchSnapshot(); - }); - - it('performs full npm install', async () => { - const execSnapshots = mockExecAll(); - const skipInstalls = false; - const res = await lernaHelper.generateLockFiles( - lernaPkgFile('npm'), - 'some-dir', - config, - {}, - skipInstalls - ); - expect(res.error).toBeFalse(); - expect(execSnapshots).toMatchSnapshot(); - }); - - it('generates yarn.lock files', async () => { - const execSnapshots = mockExecAll(); - fs.readLocalFile.mockResolvedValueOnce( - '{"packageManager":"yarn@^1.10.0"}' - ); - const res = await lernaHelper.generateLockFiles( - lernaPkgFile('yarn'), - 'some-dir', - { ...config }, - {} - ); - expect(execSnapshots).toMatchSnapshot(); - expect(res.error).toBeFalse(); - }); - - it('defaults to latest and skips bootstrap if lerna version unspecified', async () => { - const execSnapshots = mockExecAll(); - const res = await lernaHelper.generateLockFiles( - lernaPkgFile('npm'), - 'some-dir', - { ...config, constraints: null }, - {} - ); - expect(res.error).toBeFalse(); - expect(execSnapshots).toMatchSnapshot(); - }); - - it('allows scripts for trust level high', async () => { - const execSnapshots = mockExecAll(); - GlobalConfig.set({ ...globalConfig, allowScripts: true }); - const res = await lernaHelper.generateLockFiles( - lernaPkgFile('npm'), - 'some-dir', - { ...config, constraints: { ...config.constraints, npm: '^6.0.0' } }, - {} - ); - expect(res.error).toBeFalse(); - expect(execSnapshots).toMatchSnapshot(); - }); - - it('suppports docker', async () => { - const execSnapshots = mockExecAll(); - GlobalConfig.set({ - ...globalConfig, - binarySource: 'docker', - dockerSidecarImage: 'ghcr.io/containerbase/sidecar', - }); - - const res = await lernaHelper.generateLockFiles( - lernaPkgFile('npm'), - 'some-dir', - { ...config, constraints: { ...config.constraints, npm: '6.0.0' } }, - {} - ); - expect(execSnapshots).toMatchObject([ - { - cmd: 'docker pull ghcr.io/containerbase/sidecar', - }, - { - cmd: 'docker ps --filter name=renovate_sidecar -aq', - }, - { - cmd: - 'docker run --rm --name=renovate_sidecar --label=renovate_child ' + - '-v "/tmp/cache":"/tmp/cache" ' + - '-e CONTAINERBASE_CACHE_DIR ' + - '-w "some-dir" ghcr.io/containerbase/sidecar ' + - 'bash -l -c "' + - 'install-tool node 16.16.0 ' + - '&& ' + - 'install-tool npm 6.0.0 ' + - '&& ' + - 'hash -d npm 2>/dev/null || true ' + - '&& ' + - 'install-tool lerna 2.0.0 ' + - '&& ' + - 'lerna info || echo \\"Ignoring lerna info failure\\" ' + - '&& ' + - 'npm install --ignore-scripts --no-audit --package-lock-only ' + - '&& ' + - 'lerna bootstrap --no-ci --ignore-scripts -- --ignore-scripts --no-audit --package-lock-only' + - '"', - options: { - cwd: 'some-dir', - }, - }, - ]); - expect(res.error).toBeFalse(); - }); - - it('suppports binarySource=install', async () => { - const execSnapshots = mockExecAll(); - GlobalConfig.set({ ...globalConfig, binarySource: 'install' }); - - const res = await lernaHelper.generateLockFiles( - lernaPkgFile('npm'), - 'some-dir', - { - ...config, - constraints: { ...config.constraints, lerna: '^7.1.4', npm: '6.0.0' }, - }, - {} - ); - expect(res.error).toBeFalse(); - expect(execSnapshots).toMatchObject([ - { cmd: 'install-tool node 16.16.0' }, - { cmd: 'install-tool npm 6.0.0' }, - { cmd: 'hash -d npm 2>/dev/null || true' }, - { - cmd: 'npm install --ignore-scripts --no-audit --package-lock-only', - options: { - cwd: 'some-dir', - }, - }, - ]); - }); - }); - - describe('getLernaVersion()', () => { - it('returns specified version', () => { - const pkg = {}; - expect( - lernaHelper.getLernaConstraint(pkg, { - devDependencies: { lerna: '2.0.0' }, - }) - ).toBe('2.0.0'); - }); - - it('returns specified range', () => { - const pkg = {}; - expect( - lernaHelper.getLernaConstraint(pkg, { - dependencies: { lerna: '1.x || >=2.5.0 || 5.0.0 - 7.2.3' }, - }) - ).toBe('1.x || >=2.5.0 || 5.0.0 - 7.2.3'); - }); - - it('returns latest if no lerna dep is specified', () => { - const pkg = {}; - expect(lernaHelper.getLernaConstraint(pkg, {})).toBeNull(); - }); - - it('returns latest if pkg has no deps at all', () => { - const pkg = {}; - expect(lernaHelper.getLernaConstraint(pkg, {})).toBeNull(); - }); - - it('returns latest if specified lerna version is not a valid semVer range', () => { - const pkg = {}; - expect( - lernaHelper.getLernaConstraint(pkg, { engines: { lerna: '[a.b.c;' } }) - ).toBeNull(); - }); - }); -}); diff --git a/lib/modules/manager/npm/post-update/lerna.ts b/lib/modules/manager/npm/post-update/lerna.ts deleted file mode 100644 index 89e825e0daf69e..00000000000000 --- a/lib/modules/manager/npm/post-update/lerna.ts +++ /dev/null @@ -1,154 +0,0 @@ -import is from '@sindresorhus/is'; -import semver from 'semver'; -import upath from 'upath'; -import { GlobalConfig } from '../../../../config/global'; -import { TEMPORARY_ERROR } from '../../../../constants/error-messages'; -import { logger } from '../../../../logger'; -import { exec } from '../../../../util/exec'; -import type { - ExecOptions, - ExtraEnv, - ToolConstraint, -} from '../../../../util/exec/types'; -import type { - PackageFile, - PackageFileContent, - PostUpdateConfig, -} from '../../types'; -import type { PackageJsonSchema } from '../schema'; -import type { NpmManagerData } from '../types'; -import { getNodeToolConstraint } from './node-version'; -import type { GenerateLockFileResult } from './types'; -import { getPackageManagerVersion, lazyLoadPackageJson } from './utils'; - -// Exported for testability -export function getLernaConstraint( - lernaPackageFile: Partial>, - lazyPkgJson: PackageJsonSchema -): string | null { - const constraint = - lazyPkgJson.dependencies?.lerna ?? lazyPkgJson.devDependencies?.lerna; - if (!constraint || !semver.validRange(constraint)) { - logger.warn( - // TODO: types (#22198) - `Could not detect lerna version in ${lernaPackageFile.packageFile}, using 'latest'` - ); - return null; - } - return constraint; -} - -export async function generateLockFiles( - lernaPackageFile: Partial>, - lockFileDir: string, - config: PostUpdateConfig, - env: NodeJS.ProcessEnv, - skipInstalls?: boolean | null -): Promise { - const lernaClient = lernaPackageFile.managerData?.lernaClient; - if (!is.nonEmptyString(lernaClient)) { - logger.warn('No lernaClient specified - returning'); - return { error: false }; - } - logger.debug(`Spawning lerna with ${lernaClient} to create lock files`); - - const cmd: string[] = []; - let cmdOptions = ''; - try { - const lazyPgkJson = lazyLoadPackageJson(lockFileDir); - const toolConstraints: ToolConstraint[] = [ - await getNodeToolConstraint(config, [], lockFileDir, lazyPgkJson), - ]; - if (lernaClient === 'yarn') { - const yarnTool: ToolConstraint = { - toolName: 'yarn', - constraint: '^1.22.18', // needs to be a v1 yarn, otherwise v2 will be installed - }; - const yarnCompatibility = - config.constraints?.yarn ?? - getPackageManagerVersion('yarn', await lazyPgkJson.getValue()); - if (semver.validRange(yarnCompatibility)) { - yarnTool.constraint = yarnCompatibility; - } - toolConstraints.push(yarnTool); - if (skipInstalls !== false) { - // The following change causes Yarn 1.x to exit gracefully after updating the lock file but without installing node_modules - yarnTool.toolName = 'yarn-slim'; - } - cmdOptions = '--ignore-scripts --ignore-engines --ignore-platform'; - } else if (lernaClient === 'npm') { - const npmTool: ToolConstraint = { toolName: 'npm' }; - const npmCompatibility = - config.constraints?.npm ?? - getPackageManagerVersion('npm', await lazyPgkJson.getValue()); - if (semver.validRange(npmCompatibility)) { - npmTool.constraint = npmCompatibility; - } - toolConstraints.push(npmTool); - cmdOptions = '--ignore-scripts --no-audit'; - if (skipInstalls !== false) { - cmdOptions += ' --package-lock-only'; - } - } else { - logger.warn({ lernaClient }, 'Unknown lernaClient'); - return { error: false }; - } - let lernaCommand = `lerna bootstrap --no-ci --ignore-scripts -- `; - if (GlobalConfig.get('allowScripts') && !config.ignoreScripts) { - cmdOptions = cmdOptions.replace('--ignore-scripts ', ''); - lernaCommand = lernaCommand.replace('--ignore-scripts ', ''); - } - lernaCommand += cmdOptions; - const extraEnv: ExtraEnv = { - NPM_CONFIG_CACHE: env.NPM_CONFIG_CACHE, - npm_config_store: env.npm_config_store, - }; - const execOptions: ExecOptions = { - cwdFile: upath.join(lockFileDir, 'package.json'), - extraEnv, - docker: {}, - toolConstraints, - }; - // istanbul ignore if - if (GlobalConfig.get('exposeAllEnv')) { - extraEnv.NPM_AUTH = env.NPM_AUTH; - extraEnv.NPM_EMAIL = env.NPM_EMAIL; - } - const lernaConstraint = - config.constraints?.lerna ?? - getLernaConstraint(lernaPackageFile, await lazyPgkJson.getValue()); - if ( - !is.string(lernaConstraint) || - (semver.valid(lernaConstraint) && - semver.gte(lernaConstraint, '7.0.0')) === true || - (semver.validRange(lernaConstraint) && - (semver.satisfies('7.0.0', lernaConstraint) || - semver.satisfies('7.999.999', lernaConstraint))) - ) { - logger.debug('Skipping lerna bootstrap for lerna >= 7.0.0'); - cmd.push(`${lernaClient} install ${cmdOptions}`); - } else { - logger.debug(`Using lerna version ${lernaConstraint}`); - toolConstraints.push({ toolName: 'lerna', constraint: lernaConstraint }); - cmd.push('lerna info || echo "Ignoring lerna info failure"'); - cmd.push(`${lernaClient} install ${cmdOptions}`); - cmd.push(lernaCommand); - } - await exec(cmd, execOptions); - } catch (err) /* istanbul ignore next */ { - if (err.message === TEMPORARY_ERROR) { - throw err; - } - logger.debug( - { - cmd, - err, - type: 'lerna', - lernaClient, - }, - 'lock file error' - ); - return { error: true, stderr: err.stderr }; - } - return { error: false }; -} diff --git a/lib/modules/manager/npm/post-update/types.ts b/lib/modules/manager/npm/post-update/types.ts index 58e687d48c9dc2..ed00f0967ffcd7 100644 --- a/lib/modules/manager/npm/post-update/types.ts +++ b/lib/modules/manager/npm/post-update/types.ts @@ -6,7 +6,6 @@ export interface DetermineLockFileDirsResult { yarnLockDirs: string[]; npmLockDirs: string[]; pnpmShrinkwrapDirs: string[]; - lernaJsonFiles: string[]; } export interface AdditionalPackageFiles { diff --git a/lib/modules/manager/npm/readme.md b/lib/modules/manager/npm/readme.md index 9612cb9bf134a6..572cdb650347cc 100644 --- a/lib/modules/manager/npm/readme.md +++ b/lib/modules/manager/npm/readme.md @@ -7,3 +7,7 @@ The following `depTypes` are currently supported by the npm manager : - `engines` : Renovate will update any `node`, `npm` and `yarn` version specified under `engines`. - `volta` : Renovate will update any `node`, `npm`, `pnpm` and `yarn` version specified under `volta`. - `packageManager` + +Lerna is no longer explicitly supported. +Since v7, the removal of `lerna bootstrap` has meant that Renovate needs no Lerna-specific awareness/functionality. +In Renovate v37, it was decided to drop explicit Lerna support for Lerna v6 and below to enable a simplification of the npm, pnpm and Yarn logic. diff --git a/lib/modules/manager/npm/types.ts b/lib/modules/manager/npm/types.ts index 526afafbf17382..90cd5c91fa1e8a 100644 --- a/lib/modules/manager/npm/types.ts +++ b/lib/modules/manager/npm/types.ts @@ -81,9 +81,6 @@ export interface NpmLockFiles { export interface NpmManagerData extends NpmLockFiles, Record { hasPackageManager?: boolean; - lernaClient?: string; - lernaJsonFile?: string; - lernaPackages?: string[]; packageJsonName?: string; parents?: string[]; yarnZeroInstall?: boolean; diff --git a/lib/util/exec/containerbase.ts b/lib/util/exec/containerbase.ts index 6571d1850b8b98..3083b7a9b41155 100644 --- a/lib/util/exec/containerbase.ts +++ b/lib/util/exec/containerbase.ts @@ -104,11 +104,6 @@ const allToolConfig: Record = { extractVersion: '^kustomize/v(?.*)$', versioning: semverVersioningId, }, - lerna: { - datasource: 'npm', - packageName: 'lerna', - versioning: npmVersioningId, - }, maven: { datasource: 'maven', packageName: 'org.apache.maven:maven', diff --git a/lib/workers/repository/update/branch/lock-files/index.spec.ts b/lib/workers/repository/update/branch/lock-files/index.spec.ts index e3d77a7e112022..88e40ff39e2b0c 100644 --- a/lib/workers/repository/update/branch/lock-files/index.spec.ts +++ b/lib/workers/repository/update/branch/lock-files/index.spec.ts @@ -1,7 +1,6 @@ import { fs, git, mocked } from '../../../../../../test/util'; import { GlobalConfig } from '../../../../../config/global'; import * as lockFiles from '../../../../../modules/manager/npm/post-update'; -import * as lerna from '../../../../../modules/manager/npm/post-update/lerna'; import * as npm from '../../../../../modules/manager/npm/post-update/npm'; import * as pnpm from '../../../../../modules/manager/npm/post-update/pnpm'; import * as yarn from '../../../../../modules/manager/npm/post-update/yarn'; @@ -90,7 +89,6 @@ describe('workers/repository/update/branch/lock-files/index', () => { jest.spyOn(pnpm, 'generateLockFile').mockResolvedValueOnce({ lockFile: 'some lock file contents', }); - jest.spyOn(lerna, 'generateLockFiles'); jest.spyOn(lockFiles, 'determineLockFileDirs'); });