From c356bb0349096e1efd772e23969e513c539a8b7b Mon Sep 17 00:00:00 2001 From: Rhys Arkins <rhys@arkins.net> Date: Mon, 30 Apr 2018 13:18:51 +0200 Subject: [PATCH] feat: custom filenames for package files (#1894) Renovate now comes with a variety of package managers supported, each with their own filename pattern(s). These patterns are now exposed for user configuration through the new `fileMatch` list/array configuration option, which has been added to each manager (npm, bazel, docker-compose, etc). `fileMatch` is defined as a mergeable list, meaning that users can add to the default pattern to extend the files being detected. Closes #799 --- docs/adding-a-package-manager.md | 16 +++--- lib/config/definitions.js | 41 +++++++++++++--- lib/config/validation.js | 18 +++++++ lib/manager/bazel/index.js | 2 - lib/manager/buildkite/index.js | 3 -- lib/manager/circleci/index.js | 2 - lib/manager/docker-compose/index.js | 2 - lib/manager/docker/index.js | 2 - lib/manager/index.js | 20 +++++--- lib/manager/meteor/index.js | 2 - lib/manager/npm/index.js | 3 -- lib/manager/nvm/index.js | 2 - lib/manager/pip_requirements/index.js | 2 - lib/manager/travis/index.js | 2 - test/config/__snapshots__/index.spec.js.snap | 49 ++++++++++++++++--- .../__snapshots__/validation.spec.js.snap | 13 +++++ test/config/validation.spec.js | 16 ++++++ .../__snapshots__/resolve.spec.js.snap | 9 ++++ test/manager/index.spec.js | 38 +++++++++----- test/manager/resolve.spec.js | 2 +- .../2017-10-05-configuration-options.md | 9 ++++ 21 files changed, 189 insertions(+), 64 deletions(-) diff --git a/docs/adding-a-package-manager.md b/docs/adding-a-package-manager.md index 41048820af..727b8b5ac8 100644 --- a/docs/adding-a-package-manager.md +++ b/docs/adding-a-package-manager.md @@ -22,7 +22,6 @@ The manager's `index.js` file then needs to export up to 7 functions or values: module.exports = { contentPattern, extractDependencies, - filePattern, getPackageUpdates, language, resolvePackageFile, @@ -34,15 +33,9 @@ module.exports = { This is used when more than one package manager share settings from a common language. e.g. docker-compose, circleci and gitlabci all specify "docker" as their language and inherit all config settings from there. -##### filePattern - -`filePattern` is a javascript `RegExp` used to detect the manager's files within the repository. - -An example `filePattern` from Docker Compose is `(^|/)docker-compose[^/]*\\.ya?ml$`. You can see that it's designed to match files both in the root as well as in subdirectories, and to be flexible with matching yaml files that start with `docker-compose` but may have additional characters in the filename. - ##### contentPattern (optional) -`contentPattern` is only necessary if there's the possibility that some of the files matched by `filePattern` may not belong to that package manager, or maybe don't have any dependencies. +`contentPattern` is only necessary if there's the possibility that some of the files matched by `fileMatch` may not belong to that package manager, or maybe don't have any dependencies. An example `contentPattern` is from Meteor.js: `(^|\\n)\\s*Npm.depends\\(\\s*{`. Because Meteor's `package.js` is not particularly "unique", it's quite possible that repositories will have one or more `package.js` files that have nothing to do with Meteor.js, so we filter out only the ones that include `Npm.depends` in it. @@ -74,3 +67,10 @@ Therefore, there is the possibility that for some future package managers we may ##### updateDependency This function is the final one called for a manager. It's purpose is to patch the package file with the new version and return an updated file. + +##### fileMatch + +`fileMatch` is a javascript `RegExp` string or an exact filename string used to detect the manager's files within the repository. +It is located within `lib/config/definitions.js` so that it can be configured by the user. + +An example `fileMatch` from Docker Compose is `(^|/)docker-compose[^/]*\\.ya?ml$`. You can see that it's designed to match files both in the root as well as in subdirectories, and to be flexible with matching yaml files that start with `docker-compose` but may have additional characters in the filename. diff --git a/lib/config/definitions.js b/lib/config/definitions.js index bcb6bf3100..c557a25735 100644 --- a/lib/config/definitions.js +++ b/lib/config/definitions.js @@ -793,12 +793,23 @@ const options = [ 'Requested reviewers for Pull Requests (username in GitHub/GitLab, email or username in VSTS)', type: 'list', }, + { + name: 'fileMatch', + description: 'JS RegExp pattern for matching manager files', + type: 'list', + allowString: true, + mergeable: true, + cli: false, + env: false, + }, { name: 'npm', description: 'Configuration object for npm package.json renovation', stage: 'repository', type: 'json', - default: {}, + default: { + fileMatch: ['(^|/)package.json$'], + }, mergeable: true, }, { @@ -806,7 +817,9 @@ const options = [ description: 'Configuration object for meteor package.js renovation', stage: 'repository', type: 'json', - default: {}, + default: { + fileMatch: ['(^|/)package.js$'], + }, mergeable: true, }, { @@ -814,7 +827,9 @@ const options = [ description: 'Configuration object for bazel WORKSPACE renovation', stage: 'repository', type: 'json', - default: {}, + default: { + fileMatch: ['(^|/)WORKSPACE$'], + }, mergeable: true, }, { @@ -824,6 +839,7 @@ const options = [ type: 'json', default: { enabled: false, + fileMatch: ['\\.buildkite/.+\\.yml$'], commitMessageTopic: 'buildkite plugin {{depName}}', commitMessageExtra: 'to {{newVersion}}', managerBranchPrefix: 'buildkite-', @@ -858,7 +874,10 @@ const options = [ description: 'Configuration object for .travis.yml node version renovation', stage: 'repository', type: 'json', - default: { enabled: false }, + default: { + enabled: false, + fileMatch: ['^.travis.yml$'], + }, mergeable: true, cli: false, }, @@ -867,7 +886,9 @@ const options = [ description: 'Configuration object for .nvmrc files', state: 'repository', type: 'json', - default: {}, + default: { + fileMatch: ['^.nvmrc$'], + }, mergeable: true, cli: false, }, @@ -877,6 +898,7 @@ const options = [ stage: 'repository', type: 'json', default: { + fileMatch: ['(^|/)Dockerfile$'], managerBranchPrefix: 'docker-', commitMessageTopic: '{{{depName}}} Docker tag', prBody: template('prBody', 'docker'), @@ -915,7 +937,9 @@ const options = [ 'Configuration object for Docker Compose renovation. Also inherits settings from `docker` object.', stage: 'repository', type: 'json', - default: {}, + default: { + fileMatch: ['(^|/)docker-compose[^/]*\\.ya?ml$'], + }, mergeable: true, cli: false, }, @@ -925,7 +949,9 @@ const options = [ 'Configuration object for CircleCI yml renovation. Also inherits settings from `docker` object.', stage: 'repository', type: 'json', - default: {}, + default: { + fileMatch: ['^.circleci/config.yml$'], + }, mergeable: true, cli: false, }, @@ -936,6 +962,7 @@ const options = [ type: 'json', default: { enabled: false, + fileMatch: ['(^|\\/)([\\w-]*)requirements.(txt|pip)$'], }, mergeable: true, cli: false, diff --git a/lib/config/validation.js b/lib/config/validation.js index 3e71df5ae7..3eb263ad1c 100644 --- a/lib/config/validation.js +++ b/lib/config/validation.js @@ -208,6 +208,24 @@ async function validateConfig(config, isPreset, parentPath) { }); } } + if (key === 'fileMatch') { + try { + for (const fileMatch of val) { + RegExp(fileMatch); + if (!safe(fileMatch)) { + errors.push({ + depName: 'Configuration Error', + message: `Unsafe regExp for ${currentPath}: \`${fileMatch}\``, + }); + } + } + } catch (e) { + errors.push({ + depName: 'Configuration Error', + message: `Invalid regExp for ${currentPath}: \`${val}\``, + }); + } + } if ( (selectors.includes(key) || key === 'matchCurrentVersion') && !(parentPath && parentPath.match(/p.*Rules\[\d+\]$/)) && // Inside a packageRule diff --git a/lib/manager/bazel/index.js b/lib/manager/bazel/index.js index 9dff093317..cf2a5e0eed 100644 --- a/lib/manager/bazel/index.js +++ b/lib/manager/bazel/index.js @@ -2,13 +2,11 @@ const { extractDependencies } = require('./extract'); const { getPackageUpdates } = require('./package'); const { updateDependency } = require('./update'); -const filePattern = new RegExp('(^|/)WORKSPACE$'); const contentPattern = new RegExp('(^|\\n)git_repository\\('); module.exports = { contentPattern, extractDependencies, - filePattern, getPackageUpdates, updateDependency, }; diff --git a/lib/manager/buildkite/index.js b/lib/manager/buildkite/index.js index f5a02ded7a..0e96876493 100644 --- a/lib/manager/buildkite/index.js +++ b/lib/manager/buildkite/index.js @@ -2,11 +2,8 @@ const { extractDependencies } = require('./extract'); const { getPackageUpdates } = require('./package'); const { updateDependency } = require('./update'); -const filePattern = new RegExp('\\.buildkite/.+\\.yml$'); - module.exports = { extractDependencies, - filePattern, getPackageUpdates, updateDependency, }; diff --git a/lib/manager/circleci/index.js b/lib/manager/circleci/index.js index 2b674babc7..c70b4a9305 100644 --- a/lib/manager/circleci/index.js +++ b/lib/manager/circleci/index.js @@ -2,14 +2,12 @@ const { extractDependencies } = require('./extract'); const { getPackageUpdates } = require('../docker/package'); const { updateDependency } = require('./update'); -const filePattern = new RegExp('^.circleci/config.yml$'); const contentPattern = new RegExp('(^|\\n)\\s*- image: '); const language = 'docker'; module.exports = { contentPattern, extractDependencies, - filePattern, getPackageUpdates, language, updateDependency, diff --git a/lib/manager/docker-compose/index.js b/lib/manager/docker-compose/index.js index 0618fe466e..32df37546d 100644 --- a/lib/manager/docker-compose/index.js +++ b/lib/manager/docker-compose/index.js @@ -3,14 +3,12 @@ const { getPackageUpdates } = require('../docker/package'); const { resolvePackageFile } = require('./resolve'); const { updateDependency } = require('./update'); -const filePattern = new RegExp('(^|/)docker-compose[^/]*\\.ya?ml$'); const contentPattern = new RegExp('(^|\\n)\\s*image:'); const language = 'docker'; module.exports = { contentPattern, extractDependencies, - filePattern, getPackageUpdates, language, resolvePackageFile, diff --git a/lib/manager/docker/index.js b/lib/manager/docker/index.js index 2e4b1e3258..eb6283fdc1 100644 --- a/lib/manager/docker/index.js +++ b/lib/manager/docker/index.js @@ -3,13 +3,11 @@ const { getPackageUpdates } = require('./package'); const { resolvePackageFile } = require('./resolve'); const { updateDependency } = require('./update'); -const filePattern = new RegExp('(^|/)Dockerfile$'); const contentPattern = new RegExp('(^|\\n)FROM .+\\n', 'i'); module.exports = { contentPattern, extractDependencies, - filePattern, getPackageUpdates, resolvePackageFile, updateDependency, diff --git a/lib/manager/index.js b/lib/manager/index.js index 295aa70b19..4f510c1d7a 100644 --- a/lib/manager/index.js +++ b/lib/manager/index.js @@ -65,9 +65,13 @@ async function detectPackageFiles(config) { continue; // eslint-disable-line no-continue } const files = []; - const { filePattern } = managers[manager]; - logger.debug(`Using ${manager} file pattern: ${filePattern.toString()}`); - const allfiles = fileList.filter(file => file.match(filePattern)); + let allfiles = []; + for (const fileMatch of config[manager].fileMatch) { + logger.debug(`Using ${manager} file match: ${fileMatch}`); + allfiles = allfiles.concat( + fileList.filter(file => file.match(new RegExp(fileMatch))) + ); + } logger.debug(`Found ${allfiles.length} files`); for (const file of allfiles) { const { contentPattern } = managers[manager]; @@ -154,10 +158,12 @@ async function getUpdatedPackageFiles(config) { }; } -function getManager(filename) { +function getManager(config, filename) { for (const manager of managerList) { - if (filename.match(managers[manager].filePattern)) { - return manager; + for (const fileMatch of config[manager].fileMatch) { + if (filename.match(new RegExp(fileMatch))) { + return manager; + } } } return null; @@ -180,7 +186,7 @@ async function resolvePackageFiles(config) { async function resolvePackageFile(p) { let packageFile = typeof p === 'string' ? { packageFile: p } : p; const fileName = packageFile.packageFile.split('/').pop(); - packageFile.manager = packageFile.manager || getManager(fileName); + packageFile.manager = packageFile.manager || getManager(config, fileName); const { manager } = packageFile; if (!manager) { // Config error diff --git a/lib/manager/meteor/index.js b/lib/manager/meteor/index.js index 8b13499f40..6c097c1df0 100644 --- a/lib/manager/meteor/index.js +++ b/lib/manager/meteor/index.js @@ -2,13 +2,11 @@ const { extractDependencies } = require('./extract'); const { getPackageUpdates } = require('../npm/package'); const { updateDependency } = require('./update'); -const filePattern = new RegExp('(^|/)package.js$'); const contentPattern = new RegExp('(^|\\n)\\s*Npm.depends\\(\\s*{'); module.exports = { contentPattern, extractDependencies, - filePattern, getPackageUpdates, updateDependency, }; diff --git a/lib/manager/npm/index.js b/lib/manager/npm/index.js index 07c5fdb0ba..fd6cd40406 100644 --- a/lib/manager/npm/index.js +++ b/lib/manager/npm/index.js @@ -3,11 +3,8 @@ const { getPackageUpdates } = require('./package'); const { resolvePackageFile } = require('./resolve'); const { updateDependency } = require('./update'); -const filePattern = new RegExp('(^|/)package.json$'); - module.exports = { extractDependencies, - filePattern, getPackageUpdates, resolvePackageFile, updateDependency, diff --git a/lib/manager/nvm/index.js b/lib/manager/nvm/index.js index abcd005bc5..0d35d7ba9f 100644 --- a/lib/manager/nvm/index.js +++ b/lib/manager/nvm/index.js @@ -2,12 +2,10 @@ const { extractDependencies } = require('./extract'); const { getPackageUpdates } = require('./package'); const { updateDependency } = require('./update'); -const filePattern = new RegExp('^.nvmrc$'); const language = 'node'; module.exports = { extractDependencies, - filePattern, getPackageUpdates, language, updateDependency, diff --git a/lib/manager/pip_requirements/index.js b/lib/manager/pip_requirements/index.js index 96ffb634fa..9d4ee6fddc 100644 --- a/lib/manager/pip_requirements/index.js +++ b/lib/manager/pip_requirements/index.js @@ -2,14 +2,12 @@ const { packagePattern, extractDependencies } = require('./extract'); const { getPackageUpdates } = require('./package'); const { updateDependency } = require('./update'); -const filePattern = /(^|\/)([\w-]*)requirements.(txt|pip)$/; const contentPattern = new RegExp(`^${packagePattern}==`); const language = 'python'; module.exports = { contentPattern, extractDependencies, - filePattern, getPackageUpdates, language, updateDependency, diff --git a/lib/manager/travis/index.js b/lib/manager/travis/index.js index 4752f04cc8..de6acfbee4 100644 --- a/lib/manager/travis/index.js +++ b/lib/manager/travis/index.js @@ -2,14 +2,12 @@ const { extractDependencies } = require('./extract'); const { getPackageUpdates } = require('./package'); const { updateDependency } = require('./update'); -const filePattern = new RegExp('^.travis.yml$'); const contentPattern = new RegExp('(^|\\n)node_js:\\n'); const language = 'node'; module.exports = { contentPattern, extractDependencies, - filePattern, getPackageUpdates, language, updateDependency, diff --git a/test/config/__snapshots__/index.spec.js.snap b/test/config/__snapshots__/index.spec.js.snap index 8243dc5251..feff51b331 100644 --- a/test/config/__snapshots__/index.spec.js.snap +++ b/test/config/__snapshots__/index.spec.js.snap @@ -15,7 +15,11 @@ Object { "automerge": false, "automergeType": "pr", "baseBranches": Array [], - "bazel": Object {}, + "bazel": Object { + "fileMatch": Array [ + "(^|/)WORKSPACE$", + ], + }, "branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}", "branchPrefix": "renovate/", "branchTopic": "{{{depNameSanitized}}}-{{{newVersionMajor}}}.x", @@ -23,11 +27,18 @@ Object { "commitMessageExtra": "to {{newVersion}}", "commitMessageTopic": "buildkite plugin {{depName}}", "enabled": false, + "fileMatch": Array [ + "\\\\.buildkite/.+\\\\.yml$", + ], "managerBranchPrefix": "buildkite-", "prBody": "This Pull Request updates buildkite plugin {{#if repositoryUrl}}[{{{depName}}}]({{{repositoryUrl}}}){{else}}\`{{{depName}}}\`{{/if}} from \`{{{currentVersion}}}\` to \`{{{newVersion}}}\`.\\n\\n{{#if releases.length}}\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#if isPin}}\\n**Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.\\n{{/if}}\\n{{#if hasReleaseNotes}}\\n\\n<details>\\n<summary>Release Notes</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.releaseNotes}}\\n### [\`v{{{release.version}}}\`]({{{release.releaseNotes.url}}})\\n\\n{{{release.releaseNotes.body}}}\\n\\n---\\n\\n{{/if}}\\n{{/each}}\\n</details>\\n{{/if}}\\n\\n{{#if hasCommits}}\\n\\n<details>\\n<summary>Commits</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.hasCommits}}\\n#### v{{{release.version}}}\\n{{#each release.commits as |commit|}}\\n- [\`{{commit.shortSha}}\`]({{commit.url}}) {{commit.message}}\\n{{/each}}\\n{{/if}}\\n{{/each}}\\n\\n</details>\\n{{/if}}\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n- \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n- \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}", }, "bumpVersion": null, - "circleci": Object {}, + "circleci": Object { + "fileMatch": Array [ + "^.circleci/config.yml$", + ], + }, "commitBody": null, "commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}", "commitMessageAction": "Update", @@ -53,6 +64,9 @@ Object { }, "prBody": "This Pull Request updates Docker base image \`{{{depName}}}:{{{currentTag}}}\` to the latest digest (\`{{{newDigest}}}\`). For details on Renovate's Docker support, please visit https://renovatebot.com/docs/language-support/docker\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n- \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n- \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}", }, + "fileMatch": Array [ + "(^|/)Dockerfile$", + ], "group": Object { "commitMessageTopic": "{{{groupName}}} Docker tags", "prBody": "This Pull Request updates Dockerfiles to use image digests.\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#each upgrades as |upgrade|}}\\n- {{#if repositoryUrl}}[{{upgrade.depName}}]({{upgrade.repositoryUrl}}){{else}}\`{{{depName}}}\`{{/if}}: \`{{upgrade.newDigest}}\`\\n{{/each}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n- \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n- \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}", @@ -73,7 +87,11 @@ Object { }, "prBody": "This Pull Request updates Docker base image \`{{{depName}}}\` from tag \`{{{currentTag}}}\` to new tag \`{{{newTag}}}\`. For details on Renovate's Docker support, please visit https://renovatebot.com/docs/language-support/docker\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n- \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n- \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}", }, - "docker-compose": Object {}, + "docker-compose": Object { + "fileMatch": Array [ + "(^|/)docker-compose[^/]*\\\\.ya?ml$", + ], + }, "enabled": true, "enabledManagers": Array [], "encrypted": null, @@ -82,6 +100,7 @@ Object { "excludePackageNames": Array [], "excludePackagePatterns": Array [], "exposeEnv": false, + "fileMatch": Array [], "forkMode": false, "gitAuthor": null, "gitPrivateKey": null, @@ -119,7 +138,11 @@ Object { "major": Object {}, "managerBranchPrefix": "", "matchCurrentVersion": null, - "meteor": Object {}, + "meteor": Object { + "fileMatch": Array [ + "(^|/)package.js$", + ], + }, "minor": Object {}, "mirrorMode": false, "multipleMajorPrs": false, @@ -130,10 +153,18 @@ Object { "groupName": "Node.js", "lazyGrouping": false, }, - "npm": Object {}, + "npm": Object { + "fileMatch": Array [ + "(^|/)package.json$", + ], + }, "npmToken": null, "npmrc": null, - "nvm": Object {}, + "nvm": Object { + "fileMatch": Array [ + "^.nvmrc$", + ], + }, "onboarding": true, "onboardingConfig": Object {}, "packageFiles": Array [], @@ -159,6 +190,9 @@ Object { "pinVersions": false, "pip_requirements": Object { "enabled": false, + "fileMatch": Array [ + "(^|\\\\/)([\\\\w-]*)requirements.(txt|pip)$", + ], }, "platform": "github", "prBody": "This Pull Request {{#if isRollback}}rolls back{{else}}updates{{/if}} dependency {{#if repositoryUrl}}[{{{depName}}}]({{{repositoryUrl}}}){{else}}\`{{{depName}}}\`{{/if}} from \`{{#unless isRange}}{{#unless isPin}}v{{/unless}}{{/unless}}{{{currentVersion}}}\` to \`{{#unless isRange}}v{{/unless}}{{{newVersion}}}\`{{#if isRollback}}. This is necessary and important because \`v{{{currentVersion}}}\` cannot be found in the npm registry - probably because of it being unpublished.{{/if}}\\n{{#if hasTypes}}\\n\\nThis PR also includes an upgrade to the corresponding [@types/{{{depName}}}](https://npmjs.com/package/@types/{{{depName}}}) package.\\n{{/if}}\\n{{#if releases.length}}\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#if isPin}}\\n**Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.\\n{{/if}}\\n{{#if hasReleaseNotes}}\\n\\n<details>\\n<summary>Release Notes</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.releaseNotes}}\\n### [\`v{{{release.version}}}\`]({{{release.releaseNotes.url}}})\\n\\n{{{release.releaseNotes.body}}}\\n\\n---\\n\\n{{/if}}\\n{{/each}}\\n</details>\\n{{/if}}\\n\\n{{#if hasCommits}}\\n\\n<details>\\n<summary>Commits</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.hasCommits}}\\n#### v{{{release.version}}}\\n{{#each release.commits as |commit|}}\\n- [\`{{commit.shortSha}}\`]({{commit.url}}) {{commit.message}}\\n{{/each}}\\n{{/if}}\\n{{/each}}\\n\\n</details>\\n{{/if}}\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n- \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n- \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}", @@ -189,6 +223,9 @@ Object { "token": null, "travis": Object { "enabled": false, + "fileMatch": Array [ + "^.travis.yml$", + ], }, "unpublishSafe": false, "unstablePattern": null, diff --git a/test/config/__snapshots__/validation.spec.js.snap b/test/config/__snapshots__/validation.spec.js.snap index 232ac12e3e..dfdb6d1ecb 100644 --- a/test/config/__snapshots__/validation.spec.js.snap +++ b/test/config/__snapshots__/validation.spec.js.snap @@ -57,6 +57,19 @@ Array [ ] `; +exports[`config/validation validateConfig(config) errors for unsafe fileMatches 1`] = ` +Array [ + Object { + "depName": "Configuration Error", + "message": "Invalid regExp for npm.fileMatch: \`abc ([a-z]+) ([a-z]+))\`", + }, + Object { + "depName": "Configuration Error", + "message": "Unsafe regExp for docker.fileMatch: \`(x+x+)+y\`", + }, +] +`; + exports[`config/validation validateConfig(config) ignore packageRule nesting validation for presets 1`] = `Array []`; exports[`config/validation validateConfig(config) invalid matchCurrentVersion triggers an error 1`] = ` diff --git a/test/config/validation.spec.js b/test/config/validation.spec.js index d010aaf597..a7c5ead5f2 100644 --- a/test/config/validation.spec.js +++ b/test/config/validation.spec.js @@ -123,5 +123,21 @@ describe('config/validation', () => { expect(errors).toMatchSnapshot(); expect(errors).toHaveLength(1); }); + it('errors for unsafe fileMatches', async () => { + const config = { + npm: { + fileMatch: ['abc ([a-z]+) ([a-z]+))'], + }, + docker: { + fileMatch: ['(x+x+)+y'], + }, + }; + const { warnings, errors } = await configValidation.validateConfig( + config + ); + expect(warnings).toHaveLength(0); + expect(errors).toHaveLength(2); + expect(errors).toMatchSnapshot(); + }); }); }); diff --git a/test/manager/__snapshots__/resolve.spec.js.snap b/test/manager/__snapshots__/resolve.spec.js.snap index d3abfc6251..b71a0751cd 100644 --- a/test/manager/__snapshots__/resolve.spec.js.snap +++ b/test/manager/__snapshots__/resolve.spec.js.snap @@ -11,6 +11,9 @@ Array [ "version": "1.0.0", }, "currentPackageJsonVersion": "1.0.0", + "fileMatch": Array [ + "(^|/)package.json$", + ], "manager": "npm", "packageFile": "package.json", }, @@ -29,6 +32,9 @@ Array [ "version": "0.0.1", }, "currentPackageJsonVersion": "0.0.1", + "fileMatch": Array [ + "(^|/)package.json$", + ], "manager": "npm", "npmShrinkwrap": "npm-shrinkwrap.json", "npmrc": "npmrc", @@ -51,6 +57,9 @@ Array [ "version": "0.0.1", }, "currentPackageJsonVersion": "0.0.1", + "fileMatch": Array [ + "(^|/)package.json$", + ], "manager": "npm", "packageFile": "package.json", }, diff --git a/test/manager/index.spec.js b/test/manager/index.spec.js index 7c6edbe566..9eafe3964d 100644 --- a/test/manager/index.spec.js +++ b/test/manager/index.spec.js @@ -143,23 +143,35 @@ describe('manager', () => { }); describe('getManager', () => { it('rejects unknown files', () => { - expect(manager.getManager('WORKSPACER')).toBe(null); + expect(manager.getManager(defaultConfig, 'WORKSPACER')).toBe(null); }); it('detects files in root', () => { - expect(manager.getManager('WORKSPACE')).toBe('bazel'); - expect(manager.getManager('Dockerfile')).toBe('docker'); - expect(manager.getManager('package.js')).toBe('meteor'); - expect(manager.getManager('package.json')).toBe('npm'); - expect(manager.getManager('.nvmrc')).toBe('nvm'); - expect(manager.getManager('.travis.yml')).toBe('travis'); + expect(manager.getManager(defaultConfig, 'WORKSPACE')).toBe('bazel'); + expect(manager.getManager(defaultConfig, 'Dockerfile')).toBe('docker'); + expect(manager.getManager(defaultConfig, 'package.js')).toBe('meteor'); + expect(manager.getManager(defaultConfig, 'package.json')).toBe('npm'); + expect(manager.getManager(defaultConfig, '.nvmrc')).toBe('nvm'); + expect(manager.getManager(defaultConfig, '.travis.yml')).toBe('travis'); }); it('detects nested files', () => { - expect(manager.getManager('foo/bar/WORKSPACE')).toBe('bazel'); - expect(manager.getManager('backend/Dockerfile')).toBe('docker'); - expect(manager.getManager('package/a/package.js')).toBe('meteor'); - expect(manager.getManager('frontend/package.json')).toBe('npm'); - expect(manager.getManager('subfolder-1/.nvmrc')).toBe(null); - expect(manager.getManager('subfolder-2/.travis.yml')).toBe(null); + expect(manager.getManager(defaultConfig, 'foo/bar/WORKSPACE')).toBe( + 'bazel' + ); + expect(manager.getManager(defaultConfig, 'backend/Dockerfile')).toBe( + 'docker' + ); + expect(manager.getManager(defaultConfig, 'package/a/package.js')).toBe( + 'meteor' + ); + expect(manager.getManager(defaultConfig, 'frontend/package.json')).toBe( + 'npm' + ); + expect(manager.getManager(defaultConfig, 'subfolder-1/.nvmrc')).toBe( + null + ); + expect(manager.getManager(defaultConfig, 'subfolder-2/.travis.yml')).toBe( + null + ); }); }); describe('getUpdatedPackageFiles', () => { diff --git a/test/manager/resolve.spec.js b/test/manager/resolve.spec.js index bb8c76bed5..bbc5a53a62 100644 --- a/test/manager/resolve.spec.js +++ b/test/manager/resolve.spec.js @@ -110,7 +110,7 @@ describe('manager/resolve', () => { platform.getFile.mockReturnValueOnce('# comment\nFROM node:8\n'); // Dockerfile platform.getFile.mockReturnValueOnce('image: node:8\n'); // Docker Compose platform.getFile.mockReturnValueOnce('# travis'); // .travis.yml - platform.getFile.mockReturnValueOnce('# WORKSPACE'); // Dockerfile + platform.getFile.mockReturnValueOnce('# WORKSPACE'); // Dockerfileyarn j platform.getFile.mockReturnValueOnce('8.9\n'); // Dockerfile const res = await resolvePackageFiles(config); expect(res.packageFiles).toHaveLength(7); diff --git a/website/docs/_posts/2017-10-05-configuration-options.md b/website/docs/_posts/2017-10-05-configuration-options.md index a68a761233..f6266c103d 100644 --- a/website/docs/_posts/2017-10-05-configuration-options.md +++ b/website/docs/_posts/2017-10-05-configuration-options.md @@ -439,6 +439,15 @@ Preset configs to use/extend. See https://renovateapp.com/docs/configuration-reference/config-presets for details. +## fileMatch + +JS RegExp pattern for matching manager files. + +| name | value | +| ------- | ----- | +| type | list | +| default | [] | + ## gitAuthor | name | value | -- GitLab