diff --git a/coverage.svg b/coverage.svg
index 0cad02c..5337bed 100644
--- a/coverage.svg
+++ b/coverage.svg
@@ -1 +1 @@
-coverage coverage 76.5% 76.5%
\ No newline at end of file
+coverage coverage 76.8% 76.8%
\ No newline at end of file
diff --git a/index.html b/index.html
index 86d14dc..9c30ab1 100644
--- a/index.html
+++ b/index.html
@@ -74,8 +74,8 @@ Pixel-perfect Retina-ready Fast Consistent Hackable
https://img.shields.io/travis/joyent/node/v0.6.svg
Wercker:
-
- https://img.shields.io/wercker/ci/54330318b4ce963d50020750.svg
+
+ https://img.shields.io/wercker/ci/wercker/docs.svg
TeamCity CodeBetter:
@@ -471,6 +471,10 @@ Pixel-perfect Retina-ready Fast Consistent Hackable
https://img.shields.io/david/dev/strongloop/express.svg
+ David:
+
+ https://img.shields.io/david/optional/elnounch/byebye.svg
+
David:
https://img.shields.io/david/peer/webcomponents/generator-element.svg
@@ -676,8 +680,8 @@ The following styles are available (flat is the default as of Feb 1st 2015):
https://img.shields.io/badge/style-flat-green.svg?style=flat
-
- https://img.shields.io/badge/style-flat--squared-green.svg?style=flat-square
+
+ https://img.shields.io/badge/style-flat--square-green.svg?style=flat-square
@@ -700,7 +704,7 @@ And tell us, we might be able to bring it to you anyway!
-
+
diff --git a/load-logos.js b/load-logos.js
new file mode 100644
index 0000000..3aa8407
--- /dev/null
+++ b/load-logos.js
@@ -0,0 +1,20 @@
+var fs = require('fs');
+var path = require('path');
+
+var loadLogos = function() {
+ var logos = {};
+ var logoFiles = fs.readdirSync(path.join(__dirname, 'logo'));
+ logoFiles.forEach(function(filename) {
+ if (filename[0] === '.') { return; }
+ // filename is eg, github.svg
+ var svg = fs.readFileSync(
+ path.join(__dirname, 'logo', filename)).toString();
+
+ // eg, github
+ var name = filename.slice(0, -('.svg'.length));
+ logos[name] = 'data:image/svg+xml;base64,' + Buffer(svg).toString('base64');
+ });
+ return logos;
+};
+
+module.exports = loadLogos;
diff --git a/logo/github.svg b/logo/github.svg
new file mode 100644
index 0000000..99e8eb5
--- /dev/null
+++ b/logo/github.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/logo/gratipay.svg b/logo/gratipay.svg
new file mode 100644
index 0000000..fcf505e
--- /dev/null
+++ b/logo/gratipay.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/server.js b/server.js
index 39ebcdc..8350e40 100644
--- a/server.js
+++ b/server.js
@@ -12,6 +12,7 @@ var fs = require('fs');
var LruCache = require('./lru-cache.js');
var badge = require('./badge.js');
var svg2img = require('./svg-to-img.js');
+var loadLogos = require('./load-logos.js');
var querystring = require('querystring');
var serverSecrets;
try {
@@ -23,6 +24,7 @@ var semver = require('semver');
var serverStartTime = new Date((new Date()).toGMTString());
var validTemplates = ['default', 'plastic', 'flat', 'flat-square', 'social'];
+var logos = loadLogos();
// Analytics
@@ -176,7 +178,8 @@ function cache(f) {
}
var cacheIndex = match[0] + '?label=' + data.label + '&style=' + data.style
- + '&logo=' + data.logo + '&logoWidth=' + data.logoWidth;
+ + '&logo=' + data.logo + '&logoWidth=' + data.logoWidth
+ + '&link=' + data.link;
// Should we return the data right away?
var cached = requestCache.get(cacheIndex);
var cachedVersionSent = false;
@@ -222,6 +225,8 @@ function cache(f) {
} else {
options = uri;
}
+ options.headers = options.headers || {};
+ options.headers['User-Agent'] = options.headers['User-Agent'] || 'Shields.io';
return request(options, function(err, res, json) {
if (res != null && res.headers != null) {
var cacheControl = res.headers['cache-control'];
@@ -309,8 +314,43 @@ cache(function(data, match, sendBadge, request) {
});
}));
+// Shippable integration
+camp.route(/^\/shippable?\/([^\/]+)(?:\/(.+))?\.(svg|png|gif|jpg|json)$/,
+cache(function(data, match, sendBadge, request) {
+ var project = match[1]; // eg, 54d119db5ab6cc13528ab183
+ var branch = match[2];
+ var format = match[3];
+ var url = 'https://api.shippable.com/projects/' + project + '/badge';
+ if (branch != null) {
+ url += '?branchName=' + branch;
+ }
+ var badgeData = getBadgeData('build', data);
+ fetchFromSvg(request, url, function(err, res) {
+ if (err != null) {
+ badgeData.text[1] = 'inaccessible';
+ sendBadge(format, badgeData);
+ return;
+ }
+ try {
+ badgeData.text[1] = res;
+ if (res === 'shippable') {
+ badgeData.colorscheme = 'brightgreen';
+ } else if (res === 'unshippable') {
+ badgeData.colorscheme = 'red';
+ } else {
+ badgeData.text[1] = res;
+ }
+ sendBadge(format, badgeData);
+
+ } catch(e) {
+ badgeData.text[1] = 'invalid';
+ sendBadge(format, badgeData);
+ }
+ });
+}));
+
// Wercker integration
-camp.route(/^\/wercker\/ci\/(.+)\.(svg|png|gif|jpg|json)$/,
+camp.route(/^\/wercker\/ci\/([a-fA-F0-9]+)\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
var projectId = match[1]; // eg, `54330318b4ce963d50020750`
var format = match[2];
@@ -349,6 +389,47 @@ cache(function(data, match, sendBadge, request) {
});
}));
+// Wercker V3 integration
+camp.route(/^\/wercker\/ci\/(.+)\/(.+)\.(svg|png|gif|jpg|json)$/,
+cache(function(data, match, sendBadge, request) {
+ var owner = match[1];
+ var application = match[2];
+ var format = match[3];
+ var options = {
+ method: 'GET',
+ json: true,
+ uri: 'https://app.wercker.com/api/v3/applications/' + owner + '/' + application + '/builds?limit=1'
+ };
+ var badgeData = getBadgeData('build', data);
+ request(options, function(err, res, json) {
+ if (err != null) {
+ badgeData.text[1] = 'inaccessible';
+ sendBadge(format, badgeData);
+ return;
+ }
+ try {
+ var build = json[0];
+
+ if (build.status === 'finished') {
+ if (build.result === 'passed') {
+ badgeData.colorscheme = 'brightgreen';
+ badgeData.text[1] = build.result;
+ } else {
+ badgeData.colorscheme = 'red';
+ badgeData.text[1] = build.result;
+ }
+ } else {
+ badgeData.text[1] = build.status;
+ }
+ sendBadge(format, badgeData);
+
+ } catch(e) {
+ badgeData.text[1] = 'invalid';
+ sendBadge(format, badgeData);
+ }
+ });
+}));
+
// Rust download and version integration
camp.route(/^\/crates\/(d|v|dv|l)\/([A-Za-z0-9_-]+)(?:\/([0-9.]+))?\.(svg|png|gif|jpg|json)$/,
cache(function (data, match, sendBadge, request) {
@@ -647,6 +728,9 @@ cache(function(data, match, sendBadge, request) {
var format = match[3];
var apiUrl = 'https://www.gratipay.com/' + user + '/public.json';
var badgeData = getBadgeData('tips', data);
+ if (badgeData.template === 'social') {
+ badgeData.logo = badgeData.logo || logos.gratipay;
+ }
request(apiUrl, function dealWithData(err, res, buffer) {
if (err != null) {
badgeData.text[1] = 'inaccessible';
@@ -1389,16 +1473,87 @@ cache(function(data, match, sendBadge, request) {
badgeData.text[1] = vdata.version;
badgeData.colorscheme = vdata.color;
sendBadge(format, badgeData);
- } else if (info == 'l') {
+ } else if (info === 'l') {
var license = data.info.license;
badgeData.text[0] = 'license';
- if (license == null || license == 'UNKNOWN') {
+ if (license === null || license === 'UNKNOWN') {
badgeData.text[1] = 'Unknown';
} else {
badgeData.text[1] = license;
badgeData.colorscheme = 'blue';
}
sendBadge(format, badgeData);
+ } else if (info === 'wheel') {
+ var releases = data.releases[data.info.version];
+ var hasWheel = false;
+ for (var i = 0; i < releases.length; i++) {
+ if (releases[i].packagetype === 'wheel' ||
+ releases[i].packagetype === 'bdist_wheel') {
+ hasWheel = true;
+ break;
+ }
+ }
+ badgeData.text[0] = 'wheel';
+ badgeData.text[1] = hasWheel ? 'yes' : 'no';
+ badgeData.colorscheme = hasWheel ? 'brightgreen' : 'red';
+ sendBadge(format, badgeData);
+ } else if (info === 'pyversions') {
+ var versions = [];
+ var pattern = /^Programming Language \:\: Python \:\: (\d\.\d)$/;
+ for (var i = 0; i < data.info.classifiers.length; i++) {
+ var matched = pattern.exec(data.info.classifiers[i]);
+ if (matched && matched[1]) {
+ versions.push(matched[1]);
+ }
+ }
+ if (!versions.length) {
+ versions.push('not found');
+ }
+ badgeData.text[0] = 'python';
+ badgeData.text[1] = versions.sort().join(', ');
+ badgeData.colorscheme = 'blue';
+ sendBadge(format, badgeData);
+ } else if (info === 'implementation') {
+ var implementations = [];
+ var pattern = /^Programming Language \:\: Python \:\: Implementation \:\: (\S+)$/;
+ for (var i = 0; i < data.info.classifiers.length; i++) {
+ var matched = pattern.exec(data.info.classifiers[i]);
+ if (matched && matched[1]) {
+ implementations.push(matched[1].toLowerCase());
+ }
+ }
+ if (!implementations.length) {
+ implementations.push('cpython'); // assume CPython
+ }
+ badgeData.text[0] = 'implementation';
+ badgeData.text[1] = implementations.sort().join(', ');
+ badgeData.colorscheme = 'blue';
+ sendBadge(format, badgeData);
+ } else if (info === 'status') {
+ var pattern = /^Development Status \:\: ([1-7]) - (\S+)$/;
+ var statusColors = {
+ '1': 'red', '2': 'red', '3': 'red', '4': 'yellow',
+ '5': 'brightgreen', '6': 'brightgreen', '7': 'red'};
+ var statusCode = '1', statusText = 'unknown';
+ for (var i = 0; i < data.info.classifiers.length; i++) {
+ var matched = pattern.exec(data.info.classifiers[i]);
+ if (matched && matched[1] && matched[2]) {
+ statusCode = matched[1];
+ statusText = matched[2].toLowerCase().replace('-', '--');
+ if (statusText === 'production/stable') {
+ statusText = 'stable';
+ }
+ break;
+ }
+ }
+ badgeData.text[0] = 'status';
+ badgeData.text[1] = statusText;
+ badgeData.colorscheme = statusColors[statusCode];
+ sendBadge(format, badgeData);
+ } else {
+ // That request is incorrect.
+ badgeData.text[1] = 'request unknown';
+ sendBadge(format, badgeData);
}
} catch(e) {
badgeData.text[1] = 'invalid';
@@ -2109,6 +2264,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('tag', data);
+ if (badgeData.template === 'social') {
+ badgeData.logo = badgeData.logo || logos.github;
+ }
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@@ -2149,6 +2307,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('release', data);
+ if (badgeData.template === 'social') {
+ badgeData.logo = badgeData.logo || logos.github;
+ }
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@@ -2192,6 +2353,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('downloads', data);
+ if (badgeData.template === 'social') {
+ badgeData.logo = badgeData.logo || logos.github;
+ }
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@@ -2239,6 +2403,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('issues', data);
+ if (badgeData.template === 'social') {
+ badgeData.logo = badgeData.logo || logos.github;
+ }
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@@ -2277,6 +2444,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('forks', data);
+ if (badgeData.template === 'social') {
+ badgeData.logo = badgeData.logo || logos.github;
+ }
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@@ -2316,6 +2486,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('stars', data);
+ if (badgeData.template === 'social') {
+ badgeData.logo = badgeData.logo || logos.github;
+ }
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@@ -2352,6 +2525,9 @@ cache(function(data, match, sendBadge, request) {
+ '&client_secret=' + serverSecrets.gh_client_secret;
}
var badgeData = getBadgeData('followers', data);
+ if (badgeData.template === 'social') {
+ badgeData.logo = badgeData.logo || logos.github;
+ }
// A special User-Agent is required:
// http://developer.github.com/v3/#user-agent-required
request(apiUrl, { headers: githubHeaders }, function(err, res, buffer) {
@@ -2383,6 +2559,9 @@ cache(function(data, match, sendBadge, request) {
var format = match[3];
var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo;
var badgeData = getBadgeData('license', data);
+ if (badgeData.template === 'social') {
+ badgeData.logo = badgeData.logo || logos.github;
+ }
// Using our OAuth App secret grants us 5000 req/hour
// instead of the standard 60 req/hour.
if (serverSecrets) {
@@ -3705,7 +3884,7 @@ function getLabel(label, data) {
return data.label || label;
}
-// data (URL query) can include `label`, `style`, `logo`, `logoWidth`.
+// data (URL query) can include `label`, `style`, `logo`, `logoWidth`, `link`.
function getBadgeData(defaultLabel, data) {
var label = getLabel(defaultLabel, data);
var template = data.style || 'default';
@@ -3722,7 +3901,8 @@ function getBadgeData(defaultLabel, data) {
colorscheme: 'lightgrey',
template: template,
logo: data.logo,
- logoWidth: +data.logoWidth
+ logoWidth: +data.logoWidth,
+ links: data.link,
};
}
diff --git a/try.html b/try.html
index fea686f..a21b93d 100644
--- a/try.html
+++ b/try.html
@@ -73,8 +73,8 @@ Pixel-perfect Retina-ready Fast Consistent Hackable
https://img.shields.io/travis/joyent/node/v0.6.svg
Wercker:
-
- https://img.shields.io/wercker/ci/54330318b4ce963d50020750.svg
+
+ https://img.shields.io/wercker/ci/wercker/docs.svg
TeamCity CodeBetter:
@@ -116,7 +116,15 @@ Pixel-perfect Retina-ready Fast Consistent Hackable
https://img.shields.io/circleci/token/YOURTOKEN/project/BrightFlair/PHP.Gt/master.svg
- Jenkins:
+ Shippable:
+
+ https://img.shields.io/shippable/54d119db5ab6cc13528ab183.svg
+
+ Shippable branch:
+
+ https://img.shields.io/shippable/54d119db5ab6cc13528ab183/master.svg
+
+ Jenkins:
https://img.shields.io/jenkins/s/https/jenkins.qa.ubuntu.com/precise-desktop-amd64_default.svg
@@ -514,6 +522,22 @@ Pixel-perfect Retina-ready Fast Consistent Hackable
https://img.shields.io/pypi/l/Django.svg
+ PyPI:
+
+ https://img.shields.io/pypi/wheel/Django.svg
+
+ PyPI:
+
+ https://img.shields.io/pypi/pyversions/Django.svg
+
+ PyPI:
+
+ https://img.shields.io/pypi/implementation/Django.svg
+
+ PyPI:
+
+ https://img.shields.io/pypi/status/Django.svg
+
Hex.pm:
https://img.shields.io/hexpm/l/plug.svg
@@ -707,7 +731,7 @@ And tell us, we might be able to bring it to you anyway!
-
+