diff --git a/lib/nuget-provider.js b/lib/nuget-provider.js index f4db7e00b28ab..8137f10ae08d6 100644 --- a/lib/nuget-provider.js +++ b/lib/nuget-provider.js @@ -139,7 +139,8 @@ function mapNugetFeed({ camp, cache }, pattern, offset, getInfo) { function getNugetData(apiUrl, id, request, done) { // get service index document - regularUpdate(apiUrl + '/index.json', + regularUpdate({ + url: apiUrl + '/index.json', // The endpoint changes once per year (ie, a period of n = 1 year). // We minimize the users' waiting time for information. // With l = latency to fetch the endpoint and x = endpoint update period @@ -148,17 +149,9 @@ function mapNugetFeed({ camp, cache }, pattern, offset, getInfo) { // right endpoint. // So the waiting time within n years is n*l/x + x years, for which a // derivation yields an optimum at x = sqrt(n*l), roughly 42 minutes. - (42 * 60 * 1000), - function(buffer) { - const data = JSON.parse(buffer); - - const searchQueryResources = data.resources.filter(function(resource) { - return resource['@type'] === 'SearchQueryService'; - }); - - return searchQueryResources; - }, - function(err, searchQueryResources) { + intervalMillis: 42 * 60 * 1000, + scraper: json => json.resources.filter(resource => resource['@type'] === 'SearchQueryService'), + }, (err, searchQueryResources) => { if (err != null) { done(err); return; } // query autocomplete service @@ -167,7 +160,7 @@ function mapNugetFeed({ camp, cache }, pattern, offset, getInfo) { + '?q=packageid:' + encodeURIComponent(id.toLowerCase()) // NuGet package id (lowercase) + '&prerelease=true'; // Include prerelease versions? - request(reqUrl, function(err, res, buffer) { + request(reqUrl, (err, res, buffer) => { if (err != null) { done(err); return; diff --git a/lib/php-version.js b/lib/php-version.js index 261a7fab14838..9dade95d750cf 100644 --- a/lib/php-version.js +++ b/lib/php-version.js @@ -5,78 +5,11 @@ */ 'use strict'; -const {listCompare} = require('./version.js'); -const {omitv} = require('./text-formatters.js'); +const request = require('request'); const uniq = require('lodash.uniq'); - -// Return a negative value if v1 < v2, -// zero if v1 = v2, -// a positive value otherwise. -// -// See https://getcomposer.org/doc/04-schema.md#version -// and https://github.com/badges/shields/issues/319#issuecomment-74411045 -function compare(v1, v2) { - // Omit the starting `v`. - const rawv1 = omitv(v1); - const rawv2 = omitv(v2); - let v1data, v2data; - try { - v1data = numberedVersionData(rawv1); - v2data = numberedVersionData(rawv2); - } catch(e) { - return asciiVersionCompare(rawv1, rawv2); - } - - // Compare the numbered part (eg, 1.0.0 < 2.0.0). - const numbersCompare = listCompare(v1data.numbers, v2data.numbers); - if (numbersCompare !== 0) { - return numbersCompare; - } - - // Compare the modifiers (eg, alpha < beta). - if (v1data.modifier < v2data.modifier) { - return -1; - } else if (v1data.modifier > v2data.modifier) { - return 1; - } - - // Compare the modifier counts (eg, alpha1 < alpha3). - if (v1data.modifierCount < v2data.modifierCount) { - return -1; - } else if (v1data.modifierCount > v2data.modifierCount) { - return 1; - } - - return 0; -} -exports.compare = compare; - -function latest(versions) { - let latest = versions[0]; - for (let i = 1; i < versions.length; i++) { - if (compare(latest, versions[i]) < 0) { - latest = versions[i]; - } - } - return latest; -} -exports.latest = latest; - -// Whether a version is stable. -function isStable(version) { - const rawVersion = omitv(version); - let versionData; - try { - versionData = numberedVersionData(rawVersion); - } catch(e) { - return false; - } - // normal or patch - return (versionData.modifier === 3) || (versionData.modifier === 4); -} -exports.isStable = isStable; - -// === Private helper functions === +const {listCompare} = require('./version'); +const {omitv} = require('./text-formatters'); +const { regularUpdate } = require('./regular-update'); // Return a negative value if v1 < v2, // zero if v1 = v2, a positive value otherwise. @@ -175,6 +108,69 @@ function numberedVersionData(version) { }; } +// Return a negative value if v1 < v2, +// zero if v1 = v2, +// a positive value otherwise. +// +// See https://getcomposer.org/doc/04-schema.md#version +// and https://github.com/badges/shields/issues/319#issuecomment-74411045 +function compare(v1, v2) { + // Omit the starting `v`. + const rawv1 = omitv(v1); + const rawv2 = omitv(v2); + let v1data, v2data; + try { + v1data = numberedVersionData(rawv1); + v2data = numberedVersionData(rawv2); + } catch(e) { + return asciiVersionCompare(rawv1, rawv2); + } + + // Compare the numbered part (eg, 1.0.0 < 2.0.0). + const numbersCompare = listCompare(v1data.numbers, v2data.numbers); + if (numbersCompare !== 0) { + return numbersCompare; + } + + // Compare the modifiers (eg, alpha < beta). + if (v1data.modifier < v2data.modifier) { + return -1; + } else if (v1data.modifier > v2data.modifier) { + return 1; + } + + // Compare the modifier counts (eg, alpha1 < alpha3). + if (v1data.modifierCount < v2data.modifierCount) { + return -1; + } else if (v1data.modifierCount > v2data.modifierCount) { + return 1; + } + + return 0; +} + +function latest(versions) { + let latest = versions[0]; + for (let i = 1; i < versions.length; i++) { + if (compare(latest, versions[i]) < 0) { + latest = versions[i]; + } + } + return latest; +} + +function isStable(version) { + const rawVersion = omitv(version); + let versionData; + try { + versionData = numberedVersionData(rawVersion); + } catch(e) { + return false; + } + // normal or patch + return (versionData.modifier === 3) || (versionData.modifier === 4); +} + function minorVersion(version) { const result = version.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/); @@ -184,7 +180,6 @@ function minorVersion(version) { return result[1] + '.' + (result[2] ? result[2] : '0'); } -exports.minorVersion = minorVersion; function versionReduction(versions, phpReleases) { if (!versions.length) { @@ -213,4 +208,26 @@ function versionReduction(versions, phpReleases) { return versions.join(', '); } -exports.versionReduction = versionReduction; + +function getPhpReleases(githubRequest, cb) { + regularUpdate({ + url: 'https://api.github.com/repos/php/php-src/git/refs/tags', + intervalMillis: 24 * 3600 * 1000, // 1 day + scraper: tags => uniq( + tags + // only releases + .filter(tag => tag.ref.match(/^refs\/tags\/php-\d+\.\d+\.\d+$/) != null) + // get minor version of release + .map(tag => tag.ref.match(/^refs\/tags\/php-(\d+\.\d+)\.\d+$/)[1])), + request: (url, options, cb) => githubRequest(request, url, {}, cb), + }, cb); +} + +module.exports = { + compare, + latest, + isStable, + minorVersion, + versionReduction, + getPhpReleases, +}; diff --git a/lib/regular-update.js b/lib/regular-update.js index c3ebbf634faab..3a79b0b965f74 100644 --- a/lib/regular-update.js +++ b/lib/regular-update.js @@ -1,33 +1,44 @@ 'use strict'; -const request = require('request'); - -// Map from URL to { timestamp: last fetch time, interval: in milliseconds, -// data: data }. +// Map from URL to { timestamp: last fetch time, data: data }. let regularUpdateCache = Object.create(null); // url: a string, scraper: a function that takes string data at that URL. // interval: number in milliseconds. // cb: a callback function that takes an error and data returned by the scraper. -function regularUpdate(url, interval, scraper, cb, options) { +function regularUpdate({ + url, + intervalMillis, + json = true, + scraper = buffer => buffer, + options = {}, + request = require('request'), +}, cb) { const timestamp = Date.now(); - const cache = regularUpdateCache[url]; - if (cache != null && - (timestamp - cache.timestamp) < interval) { - cb(null, regularUpdateCache[url].data); + const cached = regularUpdateCache[url]; + if (cached != null && + (timestamp - cached.timestamp) < intervalMillis) { + cb(null, cached.data); return; } - request(url, options || {}, function(err, res, buffer) { + request(url, options, (err, res, buffer) => { if (err != null) { cb(err); return; } - if (regularUpdateCache[url] == null) { - regularUpdateCache[url] = { timestamp: 0, data: 0 }; + + let reqData; + if (json) { + try { + reqData = JSON.parse(buffer); + } catch(e) { cb(e); return; } + } else { + reqData = buffer; } + let data; try { - data = scraper(buffer); + data = scraper(reqData); } catch(e) { cb(e); return; } - regularUpdateCache[url].timestamp = timestamp; - regularUpdateCache[url].data = data; + + regularUpdateCache[url] = { timestamp, data }; cb(null, data); }); } diff --git a/server.js b/server.js index 0ca212456a46b..aefea68db0e93 100644 --- a/server.js +++ b/server.js @@ -7,7 +7,6 @@ const prettyBytes = require('pretty-bytes'); const queryString = require('query-string'); const semver = require('semver'); const xml2js = require('xml2js'); -const uniq = require('lodash.uniq'); const analytics = require('./lib/analytics'); const config = require('./lib/server-config'); @@ -26,6 +25,7 @@ const { isStable: phpStableVersion, minorVersion: phpMinorVersion, versionReduction: phpVersionReduction, + getPhpReleases, } = require('./lib/php-version'); const { parseVersion: luarocksParseVersion, @@ -314,80 +314,57 @@ cache(function(data, match, sendBadge, request) { uri: 'https://api.travis-ci.org/repos/' + userRepo + '/branches/' + version, }; const badgeData = getBadgeData('PHP', data); - // Custom user-agent and accept headers are required - // http://developer.github.com/v3/#user-agent-required - // https://developer.github.com/v3/licenses/ - const customHeaders = { - 'User-Agent': 'Shields.io' - }; - // require PHP releases - regularUpdate( - 'https://api.github.com/repos/php/php-src/git/refs/tags', - (24 * 3600 * 1000), // 1 day - tags => { - return uniq( - JSON.parse(tags). - // only releases - filter((tag) => tag.ref.match(/^refs\/tags\/php-\d+\.\d+\.\d+$/) != null). - // get minor version of release - map((tag) => tag.ref.match(/^refs\/tags\/php-(\d+\.\d+)\.\d+$/)[1]) - ); - }, - (err, phpReleases) => { - if (err != null) { + getPhpReleases(githubAuth.request, (err, phpReleases) => { + if (err != null) { + badgeData.text[1] = 'invalid'; + sendBadge(format, badgeData); + return; + } + request(options, (err, res, buffer) => { + if (err !== null) { + log.error('Travis CI error: ' + err.stack); + if (res) { + log.error('' + res); + } badgeData.text[1] = 'invalid'; sendBadge(format, badgeData); return; } - request(options, function(err, res, buffer) { - if (err !== null) { - log.error('Travis CI error: ' + err.stack); - if (res) { - log.error('' + res); - } - badgeData.text[1] = 'invalid'; - sendBadge(format, badgeData); - return; - } - try { - const data = JSON.parse(buffer); - let travisVersions = []; + try { + const data = JSON.parse(buffer); + let travisVersions = []; - // from php - if (typeof data.branch.config.php !== 'undefined') { - travisVersions = travisVersions.concat(data.branch.config.php.map((v) => v.toString())); - } - // from matrix - if (typeof data.branch.config.matrix.include !== 'undefined') { - travisVersions = travisVersions.concat(data.branch.config.matrix.include.map((v) => v.php.toString())); - } + // from php + if (typeof data.branch.config.php !== 'undefined') { + travisVersions = travisVersions.concat(data.branch.config.php.map((v) => v.toString())); + } + // from matrix + if (typeof data.branch.config.matrix.include !== 'undefined') { + travisVersions = travisVersions.concat(data.branch.config.matrix.include.map((v) => v.php.toString())); + } - const hasHhvm = travisVersions.find((v) => v.startsWith('hhvm')); - const versions = travisVersions.map((v) => phpMinorVersion(v)).filter((v) => v.indexOf('.') !== -1); - let reduction = phpVersionReduction(versions, phpReleases); + const hasHhvm = travisVersions.find((v) => v.startsWith('hhvm')); + const versions = travisVersions.map((v) => phpMinorVersion(v)).filter((v) => v.indexOf('.') !== -1); + let reduction = phpVersionReduction(versions, phpReleases); - if (hasHhvm) { - reduction += reduction ? ', ' : ''; - reduction += 'HHVM'; - } + if (hasHhvm) { + reduction += reduction ? ', ' : ''; + reduction += 'HHVM'; + } - if (reduction) { - badgeData.colorscheme = 'blue'; - badgeData.text[1] = reduction; - } else { - badgeData.text[1] = 'invalid'; - } - } catch(e) { + if (reduction) { + badgeData.colorscheme = 'blue'; + badgeData.text[1] = reduction; + } else { badgeData.text[1] = 'invalid'; } - sendBadge(format, badgeData); - }); - }, - { - headers: customHeaders - } - ); + } catch(e) { + badgeData.text[1] = 'invalid'; + } + sendBadge(format, badgeData); + }); + }); })); // Travis integration @@ -1925,16 +1902,19 @@ cache({ return; } badgeData.text[1] = versionRange; - regularUpdate('http://nodejs.org/dist/latest/SHASUMS256.txt', - (24 * 3600 * 1000), - shasums => { + regularUpdate({ + url: 'http://nodejs.org/dist/latest/SHASUMS256.txt', + intervalMillis: 24 * 3600 * 1000, + json: false, + scraper: shasums => { // tarball index start, tarball index end const taris = shasums.indexOf('node-v'); const tarie = shasums.indexOf('\n', taris); const tarball = shasums.slice(taris, tarie); const version = tarball.split('-')[1]; return version; - }, (err, version) => { + }, + }, (err, version) => { if (err != null) { badgeData.text[1] = 'invalid'; sendBadge(format, badgeData); @@ -4917,15 +4897,14 @@ cache(function(data, match, sendBadge, request) { var pluginId = match[1]; // e.g. blueocean var format = match[2]; var badgeData = getBadgeData('plugin', data); - regularUpdate('https://updates.jenkins-ci.org/current/update-center.actual.json', - (4 * 3600 * 1000), - function(json) { - json = JSON.parse(json); - return Object.keys(json.plugins).reduce(function(previous, current) { - previous[current] = json.plugins[current].version; - return previous; - }, {}); - }, function(err, versions) { + regularUpdate({ + url: 'https://updates.jenkins-ci.org/current/update-center.actual.json', + intervalMillis: 4 * 3600 * 1000, + scraper: json => Object.keys(json.plugins).reduce((previous, current) => { + previous[current] = json.plugins[current].version; + return previous; + }, {}), + }, (err, versions) => { if (err != null) { badgeData.text[1] = 'inaccessible'; sendBadge(format, badgeData); @@ -4943,7 +4922,7 @@ cache(function(data, match, sendBadge, request) { badgeData.text[1] = 'not found'; sendBadge(format, badgeData); } - }); + }); })); // Ansible integration @@ -7813,86 +7792,63 @@ cache(function(data, match, sendBadge, request) { uri: 'https://php-eye.com/api/v1/package/' + userRepo + '.json', }; const badgeData = getBadgeData('PHP tested', data); - // Custom user-agent and accept headers are required - // http://developer.github.com/v3/#user-agent-required - // https://developer.github.com/v3/licenses/ - const customHeaders = { - 'User-Agent': 'Shields.io' - }; - // require PHP releases - regularUpdate( - 'https://api.github.com/repos/php/php-src/git/refs/tags', - (24 * 3600 * 1000), // 1 day - tags => { - return uniq( - JSON.parse(tags). - // only releases - filter((tag) => tag.ref.match(/^refs\/tags\/php-\d+\.\d+\.\d+$/) != null). - // get minor version of release - map((tag) => tag.ref.match(/^refs\/tags\/php-(\d+\.\d+)\.\d+$/)[1]) - ); - }, - (err, phpReleases) => { - if (err != null) { + getPhpReleases(githubAuth.request, (err, phpReleases) => { + if (err != null) { + badgeData.text[1] = 'invalid'; + sendBadge(format, badgeData); + return; + } + request(options, function(err, res, buffer) { + if (err !== null) { + log.error('PHP-Eye error: ' + err.stack); + if (res) { + log.error('' + res); + } badgeData.text[1] = 'invalid'; sendBadge(format, badgeData); return; } - request(options, function(err, res, buffer) { - if (err !== null) { - log.error('PHP-Eye error: ' + err.stack); - if (res) { - log.error('' + res); - } - badgeData.text[1] = 'invalid'; + + try { + const data = JSON.parse(buffer); + const travis = data.versions.filter((release) => release.name === version)[0].travis; + + if (!travis.config_exists) { + badgeData.colorscheme = 'red'; + badgeData.text[1] = 'not tested'; sendBadge(format, badgeData); return; } - try { - const data = JSON.parse(buffer); - const travis = data.versions.filter((release) => release.name === version)[0].travis; - - if (!travis.config_exists) { - badgeData.colorscheme = 'red'; - badgeData.text[1] = 'not tested'; - sendBadge(format, badgeData); - return; - } - - let versions = []; - for (const index in travis.runtime_status) { - if (travis.runtime_status[index] === 3 && index.match(/^php\d\d$/) !== null) { - versions.push(index.replace(/^php(\d)(\d)$/, '$1.$2')); - } + let versions = []; + for (const index in travis.runtime_status) { + if (travis.runtime_status[index] === 3 && index.match(/^php\d\d$/) !== null) { + versions.push(index.replace(/^php(\d)(\d)$/, '$1.$2')); } + } - let reduction = phpVersionReduction(versions, phpReleases); + let reduction = phpVersionReduction(versions, phpReleases); - if (travis.runtime_status.hhvm === 3) { - reduction += reduction ? ', ' : ''; - reduction += 'HHVM'; - } + if (travis.runtime_status.hhvm === 3) { + reduction += reduction ? ', ' : ''; + reduction += 'HHVM'; + } - if (reduction) { - badgeData.colorscheme = 'brightgreen'; - badgeData.text[1] = reduction; - } else if (!versions.length) { - badgeData.colorscheme = 'red'; - badgeData.text[1] = 'not tested'; - } else { - badgeData.text[1] = 'invalid'; - } - } catch(e) { + if (reduction) { + badgeData.colorscheme = 'brightgreen'; + badgeData.text[1] = reduction; + } else if (!versions.length) { + badgeData.colorscheme = 'red'; + badgeData.text[1] = 'not tested'; + } else { badgeData.text[1] = 'invalid'; } - sendBadge(format, badgeData); - }); - }, - { - headers: customHeaders - } - ); + } catch(e) { + badgeData.text[1] = 'invalid'; + } + sendBadge(format, badgeData); + }); + }); })); // Any badge.