diff --git a/lib/config/definitions.js b/lib/config/definitions.js index 432609647d047271f67a30e64d94b3975e892ab2..df8d53ebe0dbbd8f364c5fbf49eb8e52ae82a9b5 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 0000000000000000000000000000000000000000..3f52a2406284c47aff8fccc485e0962311cfee14 --- /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 0000000000000000000000000000000000000000..aa54acefa28db7f42cdd0efc5ba4435c2221807d --- /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 0000000000000000000000000000000000000000..9fd44f840d937f71850f246879fbcbfc9b91fd34 --- /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 d4b38352cdd709b515b6dc54fbf7687f10ac8231..236458e7794337f5cc65da214f11011c44d36552 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 ef58b90c343db9c62ce9234aa57a71921f82bbab..0c905b0dc32db1bedebe3438bbf55071a1c52bcb 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 0000000000000000000000000000000000000000..84c31421b7735b1c0eeb0d0688e5e74ac8757bd8 --- /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 0000000000000000000000000000000000000000..2334744c0b893c01e106362b753cea44b063cb1c --- /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 0000000000000000000000000000000000000000..66f0fe9e5ff690d533eb6e9c93bd8152a9b9a991 --- /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 0000000000000000000000000000000000000000..80f96ddd6b05ac2c3ed49ec6a600f8cae44bd1bf --- /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 0000000000000000000000000000000000000000..afa89b446d78312e13affb9750bd031233bba661 --- /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 0000000000000000000000000000000000000000..62869a20549afacfc6d2e370f37b8c1980e3625f --- /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 9dcb229b6d7052c2a474772abc8c65361a18e20c..9cfcebb26f3e39aba2f27693f4577c16af3ad658 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 3a2aed0f405b12e05858b0cf6248221428b773b2..f5c2e4c8c4ea21676c9868ba226e6c38d9036ec3 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 b3454654aed296b0dc6bfc19ee105199a1455d7e..1b2dc4e01f841976c8cd42d7d14e4916bd1c8d40 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 cba622f5261650859bb17f2dccf8d5119119f647..c20d8cdfce5c44106c9786a997b537fe13cd5456 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.