From 517de6f545c2095df20ec2fbe378f9a829da56f1 Mon Sep 17 00:00:00 2001 From: Rhys Arkins <rhys@arkins.net> Date: Thu, 5 Jul 2018 11:33:50 +0200 Subject: [PATCH] feat: repositoryUrls (#2221) Adds config option repositoryUrls which can be used by pip to define an alternate host to pypi. Closes #2181 --- lib/config/definitions.js | 10 +++++++ lib/datasource/pypi.js | 11 ++++++-- lib/manager/pip_requirements/extract.js | 14 +++++++++- .../__snapshots__/pypi.spec.js.snap | 11 ++++++++ test/datasource/pypi.spec.js | 13 ++++++++++ .../__snapshots__/extract.spec.js.snap | 9 +++++++ website/docs/configuration-options.md | 4 +++ website/docs/python.md | 26 +++++++++++++++++++ 8 files changed, 95 insertions(+), 3 deletions(-) diff --git a/lib/config/definitions.js b/lib/config/definitions.js index bb844f547f..c45e3f149a 100644 --- a/lib/config/definitions.js +++ b/lib/config/definitions.js @@ -320,6 +320,16 @@ const options = [ mergeable: true, cli: false, }, + { + name: 'registryUrls', + description: + 'List of URLs to try for dependency lookup. Package manager-specific', + type: 'list', + default: null, + stage: 'package', + cli: false, + env: false, + }, // depType { name: 'ignoreDeps', diff --git a/lib/datasource/pypi.js b/lib/datasource/pypi.js index 69da412037..9bdda93008 100644 --- a/lib/datasource/pypi.js +++ b/lib/datasource/pypi.js @@ -1,15 +1,22 @@ const got = require('got'); const { isVersion, sortVersions } = require('../versioning')('pep440'); +const url = require('url'); +const is = require('@sindresorhus/is'); module.exports = { getDependency, }; -async function getDependency(purl) { +async function getDependency(purl, config = {}) { const { fullname: depName } = purl; + let hostUrl = 'https://pypi.org/pypi/'; + if (!is.empty(config.registryUrls)) { + [hostUrl] = config.registryUrls; + } + const lookupUrl = url.resolve(hostUrl, `${depName}/json`); try { const dependency = {}; - const rep = await got(`https://pypi.org/pypi/${depName}/json`, { + const rep = await got(lookupUrl, { json: true, }); const dep = rep && rep.body; diff --git a/lib/manager/pip_requirements/extract.js b/lib/manager/pip_requirements/extract.js index b7283f4529..cd3e7a3367 100644 --- a/lib/manager/pip_requirements/extract.js +++ b/lib/manager/pip_requirements/extract.js @@ -16,6 +16,14 @@ module.exports = { function extractDependencies(content) { logger.debug('pip_requirements.extractDependencies()'); + let registryUrls; + content.split('\n').forEach(line => { + if (line.startsWith('--index-url ')) { + const registryUrl = line.substring('--index-url '.length); + registryUrls = [registryUrl]; + } + }); + const regex = new RegExp(`^(${packagePattern})(${specifierPattern})$`, 'g'); const deps = content .split('\n') @@ -26,13 +34,17 @@ function extractDependencies(content) { return null; } const [, depName, currentValue] = matches; - return { + const dep = { depName, currentValue, lineNumber, purl: 'pkg:pypi/' + depName, versionScheme: 'pep440', }; + if (registryUrls) { + dep.registryUrls = registryUrls; + } + return dep; }) .filter(Boolean); if (!deps.length) { diff --git a/test/datasource/__snapshots__/pypi.spec.js.snap b/test/datasource/__snapshots__/pypi.spec.js.snap index 32eece8d04..1ef2c12495 100644 --- a/test/datasource/__snapshots__/pypi.spec.js.snap +++ b/test/datasource/__snapshots__/pypi.spec.js.snap @@ -102,3 +102,14 @@ Object { "releases": Array [], } `; + +exports[`datasource/pypi getDependency supports custom datasource url 1`] = ` +Array [ + Array [ + "https://custom.pypi.net/azure-cli-monitor/json", + Object { + "json": true, + }, + ], +] +`; diff --git a/test/datasource/pypi.spec.js b/test/datasource/pypi.spec.js index e8bc3da2d5..bc2d62c51c 100644 --- a/test/datasource/pypi.spec.js +++ b/test/datasource/pypi.spec.js @@ -8,6 +8,9 @@ const res1 = fs.readFileSync('test/_fixtures/pypi/azure-cli-monitor.json'); describe('datasource/pypi', () => { describe('getDependency', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); it('returns null for empty result', async () => { got.mockReturnValueOnce({}); expect(await datasource.getDependency('pkg:pypi/something')).toBeNull(); @@ -26,6 +29,16 @@ describe('datasource/pypi', () => { await datasource.getDependency('pkg:pypi/azure-cli-monitor') ).toMatchSnapshot(); }); + it('supports custom datasource url', async () => { + got.mockReturnValueOnce({ + body: JSON.parse(res1), + }); + const config = { + registryUrls: ['https://custom.pypi.net/foo'], + }; + await datasource.getDependency('pkg:pypi/azure-cli-monitor', config); + expect(got.mock.calls).toMatchSnapshot(); + }); it('returns non-github home_page', async () => { got.mockReturnValueOnce({ body: { diff --git a/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap b/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap index 7680614897..c366526d88 100644 --- a/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap +++ b/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap @@ -7,6 +7,9 @@ Array [ "depName": "some-package", "lineNumber": 2, "purl": "pkg:pypi/some-package", + "registryUrls": Array [ + "http://example.com/private-pypi/", + ], "versionScheme": "pep440", }, Object { @@ -14,6 +17,9 @@ Array [ "depName": "some-other-package", "lineNumber": 3, "purl": "pkg:pypi/some-other-package", + "registryUrls": Array [ + "http://example.com/private-pypi/", + ], "versionScheme": "pep440", }, Object { @@ -21,6 +27,9 @@ Array [ "depName": "not_semver", "lineNumber": 4, "purl": "pkg:pypi/not_semver", + "registryUrls": Array [ + "http://example.com/private-pypi/", + ], "versionScheme": "pep440", }, ] diff --git a/website/docs/configuration-options.md b/website/docs/configuration-options.md index 87ad02bd5e..30bd58f7c4 100644 --- a/website/docs/configuration-options.md +++ b/website/docs/configuration-options.md @@ -553,6 +553,10 @@ By default, Renovate will detect if it has proposed an update to a project befor Typically you shouldn't need to modify this setting. +## registryUrls + +This is only necessary in case you need to manually configure a registry URL to use for datasource lookups. Applies to PyPI (pip) only for now. Supports only one URL for now but is defined as a list for forwards compatibility. + ## renovateFork By default, Renovate will skip over any repositories that are forked, even if they contain a `renovate.json`, because that config may have been from the source repository. To enable Renovate on forked repositories, you need to add `renovateFork: true` to your renovate config. diff --git a/website/docs/python.md b/website/docs/python.md index c05a9033b1..753f3ad7bc 100644 --- a/website/docs/python.md +++ b/website/docs/python.md @@ -28,6 +28,32 @@ The default file matching regex for requirements.txt aims to pick up the most po } ``` +## Alternate registries + +Renovate will default to performing all lookups on pypi.org, but it also supports alternative index URLs. There are two ways to achieve this: + +#### index-url in `requirements.txt` + +The index URL can be specified in the first line of the file, For example: + +``` +--index-url http://example.com/private-pypi/ +some-package==0.3.1 +some-other-package==1.0.0 +``` + +#### Specify URL in configuration + +The configuration option `registryUrls` can be used to configure an alternate index URL. Example: + +```json + "python": { + "registryUrls": ["http://example.com/private-pypi/"] + } +``` + +Note: an index-url found in the `requirements.txt` will take precedent over a registryUrl configured like the above. To override the URL found in `requirements.txt`, you need to configure it in `packageRules`, as they are applied _after_ package file extraction. + ## Disabling Python Support The most direct way to disable all Python support in Renovate is like this: -- GitLab