-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Add GitLab CI badge support #841
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
var querystring = require('querystring'); | ||
var request = require('request'); | ||
var autosave = require('json-autosave'); | ||
var serverSecrets; | ||
try { | ||
// Everything that cannot be checked in but is useful server-side | ||
// is stored in this JSON data. | ||
serverSecrets = require('../secret.json'); | ||
} catch(e) {} | ||
var gitlabUserTokens; | ||
var gitlabUserTokensFile = '.gitlab-user-tokens.json'; | ||
autosave(gitlabUserTokensFile, {data:[]}).then(function(f) { | ||
gitlabUserTokens = f; | ||
for (var i = 0; i < gitlabUserTokens.data.length; i++) { | ||
addGitlabToken(gitlabUserTokens.data[i]); | ||
} | ||
}).catch(function(e) { console.error('Could not create ' + gitlabUserTokensFile); }); | ||
|
||
// Retrieve a user token if there is one for which we believe there are requests | ||
// remaining. Return undefined if we could not find one. | ||
function getReqRemainingToken() { | ||
return gitlabUserTokens.data[0]; | ||
} | ||
|
||
function addGitlabToken(token) { | ||
// Insert it only if it is not registered yet. | ||
if (gitlabUserTokens.data.indexOf(token) === -1) { | ||
gitlabUserTokens.data.push(token); | ||
} | ||
} | ||
|
||
function rmGitlabToken(token) { | ||
// Remove it only if it is in there. | ||
var idx = gitlabUserTokens.data.indexOf(token); | ||
if (idx >= 0) { | ||
gitlabUserTokens.data.splice(idx, 1); | ||
} | ||
} | ||
|
||
// Personal tokens allow access to GitLab private repositories. | ||
// You can manage your personal GitLab token at | ||
// <https://gitlab.com/profile/personal_access_tokens>. | ||
if (serverSecrets && serverSecrets.gl_token) { | ||
addGitlabToken(serverSecrets.gl_token); | ||
} | ||
|
||
// Act like request(), but tweak headers and query to avoid hitting a rate | ||
// limit. | ||
function gitlabRequest(request, url, query, cb) { | ||
query = query || {}; | ||
var headers = { | ||
'User-Agent': 'Shields.io', | ||
'Accept': 'application/json', | ||
}; | ||
var gitlabToken = getReqRemainingToken(); | ||
|
||
if (gitlabToken != null) { | ||
// There's currently no rate limit on API as of 2016. | ||
headers['PRIVATE-TOKEN'] = gitlabToken; | ||
} | ||
// Else, try without authentication (not implemented as of 2016) | ||
|
||
var qs = querystring.stringify(query); | ||
if (qs) { url += '?' + qs; } | ||
request(url, {headers: headers}, function(err, res, buffer) { | ||
if (gitlabToken != null) { | ||
if (res.statusCode === 401) { // Unauthorized. | ||
rmGitlabToken(gitlabToken); | ||
} else { | ||
// This was originally for checking rate limits | ||
} | ||
} | ||
cb(err, res, buffer); | ||
}); | ||
} | ||
|
||
exports.request = gitlabRequest; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ var serverPort = +process.env.PORT || +process.argv[2] || (secureServer? 443: 80 | |
var bindAddress = process.env.BIND_ADDRESS || process.argv[3] || '::'; | ||
var infoSite = process.env.INFOSITE || "http://shields.io"; | ||
var githubApiUrl = process.env.GITHUB_URL || 'https://api.github.com'; | ||
var gitlabApiUrl = process.env.GITLAB_URL || 'https://gitlab.com/api/v3'; | ||
var Camp = require('camp'); | ||
var camp = Camp.start({ | ||
documentRoot: __dirname, | ||
|
@@ -20,6 +21,7 @@ var badge = require('./badge.js'); | |
var svg2img = require('./svg-to-img.js'); | ||
var loadLogos = require('./load-logos.js'); | ||
var githubAuth = require('./lib/github-auth.js'); | ||
var gitlabAuth = require('./lib/gitlab-auth.js'); | ||
var querystring = require('querystring'); | ||
var xml2js = require('xml2js'); | ||
var serverSecrets; | ||
|
@@ -572,6 +574,111 @@ cache(function(data, match, sendBadge, request) { | |
}); | ||
})); | ||
|
||
camp.route(/^\/gitlab\/ci\/([sc])\/([^\/]+\/[^\/]+)(?:\/(.+))?\.(svg|png|gif|jpg|json)$/, | ||
cache(function(data, match, sendBadge, request) { | ||
var dataKind = match[1]; | ||
var userRepo = match[2]; // namespace/project | ||
var branch = match[3]; | ||
var format = match[4]; | ||
var apiUrl = gitlabApiUrl + '/projects/' + encodeURIComponent(userRepo) + '/pipelines'; | ||
console.log(apiUrl); | ||
var badgeData = getBadgeData({'s':'build', 'c': 'coverage'}[dataKind], data); | ||
var callback = function(branch, page){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is a bit difficult to follow, and may need to be reworked, it seems. |
||
var qs = { | ||
page: page, | ||
per_page: 100, | ||
}; | ||
gitlabAuth.request(request, apiUrl, qs, function(err, res, buffer) { | ||
if (err != null) { | ||
badgeData.text[1] = 'inaccessible'; | ||
sendBadge(format, badgeData); | ||
return; | ||
} | ||
try { | ||
var data = JSON.parse(buffer); | ||
var done = false; | ||
// Assume sorted by time | ||
if(page >= 10 || data.length == 0) throw 'No match'; | ||
for(var i = 0; i < data.length; ++i) { | ||
if(data[i].ref === branch && ['success', 'failed'].indexOf(data[i].status) != -1) { | ||
switch(dataKind) { | ||
case 's': | ||
var state = data[i].status; | ||
badgeData.text[1] = state; | ||
if (state === 'success') { | ||
badgeData.colorscheme = 'brightgreen'; | ||
} else if (state === 'failed') { | ||
badgeData.colorscheme = 'red'; | ||
} | ||
sendBadge(format, badgeData); | ||
break; | ||
case 'c': | ||
var buildApiUrl = gitlabApiUrl + '/projects/' + encodeURIComponent(userRepo) + | ||
'/repository/commits/' + data[i].sha + '/builds'; | ||
gitlabAuth.request(request, buildApiUrl, {per_page: 100}, function(err, res, buffer) { | ||
if (err != null) { | ||
badgeData.text[1] = 'inaccessible'; | ||
sendBadge(format, badgeData); | ||
return; | ||
} | ||
try { | ||
var data = JSON.parse(buffer); | ||
var done = false; | ||
for(var i = 0; i < data.length; ++i) { | ||
if(data[i].coverage != null) { | ||
console.log(data[i].coverage) | ||
var percentage = parseFloat(data[i].coverage); | ||
if(percentage === NaN) throw 'NaN'; | ||
badgeData.text[1] = percentage.toFixed(0) + '%'; | ||
badgeData.colorscheme = coveragePercentageColor(percentage); | ||
done = true; | ||
} | ||
} | ||
if(!done) throw 'No coverage match' | ||
} catch(e) { | ||
badgeData.text[1] = 'unknown'; | ||
} | ||
sendBadge(format, badgeData); | ||
}); | ||
break; | ||
} | ||
done = true; | ||
break; | ||
} | ||
} | ||
if(!done) { | ||
callback(branch, page+1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @espadrine has mentioned that requests need to be bounded in time and complexity. Could this be modified to make a max of two or three requests? |
||
} | ||
} catch(e) { | ||
badgeData.text[1] = 'unknown'; | ||
sendBadge(format, badgeData); | ||
} | ||
}); | ||
} | ||
if(branch == null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To reduce complexity, could the branch default to |
||
var projectApiUrl = gitlabApiUrl + '/projects/' + encodeURIComponent(userRepo); | ||
gitlabAuth.request(request, projectApiUrl, {}, function(err, res, buffer) { | ||
if(err != null) { | ||
badgeData.text[1] = 'inaccessible'; | ||
sendBadge(format, badgeData); | ||
return; | ||
} | ||
try { | ||
var data = JSON.parse(buffer); | ||
branch = data.default_branch; | ||
} catch(e) {} | ||
if(branch == null || branch === undefined) { | ||
badgeData.text[1] = 'unknown'; | ||
sendBadge(format, badgeData); | ||
return; | ||
} | ||
callback(branch, 1); | ||
}); | ||
} else { | ||
callback(branch, 1); | ||
} | ||
})); | ||
|
||
// 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) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you remove ^^?