From 697b80aaf0e9781671acba989fe1ea0904c22fa7 Mon Sep 17 00:00:00 2001 From: Ayoub Kaanich <kayoub5@live.com> Date: Fri, 8 Jun 2018 06:15:13 +0200 Subject: [PATCH] feat: composer exact semver support (without lockfile updates) (#1993) This PR adds the packagist datasource plus basic exact semver Composer support. Composer lockfile updating is not yet supported. --- lib/config/definitions.js | 21 ++ lib/datasource/packagist.js | 57 ++++ lib/manager/composer/extract.js | 51 ++++ lib/manager/composer/index.js | 12 + lib/manager/index.js | 1 + .../repository/process/lookup/index.js | 3 + test/_fixtures/composer/composer1.json | 74 +++++ test/_fixtures/packagist/uploader.json | 1 + .../__snapshots__/packagist.spec.js.snap | 43 +++ test/datasource/packagist.spec.js | 38 +++ .../__snapshots__/extract.spec.js.snap | 253 ++++++++++++++++++ test/manager/composer/extract.spec.js | 28 ++ .../extract/__snapshots__/index.spec.js.snap | 3 + .../lookup/__snapshots__/index.spec.js.snap | 9 + .../repository/process/lookup/index.spec.js | 10 + website/docs/configuration-options.md | 8 + 16 files changed, 612 insertions(+) create mode 100644 lib/datasource/packagist.js create mode 100644 lib/manager/composer/extract.js create mode 100644 lib/manager/composer/index.js create mode 100644 test/_fixtures/composer/composer1.json create mode 100644 test/_fixtures/packagist/uploader.json create mode 100644 test/datasource/__snapshots__/packagist.spec.js.snap create mode 100644 test/datasource/packagist.spec.js create mode 100644 test/manager/composer/__snapshots__/extract.spec.js.snap create mode 100644 test/manager/composer/extract.spec.js diff --git a/lib/config/definitions.js b/lib/config/definitions.js index 432609647d..df8d53ebe0 100644 --- a/lib/config/definitions.js +++ b/lib/config/definitions.js @@ -958,6 +958,27 @@ const options = [ mergeable: true, cli: false, }, + { + name: 'composer', + description: 'Configuration object for composer.json files', + stage: 'package', + type: 'json', + default: { + enabled: false, + fileMatch: ['(^|\\/)([\\w-]*)composer.json$'], + }, + mergeable: true, + cli: false, + }, + { + name: 'php', + description: 'Configuration object for php', + stage: 'package', + type: 'json', + default: {}, + mergeable: true, + cli: false, + }, { name: 'pip_requirements', description: 'Configuration object for requirements.txt files', diff --git a/lib/datasource/packagist.js b/lib/datasource/packagist.js new file mode 100644 index 0000000000..3f52a24062 --- /dev/null +++ b/lib/datasource/packagist.js @@ -0,0 +1,57 @@ +const URL = require('url'); +const got = require('got'); +const parse = require('github-url-from-git'); +const semver = require('semver'); + +module.exports = { + getDependency, +}; + +async function getDependency(name) { + logger.trace(`getDependency(${name})`); + + const regUrl = 'https://packagist.org'; + + const pkgUrl = URL.resolve(regUrl, `/packages/${name}.json`); + + try { + const res = (await got(pkgUrl, { + json: true, + retries: 5, + })).body.package; + + // Simplify response before caching and returning + const dep = { + name: res.name, + versions: {}, + }; + + if (res.repository) { + dep.repositoryUrl = parse(res.repository); + } + + Object.keys(res.versions) + .filter(version => semver.valid(version)) + .forEach(version => { + const release = res.versions[version]; + dep.homepage = dep.homepage || release.homepage; + dep.versions[semver.valid(version)] = { + gitHead: version, + time: release.time, + }; + }); + dep.homepage = dep.homepage || res.repository; + logger.trace({ dep }, 'dep'); + return dep; + } catch (err) { + if (err.statusCode === 404 || err.code === 'ENOTFOUND') { + logger.info({ name }, `Dependency lookup failure: not found`); + logger.debug({ + err, + }); + return null; + } + logger.warn({ err, name }, 'packagist registry failure: Unknown error'); + return null; + } +} diff --git a/lib/manager/composer/extract.js b/lib/manager/composer/extract.js new file mode 100644 index 0000000000..aa54acefa2 --- /dev/null +++ b/lib/manager/composer/extract.js @@ -0,0 +1,51 @@ +const semver = require('../../versioning')('semver'); + +module.exports = { + extractDependencies, +}; + +function extractDependencies(content, packageFile) { + logger.debug('composer.extractDependencies()'); + let packageJson; + try { + packageJson = JSON.parse(content); + } catch (err) { + logger.info({ packageFile }, 'Invalid JSON'); + return null; + } + const deps = []; + const depTypes = ['require', 'require-dev']; + for (const depType of depTypes) { + if (packageJson[depType]) { + try { + for (const [depName, version] of Object.entries(packageJson[depType])) { + const currentValue = version.trim(); + const dep = { + depType, + depName, + currentValue, + verionScheme: 'semver', + purl: 'pkg:packagist/' + depName, + }; + if (!depName.includes('/')) { + dep.skipReason = 'unsupported'; + } + if (!semver.isVersion(currentValue)) { + dep.skipReason = 'unsupported-constraint'; + } + deps.push(dep); + } + } catch (err) /* istanbul ignore next */ { + logger.info( + { packageFile, depType, err, message: err.message }, + 'Error parsing composer.json' + ); + return null; + } + } + } + if (!deps.length) { + return null; + } + return { deps }; +} diff --git a/lib/manager/composer/index.js b/lib/manager/composer/index.js new file mode 100644 index 0000000000..9fd44f840d --- /dev/null +++ b/lib/manager/composer/index.js @@ -0,0 +1,12 @@ +const { extractDependencies } = require('./extract'); +const { updateDependency } = require('../npm/update'); + +const language = 'php'; + +module.exports = { + extractDependencies, + language, + updateDependency, + // TODO: support this + // supportsLockFileMaintenance: true, +}; diff --git a/lib/manager/index.js b/lib/manager/index.js index d4b38352cd..236458e779 100644 --- a/lib/manager/index.js +++ b/lib/manager/index.js @@ -2,6 +2,7 @@ const managerList = [ 'bazel', 'buildkite', 'circleci', + 'composer', 'docker', 'docker-compose', 'meteor', diff --git a/lib/workers/repository/process/lookup/index.js b/lib/workers/repository/process/lookup/index.js index ef58b90c34..0c905b0dc3 100644 --- a/lib/workers/repository/process/lookup/index.js +++ b/lib/workers/repository/process/lookup/index.js @@ -4,6 +4,7 @@ const { getRangeStrategy } = require('../../../../manager'); const { filterVersions } = require('./filter'); const npmApi = require('../../../../datasource/npm'); const github = require('../../../../datasource/github'); +const packagist = require('../../../../datasource/packagist'); const pypi = require('../../../../datasource/pypi'); const { parse } = require('../../../../../lib/util/purl'); @@ -40,6 +41,8 @@ async function lookupUpdates(config) { dependency = await github.getDependency(purl.fullname, purl.qualifiers); } else if (purl.type === 'pypi') { dependency = await pypi.getDependency(purl.fullname); + } else if (purl.type === 'packagist') { + dependency = await packagist.getDependency(purl.fullname); } else { logger.warn({ config }, 'Unknown purl'); return []; diff --git a/test/_fixtures/composer/composer1.json b/test/_fixtures/composer/composer1.json new file mode 100644 index 0000000000..84c31421b7 --- /dev/null +++ b/test/_fixtures/composer/composer1.json @@ -0,0 +1,74 @@ +{ + "autoload": { + "psr-0": { + "": "src/" + } + }, + "require": { + "php": ">=5.3.2", + + "symfony/assetic-bundle": "dev-master", + "symfony/monolog-bundle": "dev-master", + "symfony/swiftmailer-bundle": "dev-master", + "symfony/symfony": "2.1.*", + + "doctrine/common": "2.2.2", + "doctrine/doctrine-bundle": "dev-master", + "doctrine/doctrine-fixtures-bundle": "dev-master", + "doctrine/orm": "2.2.x-dev", + + "exercise/elastica-bundle": "dev-master", + + "friendsofsymfony/rest-bundle": "dev-master", + "friendsofsymfony/user-bundle": "*", + + "fzaninotto/faker": "*", + + "jms/di-extra-bundle": "1.0.1", + "jms/payment-core-bundle": "*", + "jms/security-extra-bundle": "1.1.0", + + "knplabs/knp-menu-bundle": "dev-master", + "knplabs/knp-paginator-bundle": "dev-master", + + "liip/imagine-bundle": "dev-master", + + "merk/dough-bundle": "dev-master", + + "sensio/distribution-bundle": "dev-master", + "sensio/framework-extra-bundle": "dev-master", + "sensio/generator-bundle": "dev-master", + + "simplethings/entity-audit-bundle": "dev-master", + + "stof/doctrine-extensions-bundle": "dev-master", + + "twig/extensions": "dev-master" + }, + "require-dev": { + "behat/behat": "2.3.*", + "behat/behat-bundle": "*", + "behat/mink-bundle": "*", + "behat/sahi-client": "*", + "behat/common-contexts": "*" + }, + "scripts": { + "post-install-cmd": [ + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets" + ], + "post-update-cmd": [ + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", + "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets" + ] + }, + "config": { + "bin-dir": "bin" + }, + "extra": { + "symfony-app-dir": "app", + "symfony-web-dir": "web" + } +} diff --git a/test/_fixtures/packagist/uploader.json b/test/_fixtures/packagist/uploader.json new file mode 100644 index 0000000000..2334744c0b --- /dev/null +++ b/test/_fixtures/packagist/uploader.json @@ -0,0 +1 @@ +{"package":{"name":"cristianvuolo\/uploader","description":"Addon Uploader","time":"2016-10-19T22:49:42+00:00","maintainers":[{"name":"cristianvuolo","avatar_url":"https:\/\/www.gravatar.com\/avatar\/ed04ec5b11a6c9b660018342d6fcdc1a?d=identicon"}],"versions":{"dev-master":{"name":"cristianvuolo\/uploader","description":"Addon Uploader","keywords":[],"homepage":"","version":"dev-master","version_normalized":"9999999-dev","license":["MIT"],"authors":[],"source":{"type":"git","url":"https:\/\/github.com\/cristianvuolo\/uploader.git","reference":"fa3a834d521326794b920a6f983979d3a8c635f2"},"dist":{"type":"zip","url":"https:\/\/api.github.com\/repos\/cristianvuolo\/uploader\/zipball\/fa3a834d521326794b920a6f983979d3a8c635f2","reference":"fa3a834d521326794b920a6f983979d3a8c635f2","shasum":""},"type":"library","time":"2018-03-14T17:40:06+00:00","autoload":{"psr-4":{"CristianVuolo\\Uploader\\":"src\/"}},"require":{"php":"\u003E=5.6.4","intervention\/image":"^2.3","laravel\/framework":"5.*"}},"1.0.8":{"name":"cristianvuolo\/uploader","description":"Addon Uploader","keywords":[],"homepage":"","version":"1.0.8","version_normalized":"1.0.8.0","license":["MIT"],"authors":[],"source":{"type":"git","url":"https:\/\/github.com\/cristianvuolo\/uploader.git","reference":"fa3a834d521326794b920a6f983979d3a8c635f2"},"dist":{"type":"zip","url":"https:\/\/api.github.com\/repos\/cristianvuolo\/uploader\/zipball\/fa3a834d521326794b920a6f983979d3a8c635f2","reference":"fa3a834d521326794b920a6f983979d3a8c635f2","shasum":""},"type":"library","time":"2018-03-14T17:40:06+00:00","autoload":{"psr-4":{"CristianVuolo\\Uploader\\":"src\/"}},"require":{"php":"\u003E=5.6.4","intervention\/image":"^2.3","laravel\/framework":"5.*"}},"1.0.7":{"name":"cristianvuolo\/uploader","description":"Addon Uploader","keywords":[],"homepage":"","version":"1.0.7","version_normalized":"1.0.7.0","license":["MIT"],"authors":[],"source":{"type":"git","url":"https:\/\/github.com\/cristianvuolo\/uploader.git","reference":"d71a2e83673151f1de632ca656541c063da9b9f6"},"dist":{"type":"zip","url":"https:\/\/api.github.com\/repos\/cristianvuolo\/uploader\/zipball\/d71a2e83673151f1de632ca656541c063da9b9f6","reference":"d71a2e83673151f1de632ca656541c063da9b9f6","shasum":""},"type":"library","time":"2018-02-19T12:22:34+00:00","autoload":{"psr-4":{"CristianVuolo\\Uploader\\":"src\/"}},"require":{"php":"\u003E=5.6.4","intervention\/image":"^2.3","laravel\/framework":"5.*"}},"1.0.6":{"name":"cristianvuolo\/uploader","description":"","keywords":[],"homepage":"","version":"1.0.6","version_normalized":"1.0.6.0","license":["MIT"],"authors":[],"source":{"type":"git","url":"https:\/\/github.com\/cristianvuolo\/uploader.git","reference":"b9b4b7049976525018a45ef6b18c1575b56ddd10"},"dist":{"type":"zip","url":"https:\/\/api.github.com\/repos\/cristianvuolo\/uploader\/zipball\/b9b4b7049976525018a45ef6b18c1575b56ddd10","reference":"b9b4b7049976525018a45ef6b18c1575b56ddd10","shasum":""},"type":"library","time":"2018-01-25T18:57:17+00:00","autoload":{"psr-4":{"CristianVuolo\\Uploader\\":"src\/"}},"require":{"php":"\u003E=5.6.4","intervention\/image":"^2.3","laravel\/framework":"5.*"}},"1.0.5":{"name":"cristianvuolo\/uploader","description":"","keywords":[],"homepage":"","version":"1.0.5","version_normalized":"1.0.5.0","license":["MIT"],"authors":[],"source":{"type":"git","url":"https:\/\/github.com\/cristianvuolo\/uploader.git","reference":"5c346fc2e8bdae20522cb880b64112e2b380e064"},"dist":{"type":"zip","url":"https:\/\/api.github.com\/repos\/cristianvuolo\/uploader\/zipball\/5c346fc2e8bdae20522cb880b64112e2b380e064","reference":"5c346fc2e8bdae20522cb880b64112e2b380e064","shasum":""},"type":"library","time":"2017-08-02T13:23:33+00:00","autoload":{"psr-4":{"CristianVuolo\\Uploader\\":"src\/"}},"require":{"php":"\u003E=5.6.4","intervention\/image":"^2.3","laravel\/framework":"5.*"}},"1.0.4":{"name":"cristianvuolo\/uploader","description":"","keywords":[],"homepage":"","version":"1.0.4","version_normalized":"1.0.4.0","license":["MIT"],"authors":[],"source":{"type":"git","url":"https:\/\/github.com\/cristianvuolo\/uploader.git","reference":"7cbe0c930997743ba4d86147526358784575b79e"},"dist":{"type":"zip","url":"https:\/\/api.github.com\/repos\/cristianvuolo\/uploader\/zipball\/7cbe0c930997743ba4d86147526358784575b79e","reference":"7cbe0c930997743ba4d86147526358784575b79e","shasum":""},"type":"library","time":"2017-08-02T13:08:31+00:00","autoload":{"psr-4":{"CristianVuolo\\Uploader\\":"src\/"}},"require":{"php":"\u003E=5.6.4","intervention\/image":"^2.3","laravel\/framework":"5.*"}},"1.0.3":{"name":"cristianvuolo\/uploader","description":"","keywords":[],"homepage":"","version":"1.0.3","version_normalized":"1.0.3.0","license":["MIT"],"authors":[],"source":{"type":"git","url":"https:\/\/github.com\/cristianvuolo\/uploader.git","reference":"77b94732179a4821063fb4dc6a68b5af6e67a94a"},"dist":{"type":"zip","url":"https:\/\/api.github.com\/repos\/cristianvuolo\/uploader\/zipball\/77b94732179a4821063fb4dc6a68b5af6e67a94a","reference":"77b94732179a4821063fb4dc6a68b5af6e67a94a","shasum":""},"type":"library","time":"2017-07-11T16:52:54+00:00","autoload":{"psr-4":{"CristianVuolo\\Uploader\\":"src\/"}},"require":{"php":"\u003E=5.6.4","intervention\/image":"^2.3","laravel\/framework":"5.*"}},"1.0.1":{"name":"cristianvuolo\/uploader","description":"","keywords":[],"homepage":"","version":"1.0.1","version_normalized":"1.0.1.0","license":["MIT"],"authors":[],"source":{"type":"git","url":"https:\/\/github.com\/cristianvuolo\/uploader.git","reference":"6834ca1220f9cbc1f0c0a570483dc164ea193788"},"dist":{"type":"zip","url":"https:\/\/api.github.com\/repos\/cristianvuolo\/uploader\/zipball\/6834ca1220f9cbc1f0c0a570483dc164ea193788","reference":"6834ca1220f9cbc1f0c0a570483dc164ea193788","shasum":""},"type":"library","time":"2017-02-07T20:10:08+00:00","autoload":{"psr-4":{"CristianVuolo\\Uploader\\":"src\/"}},"require":{"php":"\u003E=5.6.4","intervention\/image":"^2.3","laravel\/framework":"5.*"}},"1.0.0":{"name":"cristianvuolo\/uploader","description":"","keywords":[],"homepage":"","version":"1.0.0","version_normalized":"1.0.0.0","license":["MIT"],"authors":[],"source":{"type":"git","url":"https:\/\/github.com\/cristianvuolo\/uploader.git","reference":"37c03e90f2b52a5681453a7f7b0870c359eec649"},"dist":{"type":"zip","url":"https:\/\/api.github.com\/repos\/cristianvuolo\/uploader\/zipball\/37c03e90f2b52a5681453a7f7b0870c359eec649","reference":"37c03e90f2b52a5681453a7f7b0870c359eec649","shasum":""},"type":"library","time":"2017-02-07T20:01:41+00:00","autoload":{"psr-4":{"CristianVuolo\\Uploader\\":"src\/"}},"require":{"php":"\u003E=5.6.4","intervention\/image":"^2.3","laravel\/framework":"5.*"}}},"type":"library","repository":"https:\/\/github.com\/cristianvuolo\/uploader","github_stars":0,"github_watchers":1,"github_forks":0,"github_open_issues":1,"language":"PHP","dependents":0,"suggesters":0,"downloads":{"total":94,"monthly":5,"daily":0},"favers":0}} \ No newline at end of file diff --git a/test/datasource/__snapshots__/packagist.spec.js.snap b/test/datasource/__snapshots__/packagist.spec.js.snap new file mode 100644 index 0000000000..66f0fe9e5f --- /dev/null +++ b/test/datasource/__snapshots__/packagist.spec.js.snap @@ -0,0 +1,43 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`datasource/packagist getDependency processes real data 1`] = ` +Object { + "homepage": "https://github.com/cristianvuolo/uploader", + "name": "cristianvuolo/uploader", + "repositoryUrl": "https://github.com/cristianvuolo/uploader", + "versions": Object { + "1.0.0": Object { + "gitHead": "1.0.0", + "time": "2017-02-07T20:01:41+00:00", + }, + "1.0.1": Object { + "gitHead": "1.0.1", + "time": "2017-02-07T20:10:08+00:00", + }, + "1.0.3": Object { + "gitHead": "1.0.3", + "time": "2017-07-11T16:52:54+00:00", + }, + "1.0.4": Object { + "gitHead": "1.0.4", + "time": "2017-08-02T13:08:31+00:00", + }, + "1.0.5": Object { + "gitHead": "1.0.5", + "time": "2017-08-02T13:23:33+00:00", + }, + "1.0.6": Object { + "gitHead": "1.0.6", + "time": "2018-01-25T18:57:17+00:00", + }, + "1.0.7": Object { + "gitHead": "1.0.7", + "time": "2018-02-19T12:22:34+00:00", + }, + "1.0.8": Object { + "gitHead": "1.0.8", + "time": "2018-03-14T17:40:06+00:00", + }, + }, +} +`; diff --git a/test/datasource/packagist.spec.js b/test/datasource/packagist.spec.js new file mode 100644 index 0000000000..80f96ddd6b --- /dev/null +++ b/test/datasource/packagist.spec.js @@ -0,0 +1,38 @@ +const fs = require('fs'); +const packagist = require('../../lib/datasource/packagist'); +const got = require('got'); + +jest.mock('got'); + +const res1 = fs.readFileSync('test/_fixtures/packagist/uploader.json'); + +describe('datasource/packagist', () => { + describe('getDependency', () => { + it('returns null for empty result', async () => { + got.mockReturnValueOnce({}); + expect(await packagist.getDependency('something')).toBeNull(); + }); + it('returns null for 404', async () => { + got.mockImplementationOnce(() => + Promise.reject({ + statusCode: 404, + }) + ); + expect(await packagist.getDependency('something')).toBeNull(); + }); + it('returns null for unknown error', async () => { + got.mockImplementationOnce(() => { + throw new Error(); + }); + expect(await packagist.getDependency('something')).toBeNull(); + }); + it('processes real data', async () => { + got.mockReturnValueOnce({ + body: JSON.parse(res1), + }); + expect( + await packagist.getDependency('cristianvuolo/uploader') + ).toMatchSnapshot(); + }); + }); +}); diff --git a/test/manager/composer/__snapshots__/extract.spec.js.snap b/test/manager/composer/__snapshots__/extract.spec.js.snap new file mode 100644 index 0000000000..afa89b446d --- /dev/null +++ b/test/manager/composer/__snapshots__/extract.spec.js.snap @@ -0,0 +1,253 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`lib/manager/pip_requirements/extract extractDependencies() extracts dependencies 1`] = ` +Object { + "deps": Array [ + Object { + "currentValue": ">=5.3.2", + "depName": "php", + "depType": "require", + "purl": "pkg:packagist/php", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "symfony/assetic-bundle", + "depType": "require", + "purl": "pkg:packagist/symfony/assetic-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "symfony/monolog-bundle", + "depType": "require", + "purl": "pkg:packagist/symfony/monolog-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "symfony/swiftmailer-bundle", + "depType": "require", + "purl": "pkg:packagist/symfony/swiftmailer-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "2.1.*", + "depName": "symfony/symfony", + "depType": "require", + "purl": "pkg:packagist/symfony/symfony", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "2.2.2", + "depName": "doctrine/common", + "depType": "require", + "purl": "pkg:packagist/doctrine/common", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "doctrine/doctrine-bundle", + "depType": "require", + "purl": "pkg:packagist/doctrine/doctrine-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "doctrine/doctrine-fixtures-bundle", + "depType": "require", + "purl": "pkg:packagist/doctrine/doctrine-fixtures-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "2.2.x-dev", + "depName": "doctrine/orm", + "depType": "require", + "purl": "pkg:packagist/doctrine/orm", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "exercise/elastica-bundle", + "depType": "require", + "purl": "pkg:packagist/exercise/elastica-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "friendsofsymfony/rest-bundle", + "depType": "require", + "purl": "pkg:packagist/friendsofsymfony/rest-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "*", + "depName": "friendsofsymfony/user-bundle", + "depType": "require", + "purl": "pkg:packagist/friendsofsymfony/user-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "*", + "depName": "fzaninotto/faker", + "depType": "require", + "purl": "pkg:packagist/fzaninotto/faker", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "1.0.1", + "depName": "jms/di-extra-bundle", + "depType": "require", + "purl": "pkg:packagist/jms/di-extra-bundle", + "verionScheme": "semver", + }, + Object { + "currentValue": "*", + "depName": "jms/payment-core-bundle", + "depType": "require", + "purl": "pkg:packagist/jms/payment-core-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "1.1.0", + "depName": "jms/security-extra-bundle", + "depType": "require", + "purl": "pkg:packagist/jms/security-extra-bundle", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "knplabs/knp-menu-bundle", + "depType": "require", + "purl": "pkg:packagist/knplabs/knp-menu-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "knplabs/knp-paginator-bundle", + "depType": "require", + "purl": "pkg:packagist/knplabs/knp-paginator-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "liip/imagine-bundle", + "depType": "require", + "purl": "pkg:packagist/liip/imagine-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "merk/dough-bundle", + "depType": "require", + "purl": "pkg:packagist/merk/dough-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "sensio/distribution-bundle", + "depType": "require", + "purl": "pkg:packagist/sensio/distribution-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "sensio/framework-extra-bundle", + "depType": "require", + "purl": "pkg:packagist/sensio/framework-extra-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "sensio/generator-bundle", + "depType": "require", + "purl": "pkg:packagist/sensio/generator-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "simplethings/entity-audit-bundle", + "depType": "require", + "purl": "pkg:packagist/simplethings/entity-audit-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "stof/doctrine-extensions-bundle", + "depType": "require", + "purl": "pkg:packagist/stof/doctrine-extensions-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "dev-master", + "depName": "twig/extensions", + "depType": "require", + "purl": "pkg:packagist/twig/extensions", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "2.3.*", + "depName": "behat/behat", + "depType": "require-dev", + "purl": "pkg:packagist/behat/behat", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "*", + "depName": "behat/behat-bundle", + "depType": "require-dev", + "purl": "pkg:packagist/behat/behat-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "*", + "depName": "behat/mink-bundle", + "depType": "require-dev", + "purl": "pkg:packagist/behat/mink-bundle", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "*", + "depName": "behat/sahi-client", + "depType": "require-dev", + "purl": "pkg:packagist/behat/sahi-client", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + Object { + "currentValue": "*", + "depName": "behat/common-contexts", + "depType": "require-dev", + "purl": "pkg:packagist/behat/common-contexts", + "skipReason": "unsupported-constraint", + "verionScheme": "semver", + }, + ], +} +`; diff --git a/test/manager/composer/extract.spec.js b/test/manager/composer/extract.spec.js new file mode 100644 index 0000000000..62869a2054 --- /dev/null +++ b/test/manager/composer/extract.spec.js @@ -0,0 +1,28 @@ +const fs = require('fs'); +const { + extractDependencies, +} = require('../../../lib/manager/composer/extract'); + +const requirements1 = fs.readFileSync( + 'test/_fixtures/composer/composer1.json', + 'utf8' +); + +describe('lib/manager/pip_requirements/extract', () => { + describe('extractDependencies()', () => { + let config; + beforeEach(() => { + config = {}; + }); + it('returns null for invalid json', () => { + expect(extractDependencies('nothing here', config)).toBe(null); + }); + it('returns null for empty deps', () => { + expect(extractDependencies('{}', config)).toBe(null); + }); + it('extracts dependencies', () => { + const res = extractDependencies(requirements1, config); + expect(res).toMatchSnapshot(); + }); + }); +}); diff --git a/test/workers/repository/extract/__snapshots__/index.spec.js.snap b/test/workers/repository/extract/__snapshots__/index.spec.js.snap index 9dcb229b6d..9cfcebb26f 100644 --- a/test/workers/repository/extract/__snapshots__/index.spec.js.snap +++ b/test/workers/repository/extract/__snapshots__/index.spec.js.snap @@ -11,6 +11,9 @@ Object { "circleci": Array [ Object {}, ], + "composer": Array [ + Object {}, + ], "docker": Array [ Object {}, ], diff --git a/test/workers/repository/process/lookup/__snapshots__/index.spec.js.snap b/test/workers/repository/process/lookup/__snapshots__/index.spec.js.snap index 3a2aed0f40..f5c2e4c8c4 100644 --- a/test/workers/repository/process/lookup/__snapshots__/index.spec.js.snap +++ b/test/workers/repository/process/lookup/__snapshots__/index.spec.js.snap @@ -80,6 +80,15 @@ Array [ ] `; +exports[`manager/npm/lookup .lookupUpdates() handles packagist 1`] = ` +Array [ + Object { + "message": "Failed to look up dependency foo/bar", + "type": "warning", + }, +] +`; + exports[`manager/npm/lookup .lookupUpdates() handles prerelease jumps 1`] = ` Array [ Object { diff --git a/test/workers/repository/process/lookup/index.spec.js b/test/workers/repository/process/lookup/index.spec.js index b3454654ae..1b2dc4e01f 100644 --- a/test/workers/repository/process/lookup/index.spec.js +++ b/test/workers/repository/process/lookup/index.spec.js @@ -813,6 +813,16 @@ describe('manager/npm/lookup', () => { .reply(404); expect(await lookup.lookupUpdates(config)).toMatchSnapshot(); }); + it('handles packagist', async () => { + config.depName = 'foo/bar'; + config.purl = 'pkg:packagist/foo/bar'; + config.packageFile = 'composer.json'; + config.currentValue = '1.0.0'; + nock('https://packagist.org') + .get('/packages/foo/bar.json') + .reply(404); + expect(await lookup.lookupUpdates(config)).toMatchSnapshot(); + }); it('handles unknown purl', async () => { config.depName = 'foo'; config.purl = 'pkg:typo/some/repo'; diff --git a/website/docs/configuration-options.md b/website/docs/configuration-options.md index cba622f526..c20d8cdfce 100644 --- a/website/docs/configuration-options.md +++ b/website/docs/configuration-options.md @@ -124,6 +124,10 @@ This is used to alter `commitMessage` and `prTitle` without needing to copy/past This is used to alter `commitMessage` and `prTitle` without needing to copy/paste the whole string. The "topic" is usually refers to the dependency being updated, e.g. "dependency react". +## composer + +Warning: composer support is in alpha stage so you probably only want to run this if you are helping get it feature-ready. + ## copyLocalLibs Set to true if repository package.json files contain any local (file) dependencies + lock files. The `package.json` files from each will be copied to disk before lock file generation, even if they are within ignored directories. @@ -436,6 +440,10 @@ Add to this object if you wish to define rules that apply only to patch updates. ## paths +## php + +Warning: PHP Composer support is in alpha stage so you probably only want to run this if you are helping get it feature-ready. + ## pin Add to this object if you wish to define rules that apply only to PRs that pin dependencies. -- GitLab