Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loosely detect semver versions #1628

Merged
merged 13 commits into from
Jul 26, 2018
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 @@ -2489,12 +2489,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]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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 @@ -2505,7 +2506,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 @@ -3570,13 +3571,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 @@ -3589,7 +3591,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