diff --git a/.gitignore b/.gitignore index 0a05f53c9c..ae33c8b06d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,10 @@ Desktop.ini ._* Thumbs.db +# eclipse +.project +.settings + # Files that might appear on external disks .Spotlight-V100 .Trashes diff --git a/server.js b/server.js index 0861883ced..054e5fb32e 100644 --- a/server.js +++ b/server.js @@ -4626,6 +4626,103 @@ cache(function(data, match, sendBadge, request) { }); })); +function isNexusSnapshotVersion(version) { + var pattern = /(\d+\.)*\d\-SNAPSHOT/; + if (version) { + return version.match(pattern); + } else { + return false; + } +} + +// standalone sonatype nexus installation +// API pattern: +// /nexus/(r|s|)/(http|https)/[:port][/]//[:k1=v1[:k2=v2[...]]]. +// for /nexus/[rs]/... pattern, use the search api of the nexus server, and +// for /nexus//... pattern, use the resolve api of the nexus server. +camp.route(/^\/nexus\/(r|s|[^\/]+)\/(https?)\/((?:[^\/]+)(?:\/[^\/]+)?)\/([^\/]+)\/([^\/:]+)(:.+)?\.(svg|png|gif|jpg|json)$/, +cache(function(data, match, sendBadge, request) { + var repo = match[1]; // r | s | repo-name + var scheme = match[2]; // http | https + var host = match[3]; // eg, `nexus.example.com` + var groupId = encodeURIComponent(match[4]); // eg, `com.google.inject` + var artifactId = encodeURIComponent(match[5]); // eg, `guice` + var queryOpt = (match[6] || '').replace(/:/g, '&'); // eg, `&p=pom&c=doc` + var format = match[7]; + + var badgeData = getBadgeData('nexus', data); + + var apiUrl = scheme + '://' + host + + ((repo == 'r' || repo == 's') + ? ('/service/local/lucene/search?g=' + groupId + '&a=' + artifactId + queryOpt) + : ('/service/local/artifact/maven/resolve?r=' + repo + '&g=' + groupId + '&a=' + artifactId + '&v=LATEST' + queryOpt)); + + request(apiUrl, { headers: { 'Accept': 'application/json' } }, function(err, res, buffer) { + if (err != null) { + badgeData.text[1] = 'inaccessible'; + sendBadge(format, badgeData); + return; + } else if (res && (res.statusCode === 404)) { + badgeData.text[1] = 'no-artifact'; + sendBadge(format, badgeData); + return; + } + try { + var parsed = JSON.parse(buffer); + var version = '0'; + switch (repo) { + case 'r': + if (parsed.data.length === 0) { + badgeData.text[1] = 'no-artifact'; + sendBadge(format, badgeData); + return; + } + version = parsed.data[0].latestRelease; + break; + case 's': + if (parsed.data.length === 0) { + badgeData.text[1] = 'no-artifact'; + sendBadge(format, badgeData); + return; + } + // only want to match 1.2.3-SNAPSHOT style versions, which may not always be in + // 'latestSnapshot' so check 'version' as well before continuing to next entry + parsed.data.every(function(artifact) { + if (isNexusSnapshotVersion(artifact.latestSnapshot)) { + version = artifact.latestSnapshot; + return; + } + if (isNexusSnapshotVersion(artifact.version)) { + version = artifact.version; + return; + } + return true; + }); + break; + default: + version = parsed.data.baseVersion || parsed.data.version; + break; + } + + if (version === '0' || /SNAPSHOT/.test(version)) { + badgeData.colorscheme = 'orange'; + if (version !== '0') { + badgeData.text[1] = version; + } else { + badgeData.text[1] = 'undefined'; + } + } else { + badgeData.colorscheme = 'blue'; + badgeData.text[1] = version; + } + sendBadge(format, badgeData); + } catch(e) { + badgeData.text[1] = 'invalid'; + sendBadge(format, badgeData); + } + }); +})); + // Bower version integration. camp.route(/^\/bower\/v\/(.*)\.(svg|png|gif|jpg|json)$/, cache((data, match, sendBadge, request) => { diff --git a/service-tests/nexus.js b/service-tests/nexus.js new file mode 100644 index 0000000000..f14d4a8eaf --- /dev/null +++ b/service-tests/nexus.js @@ -0,0 +1,72 @@ +'use strict'; + +const Joi = require('joi'); +const ServiceTester = require('./runner/service-tester'); + +const t = new ServiceTester({ id: 'nexus', title: 'Nexus' }); +module.exports = t; + +t.create('search release version') + .get('/r/https/repository.jboss.org/nexus/jboss/jboss-client.json') + .expectJSONTypes(Joi.object().keys({ + name: Joi.equal('nexus'), + value: Joi.string().regex(/^4(\.\d+)+$/) +})); + +t.create('search release version of an inexistent artifact') + .get('/r/https/repository.jboss.org/nexus/jboss/inexistent-artifact-id.json') + .expectJSON({ name: 'nexus', value: 'no-artifact' }); + +t.create('search snapshot version') + .get('/s/https/repository.jboss.org/nexus/com.progress.fuse/fusehq.json') + .expectJSONTypes(Joi.object().keys({ + name: Joi.equal('nexus'), + value: Joi.string().regex(/-SNAPSHOT$/) +})); + +t.create('search snapshot version not in latestSnapshot') + .get('/s/https/repository.jboss.org/nexus/com.progress.fuse/fusehq.json') + .intercept(nock => nock('https://repository.jboss.org') + .get('/nexus/service/local/lucene/search') + .query({g: 'com.progress.fuse', a: 'fusehq'}) + .reply(200, '{ "data": [ { "version": "7.0.1-SNAPSHOT" } ] }')) + .expectJSON({ name: 'nexus', value: '7.0.1-SNAPSHOT' }); + +t.create('search snapshot version of a release artifact') + .get('/s/https/repository.jboss.org/nexus/jboss/jboss-client.json') + .expectJSON({ name: 'nexus', value: 'undefined' }); + +t.create('search snapshot version of an inexistent artifact') + .get('/s/https/repository.jboss.org/nexus/jboss/inexistent-artifact-id.json') + .expectJSON({ name: 'nexus', value: 'no-artifact' }); + +t.create('resolve version') + .get('/developer/https/repository.jboss.org/nexus/ai.h2o/h2o-automl.json') + .expectJSONTypes(Joi.object().keys({ + name: Joi.equal('nexus'), + value: Joi.string().regex(/^3(\.\d+)+$/) +})); + +t.create('resolve version with query') + .get('/fs-public-snapshots/https/repository.jboss.org/nexus/com.progress.fuse/fusehq:c=agent-apple-osx:p=tar.gz.json') + .expectJSONTypes(Joi.object().keys({ + name: Joi.equal('nexus'), + value: Joi.string().regex(/^7(\.\d+)+-SNAPSHOT$/) +})); + +t.create('resolve version of an inexistent artifact') + .get('/developer/https/repository.jboss.org/nexus/jboss/inexistent-artifact-id.json') + .expectJSON({ name: 'nexus', value: 'no-artifact' }); + +t.create('connection error') + .get('/r/https/repository.jboss.org/nexus/jboss/jboss-client.json') + .networkOff() + .expectJSON({ name: 'nexus', value: 'inaccessible' }); + +t.create('json parsing error') + .get('/r/https/repository.jboss.org/nexus/jboss/jboss-client.json') + .intercept(nock => nock('https://repository.jboss.org') + .get('/nexus/service/local/lucene/search') + .query({g: 'jboss', a: 'jboss-client'}) + .reply(200, "this should be a valid json")) + .expectJSON({ name: 'nexus', value: 'invalid' }); diff --git a/try.html b/try.html index 9781088b10..fe3f34d047 100644 --- a/try.html +++ b/try.html @@ -598,6 +598,14 @@ Pixel-perfect   Retina-ready   Fast   Consistent   Hackable https://img.shields.io/maven-central/v/org.apache.maven/apache-maven/2.svg + Sonatype Nexus (Releases): + + https://img.shields.io/nexus/r/https/oss.sonatype.org/com.google.guava/guava.svg + + Sonatype Nexus (Snapshots): + + https://img.shields.io/nexus/s/https/oss.sonatype.org/com.google.guava/guava.svg + WordPress plugin: https://img.shields.io/wordpress/plugin/v/akismet.svg