From 1bf1faecc4dc3d434c93f13044b6b19ef17e21f0 Mon Sep 17 00:00:00 2001 From: Rhys Arkins Date: Sun, 27 Aug 2017 07:26:33 +0200 Subject: [PATCH] feat: yarn workspaces lock file support This feature adds explicit support for correctly generating the yarn.lock file for workspaces. Specifically, it means that the yarn.lock in the root directory is regenerated whenever *any* package.json is modified. Previously lock files were only every updated if its corresponding package.json changes, but that is not the way yarn workspaces works. Closes #473 --- lib/workers/branch/lock-files.js | 5 ++++ lib/workers/package-file/index.js | 11 -------- lib/workers/repository/apis.js | 28 ++++++++++++++++--- .../__snapshots__/lock-files.spec.js.snap | 9 ++++++ test/workers/branch/lock-files.spec.js | 24 ++++++++++++++++ test/workers/package-file/index.spec.js | 6 ---- .../__snapshots__/apis.spec.js.snap | 4 ++- test/workers/repository/apis.spec.js | 5 +++- 8 files changed, 69 insertions(+), 23 deletions(-) diff --git a/lib/workers/branch/lock-files.js b/lib/workers/branch/lock-files.js index 28829ee7399081..bef878c5f2f6cc 100644 --- a/lib/workers/branch/lock-files.js +++ b/lib/workers/branch/lock-files.js @@ -67,6 +67,11 @@ function determineLockFileDirs(config) { } } + // If yarn workspaces are in use, then we need to generate yarn.lock from root dir + if (config.updatedPackageFiles.length && config.hasYarnWorkspaces) { + yarnLockFileDirs.push(path.dirname('package.json')); + } + return { yarnLockFileDirs, packageLockFileDirs }; } diff --git a/lib/workers/package-file/index.js b/lib/workers/package-file/index.js index 46916f359dec71..b570401bdb799a 100644 --- a/lib/workers/package-file/index.js +++ b/lib/workers/package-file/index.js @@ -23,17 +23,6 @@ async function renovatePackageFile(packageFileConfig) { return upgrades; } - // Warn if workspaces found - if (config.content.workspaces) { - logger.warn('Found workspaces'); - const warn = { ...config }; - warn.depName = 'workspaces'; - warn.type = 'warning'; - warn.message = - 'workspaces configuration detected in `package.json` but this is currently unsupported. Please see https://github.com/singapore/renovate/issues/473 for details.'; - upgrades.push(warn); - } - const depTypes = [ 'dependencies', 'devDependencies', diff --git a/lib/workers/repository/apis.js b/lib/workers/repository/apis.js index a8458b3e18974f..55bfb0916885bc 100644 --- a/lib/workers/repository/apis.js +++ b/lib/workers/repository/apis.js @@ -206,30 +206,42 @@ async function mergeRenovateJson(config, branchName) { async function detectPackageFiles(input) { const config = { ...input }; - config.logger.trace({ config }, 'detectPackageFiles'); + const { logger } = config; + logger.trace({ config }, 'detectPackageFiles'); config.packageFiles = await config.api.findFilePaths('package.json'); - config.logger.debug( + logger.debug( { packageFiles: config.packageFiles }, `Found ${config.packageFiles.length} package file(s)` ); if (Array.isArray(config.ignorePaths)) { + logger.debug('Checking ignorePaths'); const skippedPackageFiles = []; config.packageFiles = config.packageFiles.filter(packageFile => { + logger.trace(`Checking ${packageFile}`); if ( - config.ignorePaths.some(ignorePath => packageFile.includes(ignorePath)) + config.ignorePaths.some(ignorePath => { + logger.trace(` ..against ${ignorePath}`); + return packageFile.includes(ignorePath); + }) ) { + logger.trace('Filtered out'); skippedPackageFiles.push(packageFile); return false; } + logger.trace('Included'); return true; }); if (skippedPackageFiles.length) { + logger.debug( + { skippedPackageFiles }, + `Skipped ${skippedPackageFiles.length} file(s)` + ); config.foundIgnoredPaths = true; config.warnings.push({ depName: 'packageFiles', message: `Skipped package.json files found within ignored paths: \`${skippedPackageFiles}\``, }); - config.logger.debug( + logger.debug( `Now have ${config.packageFiles.length} package file(s) after filtering` ); } @@ -264,6 +276,14 @@ async function resolvePackageFiles(inputConfig) { config.baseBranch ); if (packageFile.content) { + // check for workspaces + if ( + packageFile.packageFile === 'package.json' && + packageFile.content.workspaces + ) { + config.logger.info('Found yarn workspaces configuration'); + config.hasYarnWorkspaces = true; + } // hoist renovate config if exists if (packageFile.content.renovate) { config.hasPackageJsonRenovateConfig = true; diff --git a/test/workers/branch/__snapshots__/lock-files.spec.js.snap b/test/workers/branch/__snapshots__/lock-files.spec.js.snap index 5e234a665e565d..dc3f6ab903bcef 100644 --- a/test/workers/branch/__snapshots__/lock-files.spec.js.snap +++ b/test/workers/branch/__snapshots__/lock-files.spec.js.snap @@ -22,6 +22,15 @@ Object { } `; +exports[`workers/branch/lock-files determineLockFileDirs returns root directory if using yarn workspaces 1`] = ` +Object { + "packageLockFileDirs": Array [], + "yarnLockFileDirs": Array [ + ".", + ], +} +`; + exports[`workers/branch/lock-files getUpdatedLockFiles adds multiple lock files 1`] = ` Object { "lockFileError": false, diff --git a/test/workers/branch/lock-files.spec.js b/test/workers/branch/lock-files.spec.js index d4231e658fc3cc..05cdbd3bf5b239 100644 --- a/test/workers/branch/lock-files.spec.js +++ b/test/workers/branch/lock-files.spec.js @@ -149,6 +149,30 @@ describe('workers/branch/lock-files', () => { const res = determineLockFileDirs(config); expect(res).toMatchSnapshot(); }); + it('returns root directory if using yarn workspaces', () => { + config.hasYarnWorkspaces = true; + config.upgrades = [{}]; + config.packageFiles = [ + { + packageFile: 'package.json', + hasYarnLock: true, + }, + { + packageFile: 'backend/package.json', + }, + ]; + config.updatedPackageFiles = [ + { + name: 'backend/package.json', + contents: 'some contents', + }, + ]; + const res = determineLockFileDirs(config); + expect(res).toMatchSnapshot(); + expect(res.packageLockFileDirs).toHaveLength(0); + expect(res.yarnLockFileDirs).toHaveLength(1); + expect(res.yarnLockFileDirs[0]).toEqual('.'); + }); }); describe('writeExistingFiles', () => { let config; diff --git a/test/workers/package-file/index.spec.js b/test/workers/package-file/index.spec.js index 826d8f16eaf059..453f1cd484696b 100644 --- a/test/workers/package-file/index.spec.js +++ b/test/workers/package-file/index.spec.js @@ -26,12 +26,6 @@ describe('packageFileWorker', () => { const res = await packageFileWorker.renovatePackageFile(config); expect(res).toEqual([]); }); - it('warns if using workspaces', async () => { - config.content.workspaces = {}; - const res = await packageFileWorker.renovatePackageFile(config); - expect(res).toHaveLength(1); - expect(res[0].type).toEqual('warning'); - }); it('returns upgrades', async () => { depTypeWorker.renovateDepType.mockReturnValueOnce([{}]); depTypeWorker.renovateDepType.mockReturnValueOnce([{}, {}]); diff --git a/test/workers/repository/__snapshots__/apis.spec.js.snap b/test/workers/repository/__snapshots__/apis.spec.js.snap index d27792d0035d8c..f3a1a1a96dee08 100644 --- a/test/workers/repository/__snapshots__/apis.spec.js.snap +++ b/test/workers/repository/__snapshots__/apis.spec.js.snap @@ -93,7 +93,9 @@ exports[`workers/repository/apis mergeRenovateJson(config) returns warning + err exports[`workers/repository/apis resolvePackageFiles includes files with content 1`] = ` Array [ Object { - "content": Object {}, + "content": Object { + "workspaces": Array [], + }, "errors": Array [], "hasPackageLock": true, "hasYarnLock": true, diff --git a/test/workers/repository/apis.spec.js b/test/workers/repository/apis.spec.js index 872bc8ab7c10b3..346eee59e40520 100644 --- a/test/workers/repository/apis.spec.js +++ b/test/workers/repository/apis.spec.js @@ -285,7 +285,10 @@ describe('workers/repository/apis', () => { expect(res.packageFiles).toEqual([]); }); it('includes files with content', async () => { - config.api.getFileJson.mockReturnValueOnce({ renovate: {} }); + config.api.getFileJson.mockReturnValueOnce({ + renovate: {}, + workspaces: [], + }); config.api.getFileJson.mockReturnValueOnce({}); config.api.getFileContent.mockReturnValueOnce(null); config.api.getFileContent.mockReturnValueOnce(null);