Skip to content

Commit

Permalink
loosely detect semver versions (#1628)
Browse files Browse the repository at this point in the history
* loosely detect semver versions

* semver loose validity

* add test

* semver compare versions when null

* add comments for loose

* remove console.log, only match semver scheme

* looser version restrictions

* re-sort version tests

comments refered to incorrect test

* add support for pre-release tags

#1682

* support uppercase, lowercase mixed

* make pre-release opt in

* change pre param to object

* add notes
  • Loading branch information
RedSparr0w authored Jul 26, 2018
1 parent b216bcc commit 329db7d
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 35 deletions.
38 changes: 27 additions & 11 deletions lib/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,34 @@ const semver = require('semver');

// Given a list of versions (as strings), return the latest version.
// Return undefined if no version could be found.
function latest(versions) {
function latest(versions, { pre = false } = {}) {
let version = '';
let origVersions = versions;
versions = versions.filter(function(version) {
return (/[0-9]/).test(version);
// return all results that are likely semver compatible versions
versions = origVersions.filter(function(version) {
return (/\d+\.\d+/).test(version);
});
// If no semver versions then look for single numbered versions
if (!versions.length){
versions = origVersions.filter(function(version) {
return (/\d+/).test(version);
});
}
if (!pre){
// remove pre-releases from array
versions = versions.filter(function(version) {
return !(/\d+-\w+/).test(version);
});
}
try {
version = semver.maxSatisfying(versions, '');
version = versions.sort((a, b) => {
// coerce to string then lowercase otherwise alpha > RC
return semver.rcompare((''+a).toLowerCase(), (''+b).toLowerCase(), /* loose */ true);
})[0];
} catch(e) {
version = latestDottedVersion(versions);
}
if (version === undefined) {
if (version === undefined || version === null) {
origVersions = origVersions.sort();
version = origVersions[origVersions.length - 1];
}
Expand Down Expand Up @@ -83,14 +99,14 @@ function compareDottedVersion(v1, v2) {
// Slice the specified number of dotted parts from the given semver version.
// e.g. slice('2.4.7', 'minor') -> '2.4'
function slice(v, releaseType) {
if (! semver.valid(v)) {
if (! semver.valid(v, /* loose */ true)) {
return null;
}

const major = semver.major(v);
const minor = semver.minor(v);
const patch = semver.patch(v);
const prerelease = semver.prerelease(v);
const major = semver.major(v, /* loose */ true);
const minor = semver.minor(v, /* loose */ true);
const patch = semver.patch(v, /* loose */ true);
const prerelease = semver.prerelease(v, /* loose */ true);

const dottedParts = {
major: [major],
Expand All @@ -115,7 +131,7 @@ function minor(v) {
}

function rangeStart(v) {
const range = new semver.Range(v);
const range = new semver.Range(v, /* loose */ true);
return range.set[0][0].semver.version;
}

Expand Down
42 changes: 31 additions & 11 deletions lib/version.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

const { test, given } = require('sazerac');
const { latest, slice, rangeStart } = require('./version');
const includePre = true;

describe('Version helpers', function () {
test(latest, () => {
Expand All @@ -10,32 +11,51 @@ describe('Version helpers', function () {
given(['1.0.0', '2.0.0', '3.0.0']).expect('3.0.0');
given(['0.0.1', '0.0.10', '0.0.2', '0.0.20']).expect('0.0.20');

// Simple dotted versions.
given(['1.0.0', 'v1.0.2', 'r1.0.1', 'release-2.0.0']).expect('release-2.0.0');
given(['1.0.0', 'v2.0.0', 'r1.0.1', 'release-1.0.3']).expect('v2.0.0');
given(['2.0.0', 'v1.0.3', 'r1.0.1', 'release-1.0.3']).expect('2.0.0');
given(['1.0.0', 'v1.0.2', 'r2.0.0', 'release-1.0.3']).expect('r2.0.0');
// "not-quite-valid" semver versions
given(['1.0.00', '1.0.02', '1.0.01']).expect('1.0.02');
given(['1.0.05', '2.0.05', '3.0.05']).expect('3.0.05');
given(['0.0.01', '0.0.010', '0.0.02', '0.0.020']).expect('0.0.020');

// Mixed style versions. - include pre-releases
given(['1.0.0', 'v1.0.2', 'r1.0.1', 'release-2.0.0', 'v1.0.1-alpha.1'], { pre: includePre }).expect('release-2.0.0');
given(['1.0.0', 'v2.0.0', 'r1.0.1', 'release-1.0.3', 'v1.0.1-alpha.1'], { pre: includePre }).expect('v2.0.0');
given(['2.0.0', 'v1.0.3', 'r1.0.1', 'release-1.0.3', 'v1.0.1-alpha.1'], { pre: includePre }).expect('2.0.0');
given(['1.0.0', 'v1.0.2', 'r2.0.0', 'release-1.0.3', 'v1.0.1-alpha.1'], { pre: includePre }).expect('r2.0.0');
given(['1.0.0', 'v1.0.2', 'r2.0.0', 'release-1.0.3', 'v2.0.1-alpha.1'], { pre: includePre }).expect('v2.0.1-alpha.1');

// Versions with 'v' prefix.
given(['v1.0.0', 'v1.0.2', 'v1.0.1']).expect('v1.0.2');
given(['v1.0.0', 'v3.0.0', 'v2.0.0']).expect('v3.0.0');

// Simple (2 number) versions.
given(['0.1', '0.3', '0.2']).expect('0.3');
given(['0.1', '0.5', '0.12', '0.21']).expect('0.21');
given(['1.0', '2.0', '3.0']).expect('3.0');

// Versions with '-release' prefix
given(['v1.0.0', 'v1.0.2', 'v1.0.1']).expect('v1.0.2');
given(['v1.0.0', 'v3.0.0', 'v2.0.0']).expect('v3.0.0');
// Simple (one-number) versions
given(['2', '10', '1']).expect('10');

// Include pre-releases
given(['v1.0.1-alpha.2', 'v1.0.1-alpha.1', 'v1.0.1-beta.3', 'v1.0.1-beta.1', 'v1.0.1-RC.1', 'v1.0.1-RC.2', 'v1.0.0'], { pre: includePre }).expect('v1.0.1-RC.2');
given(['v1.0.1-alpha.2', 'v1.0.1-alpha.1', 'v1.0.1-beta.3', 'v1.0.1-beta.1', 'v1.0.1-RC.1', 'v1.0.1-RC.2','v1.0.1'], { pre: includePre }).expect('v1.0.1');
// Exclude pre-releases
given(['v1.0.1-alpha.2', 'v1.0.1-alpha.1', 'v1.0.1-beta.3', 'v1.0.1-beta.1', 'v1.0.1-RC.1', 'v1.0.1-RC.2', 'v1.0.0']).expect('v1.0.0');
given(['v1.0.1-alpha.2', 'v1.0.1-alpha.1', 'v1.0.1-beta.3', 'v1.0.1-beta.1', 'v1.0.1-RC.1', 'v1.0.1-RC.2','v1.0.1']).expect('v1.0.1');

// Versions with '-release' prefix
// Versions with 'release-' prefix
given(['release-1.0.0', 'release-1.0.2', 'release-1.0.20', 'release-1.0.3']).expect('release-1.0.20');

// Simple (one-number) versions
given(['2', '10', '1']).expect('10');
// Semver mixed with non semver versions
given(['1.0.0', '1.0.2', '1.1', '1.0', 'notaversion2', '12bcde4']).expect('1.1');
});

test(slice, () => {
given('2.4.7', 'major').expect('2');
given('2.4.7', 'minor').expect('2.4');
given('2.4.7', 'patch').expect('2.4.7');
given('02.4.7', 'major').expect('2');
given('2.04.7', 'minor').expect('2.4');
given('2.4.07', 'patch').expect('2.4.7');
given('2.4.7-alpha.1', 'major').expect('2-alpha.1');
given('2.4.7-alpha.1', 'minor').expect('2.4-alpha.1');
given('2.4.7-alpha.1', 'patch').expect('2.4.7-alpha.1');
Expand Down
28 changes: 15 additions & 13 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -2300,12 +2300,13 @@ cache(function(data, match, sendBadge, request) {
}));

// Dart's pub version integration.
camp.route(/^\/pub\/v\/(.*)\.(svg|png|gif|jpg|json)$/,
camp.route(/^\/pub\/v(pre)?\/(.*)\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
var userRepo = match[1]; // eg, "box2d"
var format = match[2];
var apiUrl = 'https://pub.dartlang.org/packages/' + userRepo + '.json';
var badgeData = getBadgeData('pub', data);
const includePre = Boolean(match[1]);
const userRepo = match[2]; // eg, "box2d"
const format = match[3];
const apiUrl = 'https://pub.dartlang.org/packages/' + userRepo + '.json';
let badgeData = getBadgeData('pub', data);
request(apiUrl, function(err, res, buffer) {
if (err != null) {
badgeData.text[1] = 'inaccessible';
Expand All @@ -2316,7 +2317,7 @@ cache(function(data, match, sendBadge, request) {
var data = JSON.parse(buffer);
// Grab the latest stable version, or an unstable
var versions = data.versions;
var version = latestVersion(versions);
var version = latestVersion(versions, { pre: includePre });
badgeData.text[1] = versionText(version);
badgeData.colorscheme = versionColor(version);
sendBadge(format, badgeData);
Expand Down Expand Up @@ -3284,13 +3285,14 @@ cache(function (data, match, sendBadge, request) {
}));

// GitHub tag integration.
camp.route(/^\/github\/tag\/([^/]+)\/([^/]+)\.(svg|png|gif|jpg|json)$/,
camp.route(/^\/github\/tag(-?pre)?\/([^/]+)\/([^/]+)\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
var user = match[1]; // eg, expressjs/express
var repo = match[2];
var format = match[3];
var apiUrl = githubApiUrl + '/repos/' + user + '/' + repo + '/tags';
var badgeData = getBadgeData('tag', data);
const includePre = Boolean(match[1]);
const user = match[2]; // eg, expressjs/express
const repo = match[3];
const format = match[4];
const apiUrl = githubApiUrl + '/repos/' + user + '/' + repo + '/tags';
let badgeData = getBadgeData('tag', data);
if (badgeData.template === 'social') {
badgeData.logo = getLogo('github', data);
}
Expand All @@ -3303,7 +3305,7 @@ cache(function(data, match, sendBadge, request) {
try {
var data = JSON.parse(buffer);
var versions = data.map(function(e) { return e.name; });
var tag = latestVersion(versions);
var tag = latestVersion(versions, { pre: includePre });
badgeData.text[1] = versionText(tag);
badgeData.colorscheme = versionColor(tag);
sendBadge(format, badgeData);
Expand Down

0 comments on commit 329db7d

Please sign in to comment.