Use github auth in php-v badges (#1403)

To fix service test that fails in CI (due to no github auth) https://github.com/badges/shields/issues/1359#issuecomment-354184074

- DRY getPhpReleases()
- Pass named options to regularUpdate
    - Add json option
- php-version: Move helpers before functions, and move exports to end
This commit is contained in:
Paul Melnikow
2017-12-28 21:30:30 -05:00
committed by GitHub
parent 84c60c8730
commit 60959bf7e7
4 changed files with 223 additions and 246 deletions

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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);
});
}

246
server.js
View File

@@ -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 = [];
// 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()));
}
try {
const data = JSON.parse(buffer);
let travisVersions = [];
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);
// 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()));
}
if (hasHhvm) {
reduction += reduction ? ', ' : '';
reduction += 'HHVM';
}
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 (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
@@ -7815,86 +7794,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.