diff --git a/lib/versioning/index.js b/lib/versioning/index.js index f08f9862e6ad811af7613b5813c6e62f98427141..1619f98b56d29e141d7ef2fb0b450a81c00b005c 100644 --- a/lib/versioning/index.js +++ b/lib/versioning/index.js @@ -1,7 +1,9 @@ const semver = require('./semver'); +const pep440 = require('./pep440'); const schemes = { semver, + pep440, }; module.exports = function getVersionScheme(versionScheme) { diff --git a/lib/versioning/pep440/index.js b/lib/versioning/pep440/index.js new file mode 100644 index 0000000000000000000000000000000000000000..323a7adae70e0d9cd7e77b99f121d402d886f1be --- /dev/null +++ b/lib/versioning/pep440/index.js @@ -0,0 +1,55 @@ +const pep440 = require('@renovate/pep440'); +const { filter } = require('@renovate/pep440/lib/specifier'); +const { rangify } = require('./range'); +const semantic = require('./semantic'); + +const { + compare: sortVersions, + satisfies: matches, + valid: isPinnedVersion, + validRange, + explain, + gt: isGreaterThan, + eq: equals, +} = pep440; + +const { major: getMajor, minor: getMinor } = semantic; + +const isStable = input => { + const version = explain(input); + if (!version) { + return false; + } + return !version.is_prerelease; +}; + +const isRange = input => isValid(input) && !isPinnedVersion(input); + +// If this is left as an alias, inputs like "17.04.0" throw errors +const isValid = input => validRange(input); + +const maxSatisfyingVersion = (versions, range) => { + const found = filter(versions, range).sort(sortVersions); + return found.length === 0 ? null : found[found.length - 1]; +}; + +const minSatisfyingVersion = (versions, range) => { + const found = filter(versions, range).sort(sortVersions); + return found.length === 0 ? null : found[0]; +}; + +module.exports = { + equals, + getMajor, + getMinor, + isGreaterThan, + isPinnedVersion, + isRange, + isStable, + isValid, + matches, + maxSatisfyingVersion, + minSatisfyingVersion, + rangify, + sortVersions, +}; diff --git a/lib/versioning/pep440/range.js b/lib/versioning/pep440/range.js new file mode 100644 index 0000000000000000000000000000000000000000..878b657f824227f545946a0c4ad8f0521bd24dac --- /dev/null +++ b/lib/versioning/pep440/range.js @@ -0,0 +1,13 @@ +module.exports = { + rangify, +}; + +function rangify(config, currentVersion, fromVersion, toVersion) { + const { rangeStrategy } = config; + // istanbul ignore if + if (rangeStrategy !== 'pin') { + logger.warn({ rangeStrategy }, 'Unsupported rangeStrategy'); + return null; + } + return toVersion; +} diff --git a/lib/versioning/pep440/semantic.js b/lib/versioning/pep440/semantic.js new file mode 100644 index 0000000000000000000000000000000000000000..6f23ee2a7298161180f25494fdd3084f63e492af --- /dev/null +++ b/lib/versioning/pep440/semantic.js @@ -0,0 +1,26 @@ +const pep440 = require('@renovate/pep440'); + +const major = input => { + const version = pep440.explain(input); + if (!version) { + throw new TypeError('Invalid Version: ' + input); + } + return version.release[0]; +}; + +const minor = input => { + const version = pep440.explain(input); + if (!version) { + throw new TypeError('Invalid Version: ' + input); + } + if (version.release.length < 2) { + return 0; + } + return version.release[1]; +}; + +// those notation are borrowed from semver, not sure if should be moved to @renovate/pep440 +module.exports = { + major, + minor, +}; diff --git a/package.json b/package.json index 705f0dacb898f894692a64d168bfb46965421e89..6b79b3e818ae44f9186db38877621e36ac36015d 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "openpgp": "2.6.2", "p-all": "1.0.0", "parse-link-header": "1.0.1", + "@renovate/pep440": "0.1.1", "pnpm": "2.2.2", "registry-auth-token": "3.3.2", "root-require": "0.3.1", diff --git a/test/versioning/index.spec.js b/test/versioning/index.spec.js index 8dcfd0eb588d2eda9ab75fa731897f647be319de..9e7fb786b710146f6b3e02f96f30672fbbca1166 100644 --- a/test/versioning/index.spec.js +++ b/test/versioning/index.spec.js @@ -5,4 +5,12 @@ describe('versioning(versionScheme)', () => { expect(versioning(undefined)).toBe(versioning('semver')); expect(versioning('unknown')).toBe(versioning('semver')); }); + + it('should return the same interface', () => { + const semverApi = Object.keys(versioning('semver')); + const pep440Api = Object.keys(versioning('pep440')).concat( + 'isLessThanRange' + ); + expect(pep440Api.sort()).toEqual(semverApi.sort()); + }); }); diff --git a/test/versioning/pep440.spec.js b/test/versioning/pep440.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..ab1637d226816a2177012705d5802d3158e4c537 --- /dev/null +++ b/test/versioning/pep440.spec.js @@ -0,0 +1,111 @@ +const pep440 = require('../../lib/versioning')('pep440'); + +describe('pep440.isValid(input)', () => { + it('should return null for irregular versions', () => { + expect(!!pep440.isValid('17.04.0')).toBe(false); + }); + it('should support simple pep440', () => { + expect(!!pep440.isValid('==1.2.3')).toBe(true); + }); + it('should support pep440 with RC', () => { + expect(!!pep440.isValid('==1.2.3rc0')).toBe(true); + }); + it('should support ranges', () => { + expect(!!pep440.isValid('~=1.2.3')).toBe(true); + expect(!!pep440.isValid('==1.2.*')).toBe(true); + expect(!!pep440.isValid('>1.2.3')).toBe(true); + }); + it('should reject github repositories', () => { + expect(!!pep440.isValid('renovateapp/renovate')).toBe(false); + expect(!!pep440.isValid('renovateapp/renovate#master')).toBe(false); + expect( + !!pep440.isValid('https://github.com/renovateapp/renovate.git') + ).toBe(false); + }); +}); +describe('pep440.isRange(input)', () => { + it('rejects simple pep440', () => { + expect(!!pep440.isRange('1.2.3')).toBe(false); + }); + it('accepts tilde', () => { + expect(!!pep440.isRange('~=1.2.3')).toBe(true); + }); + it('accepts glob', () => { + expect(!!pep440.isRange('==1.2.*')).toBe(true); + }); +}); + +describe('pep440.getMinor(input)', () => { + it('returns correct value', () => { + expect(pep440.getMinor('1.2.3')).toBe(2); + }); + it('pads zeros', () => { + expect(pep440.getMinor('1')).toBe(0); + }); + it('throws when version invalid', () => { + expect(() => pep440.getMinor('not_version')).toThrowError(TypeError); + }); +}); + +describe('pep440.getMajor(version)', () => { + it('returns correct value', () => { + expect(pep440.getMajor('1.2.3')).toBe(1); + }); + it('handles epoch', () => { + expect(pep440.getMajor('25!1.2.3')).toBe(1); + }); + it('throws when version invalid', () => { + expect(() => pep440.getMajor('not_version')).toThrowError(TypeError); + }); +}); + +describe('pep440.getMajor(version)', () => { + it('returns correct value', () => { + expect(pep440.getMajor('1.2.3')).toBe(1); + }); + it('handles epoch', () => { + expect(pep440.getMajor('25!1.2.3')).toBe(1); + }); + it('throws when version invalid', () => { + expect(() => pep440.getMajor('not_version')).toThrowError(TypeError); + }); +}); + +describe('pep440.isStable(version)', () => { + it('returns correct value', () => { + expect(pep440.isStable('1.2.3')).toBe(true); + expect(pep440.isStable('1.2.3rc0')).toBe(false); + }); + it('returns false when version invalid', () => { + expect(pep440.isStable('not_version')).toBe(false); + }); +}); + +const versions = [ + '0.9.4', + '1.0.0', + '1.1.5', + '1.2.1', + '1.2.2', + '1.2.3', + '1.3.4', + '2.0.3', +]; + +describe('pep440.maxSatisfyingVersion(versions, range)', () => { + it('returns correct value', () => { + expect(pep440.maxSatisfyingVersion(versions, '~=1.2.1')).toBe('1.2.3'); + }); + it('returns null when none found', () => { + expect(pep440.maxSatisfyingVersion(versions, '~=2.1')).toBe(null); + }); +}); + +describe('pep440.minSatisfyingVersion(versions, range)', () => { + it('returns correct value', () => { + expect(pep440.minSatisfyingVersion(versions, '~=1.2.1')).toBe('1.2.1'); + }); + it('returns null when none found', () => { + expect(pep440.minSatisfyingVersion(versions, '~=2.1')).toBe(null); + }); +}); diff --git a/yarn.lock b/yarn.lock index 6d09871913c351a932be2fb474fe47a10f2a3270..2fb10aa11e00a6091a392add04debfa294325f9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30,6 +30,12 @@ node-fetch "^2.1.1" url-template "^2.0.8" +"@renovate/pep440@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@renovate/pep440/-/pep440-0.1.1.tgz#1f52f35a3fb536b184376059b547a1f48cdf95a5" + dependencies: + xregexp "^4.1.1" + "@semantic-release/commit-analyzer@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@semantic-release/commit-analyzer/-/commit-analyzer-5.0.0.tgz#767a2055b5cd0a67421b1d504f3ca7db97055c42" @@ -7072,6 +7078,10 @@ xml-name-validator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" +xregexp@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.1.1.tgz#eb8a032aa028d403f7b1b22c47a5f16c24b21d8d" + xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"