diff --git a/docs/configuration.md b/docs/configuration.md index 27535462ef9b07f6b2cb7b2f2342f62a12b98937..db30434596f7148e521f0c384c1eb5807bf7f6e6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -250,6 +250,14 @@ Obviously, you can't set repository or package file location with this method. <td>`RENOVATE_YARNRC`</td> <td>`--yarnrc`<td> </tr> +<tr> + <td>`copyLocalLibs`</td> + <td>enable copy local libraries found in package.json like `"lib1: file:../path/to/folder"`, warning: feature may be deprecated in future.</td> + <td>boolean</td> + <td><pre>false</pre></td> + <td>`RENOVATE_COPY_LOCAL_LIBS`</td> + <td>`--copy-local-libs`<td> +</tr> <tr> <td>`ignoreNpmrcFile`</td> <td>Whether to ignore any .npmrc file found in repository</td> diff --git a/lib/config/definitions.js b/lib/config/definitions.js index 60fa204d50e0263f6e405be0ce0a807329cb3e72..b04802f417197b1265ed7cb8f42819d71b74b529 100644 --- a/lib/config/definitions.js +++ b/lib/config/definitions.js @@ -153,6 +153,13 @@ const options = [ stage: 'branch', type: 'string', }, + { + name: 'copyLocalLibs', + description: + 'enable copy local libraries found in package.json like `"lib1: file:../path/to/folder"`, warning: feature may be deprecated in future.', + type: 'boolean', + default: false, + }, { name: 'ignoreNpmrcFile', description: 'Whether to ignore any .npmrc file found in repository', diff --git a/lib/workers/branch/lock-files.js b/lib/workers/branch/lock-files.js index 6828ff0cba1c34c83f5f38d48b393f198c73f880..bfb1365a1caa816a60170235dead2b4c2b63bc2e 100644 --- a/lib/workers/branch/lock-files.js +++ b/lib/workers/branch/lock-files.js @@ -123,6 +123,49 @@ async function writeExistingFiles(config) { upath.join(basedir, 'package.json'), JSON.stringify(massagedFile) ); + + if (config.copyLocalLibs) { + const toCopy = listLocalLibs(massagedFile.dependencies); + toCopy.push(...listLocalLibs(massagedFile.devDependencies)); + if (toCopy.length !== 0) { + logger.debug(`listOfNeededLocalFiles files to copy: ${toCopy}`); + await Promise.all( + toCopy.map(async localPath => { + const resolvedLocalPath = upath.join( + path.resolve(basedir, localPath) + ); + if ( + !resolvedLocalPath.startsWith(upath.join(config.tmpDir.path)) + ) { + logger.info( + `local lib '${ + toCopy + }' will not be copied because it's out of the repo.` + ); + } else { + // at the root of local Lib we should find a package.json so that yarn/npm will use it to update *lock file + const resolvedRepoPath = upath.join( + resolvedLocalPath.substring(config.tmpDir.path.length + 1), + 'package.json' + ); + const fileContent = await platform.getFile(resolvedRepoPath); + if (fileContent !== null) { + await fs.outputFile( + upath.join(resolvedLocalPath, 'package.json'), + fileContent + ); + } else { + logger.info( + `listOfNeededLocalFiles - file '${ + resolvedRepoPath + }' not found.` + ); + } + } + }) + ); + } + } } if (packageFile.npmrc) { logger.debug(`Writing .npmrc to ${basedir}`); @@ -161,6 +204,27 @@ async function writeExistingFiles(config) { } } +function listLocalLibs(dependencies) { + logger.trace(`listLocalLibs (${dependencies})`); + const toCopy = []; + if (dependencies) { + for (const [libName, libVersion] of Object.entries(dependencies)) { + if (libVersion.startsWith('file:')) { + if (libVersion.endsWith('.tgz')) { + logger.info( + `Link to local lib "${libName}": "${ + libVersion + }" is not supported. Please do it like: 'file:/path/to/folder'` + ); + } else { + toCopy.push(libVersion.substring('file:'.length)); + } + } + } + } + return toCopy; +} + async function writeUpdatedPackageFiles(config) { logger.trace({ config }, 'writeUpdatedPackageFiles'); logger.debug('Writing any updated package files'); diff --git a/test/manager/__snapshots__/resolve.spec.js.snap b/test/manager/__snapshots__/resolve.spec.js.snap index 95e312ac25c6cdd8883c65d58310545779d30e50..2502e7d1b448f1870d11aeea843540bfc8cc555e 100644 --- a/test/manager/__snapshots__/resolve.spec.js.snap +++ b/test/manager/__snapshots__/resolve.spec.js.snap @@ -10,6 +10,7 @@ Object { "branchName": "{{branchPrefix}}{{depNameSanitized}}-{{newVersionMajor}}.x", "branchPrefix": "renovate/", "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -490,6 +491,7 @@ Object { "branchName": "{{branchPrefix}}{{depNameSanitized}}-{{newVersionMajor}}.x", "branchPrefix": "renovate/", "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -976,6 +978,7 @@ Object { "branchName": "{{branchPrefix}}{{depNameSanitized}}-{{newVersionMajor}}.x", "branchPrefix": "renovate/", "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -1670,6 +1673,7 @@ Object { "branchName": "{{branchPrefix}}{{depNameSanitized}}-{{newVersionMajor}}.x", "branchPrefix": "renovate/", "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -2153,6 +2157,7 @@ Object { "branchName": "{{branchPrefix}}{{depNameSanitized}}-{{newVersionMajor}}.x", "branchPrefix": "renovate/", "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -2628,6 +2633,7 @@ Object { "branchName": "{{branchPrefix}}{{depNameSanitized}}-{{newVersionMajor}}.x", "branchPrefix": "renovate/", "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -3103,6 +3109,7 @@ Object { "branchName": "{{branchPrefix}}{{depNameSanitized}}-{{newVersionMajor}}.x", "branchPrefix": "renovate/", "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, diff --git a/test/workers/branch/lock-files.spec.js b/test/workers/branch/lock-files.spec.js index c48a453c7bc865dcc7bc46ae5d5470095b050f9e..2dba3d0d95d961c86df1f40817c1dcfda6591715 100644 --- a/test/workers/branch/lock-files.spec.js +++ b/test/workers/branch/lock-files.spec.js @@ -1,6 +1,7 @@ const fs = require('fs-extra'); const lockFiles = require('../../../lib/workers/branch/lock-files'); const defaultConfig = require('../../../lib/config/defaults').getConfig(); +const upath = require('upath'); const npm = require('../../../lib/workers/branch/npm'); const yarn = require('../../../lib/workers/branch/yarn'); @@ -208,15 +209,71 @@ describe('workers/branch/lock-files', () => { expect(fs.outputFile.mock.calls).toHaveLength(6); expect(fs.remove.mock.calls).toHaveLength(4); }); - it('writes lock files', async () => { + it('writes package.json of local lib', async () => { + const renoPath = upath.join(__dirname, '../../../'); + config.copyLocalLibs = true; + config.tmpDir = { path: renoPath }; config.packageFiles = [ { - packageFile: 'package.json', - content: { name: 'package 1' }, + packageFile: 'client/package.json', + content: { + name: 'package 1', + dependencies: { + test: 'file:../test.tgz', + testFolder: 'file:../test', + }, + }, + yarnLock: 'some yarn lock', + packageLock: 'some package lock', + }, + ]; + platform.getFile.mockReturnValue('some lock file contents'); + await writeExistingFiles(config); + expect(fs.outputFile.mock.calls).toHaveLength(4); + expect(fs.remove.mock.calls).toHaveLength(0); + }); + it('Try to write package.json of local lib, but file not found', async () => { + const renoPath = upath.join(__dirname, '../../../'); + config.copyLocalLibs = true; + config.tmpDir = { path: renoPath }; + config.packageFiles = [ + { + packageFile: 'client/package.json', + content: { + name: 'package 1', + dependencies: { + test: 'file:../test.tgz', + testFolder: 'file:../test', + }, + }, + yarnLock: 'some yarn lock', + packageLock: 'some package lock', + }, + ]; + platform.getFile.mockReturnValue(null); + await writeExistingFiles(config); + expect(fs.outputFile.mock.calls).toHaveLength(3); + expect(fs.remove.mock.calls).toHaveLength(0); + }); + it('detect malicious intent (error config in package.json) local lib is not in the repo', async () => { + const renoPath = upath.join(__dirname, '../../../'); + config.copyLocalLibs = true; + config.tmpDir = { path: renoPath }; + config.packageFiles = [ + { + packageFile: 'client/package.json', + content: { + name: 'package 1', + dependencies: { + test: 'file:../test.tgz', + testFolder: 'file:../../../../test', + }, + }, yarnLock: 'some yarn lock', packageLock: 'some package lock', }, ]; + platform.getFile.mockReturnValue(null); await writeExistingFiles(config); expect(fs.outputFile.mock.calls).toHaveLength(3); expect(fs.remove.mock.calls).toHaveLength(0); diff --git a/test/workers/package/__snapshots__/index.spec.js.snap b/test/workers/package/__snapshots__/index.spec.js.snap index f4505c9f4b0a8ea32e9d509be51908685f7facad..af1a7d707d5f4cae22069d1d99f6edc420b01f79 100644 --- a/test/workers/package/__snapshots__/index.spec.js.snap +++ b/test/workers/package/__snapshots__/index.spec.js.snap @@ -8,6 +8,7 @@ Object { "branchName": "{{branchPrefix}}{{depNameSanitized}}-{{newVersionMajor}}.x", "branchPrefix": "renovate/", "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "currentVersion": "1.0.0", "depName": "foo", "description": Array [], diff --git a/test/workers/repository/updates/__snapshots__/branchify.spec.js.snap b/test/workers/repository/updates/__snapshots__/branchify.spec.js.snap index e71bdcaa3bda5d09b5949c744cf52a354315a868..078787f322863ff15e0300e019af6d5b80be5477 100644 --- a/test/workers/repository/updates/__snapshots__/branchify.spec.js.snap +++ b/test/workers/repository/updates/__snapshots__/branchify.spec.js.snap @@ -55,6 +55,7 @@ Object { }, ], "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -567,6 +568,7 @@ Object { }, ], "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -1085,6 +1087,7 @@ Object { }, ], "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -1587,6 +1590,7 @@ Object { }, ], "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", }, @@ -2092,6 +2096,7 @@ Object { }, ], "commitMessage": "Update dependency {{depName}} to v{{newVersion}}", + "copyLocalLibs": false, "dependencies": Object { "semanticCommitType": "fix", },