Auf den Download-Seiten von mycore.de sollen die aktuellen Snapshot-Versionen von MyCoRe und MIR samt direktem Download-Link erscheinen, ohne dass jemand die Seite nach jedem Build anpassen muss. Die notwendigen Informationen befinden sich in den maven-metadata.xml-Dateien des Snapshot-Repositorys auf central.sonatype.com.
Der Browser kann diese Dateien per JavaScript aber nicht einfach abrufen. Sonatype liefert keine passenden CORS-Header aus, daher wird der Zugriff von einer fremden Domain blockiert. Seit Anfang des Jahres lief der Abruf deshalb über den Dienst corsproxy.io. Weil dieser inzwischen einen API-Key benötigt, musste eine Alternative gefunden werden.
Der Hauptzweck von shields.io ist die Erstellung kleiner Status-Grafiken, sogenannte Badges, wie man sie aus vielen GitHub-READMEs kennt. Ein Badge besteht aus einem Label links und einem Wert rechts.
Beispiel-Badge im SVG-Format:
// https://img.shields.io/maven-central/v/org.mycore/mycore-base
Für viele Dienste gibt es fertige Badges, außerdem lassen sich frei konfigurierbare Badges erstellen:
Aussehen und Verhalten steuern Parameter wie style, label, labelColor, color, logo und cacheSeconds.
shields.io liefert die Daten für jedes Badge auf Wunsch auch im
JSON-Format und erlaubt Abrufe von beliebigen Domains ohne CORS-Beschränkungen. So lässt sich der Dienst als öffentlicher Proxy mit eingebauter XPath-Auswertung direkt im Browser per fetch() nutzen.
Beispiel einer JSON-Antwort:
{"label":"maven-central","message":"v2026.06.0","color":"blue","link":[]}
// https://img.shields.io/maven-central/v/org.mycore/mycore-base.json
Für Maven bringt shields.io zwei fertige Versions-Badges mit. Außerdem lassen sich Badges statisch oder dynamisch aus einer XML-Datei erzeugen.
| Badge | Einsatz | URL-Schema |
|---|---|---|
| Maven Central Version | liest Releases auf Maven Central | https://img.shields.io/maven-central/v/<groupId>/<artifactId> |
| Maven metadata URL | liest beliebige Versionen (z. B. Snapshots) aus beliebigen Repositories | https://img.shields.io/maven-metadata?metadataUrl=<URL> |
| Static Badge | erzeugt ein Badge aus den angegebenen Parametern | https://img.shields.io/static/v1?label=<Label>&message=<Text> |
| Dynamic XML Badge | extrahiert einen Wert aus einer XML-Datei | https://img.shields.io/badge/dynamic/xml?url=<URL>&query=<XPath> |
Die beiden Maven-Badges kennen weitere versionsbezogenen Parameter:
strategy bestimmt, welche Version angezeigt wird: highestVersion (Standard, sortiert nach Maven-Semantik), latestProperty oder releaseProperty (Werte aus den Metadaten).filter schränkt die Versionen vorab ein: * ist ein Platzhalter, ein vorangestelltes ! negiert das Muster, z. B. filter=2026.06*.Beim Maven-Metadata-Badge muss metadataUrl auf die artefaktweite maven-metadata.xml zeigen,
also …/<groupId-als-Pfad>/<artifactId>/maven-metadata.xml, und URL-encodiert sein.
Für mycore-base im Snapshot-Repository von Maven Central sieht das so aus:
https://img.shields.io/maven-metadata/v?metadataUrl=https%3A%2F%2Fcentral.sonatype.com
%2Frepository%2Fmaven-snapshots%2Forg%2Fmycore%2Fmycore-base%2Fmaven-metadata.xml
Wird das Badge als <img> in ein <a>-Element eingebunden, ist es auch klickbar. Einen Haken hat das Badge allerdings: Es liefert nur die Snapshot-Version wie 2026.12.0-SNAPSHOT. Für einen Download-Link braucht man aber den Dateinamen mit Zeitstempel und Build-Nummer, etwa mir-webapp-2026.06.1-20261016.132145-35.war. Dieser steht in einer zweiten maven-metadata.xml im Versionsordner des Maven-Artefakts.
Hier kommt ein anderes Badge ins Spiel.
Das
Dynamic XML Badge liest per XPath einen beliebigen Wert aus einem XML-Dokument. Die Pflichtparameter sind url (das XML-Dokument) und query (der XPath-Ausdruck). Mit der Endung .json wird daraus ein einfacher XPath-Service ohne CORS-Beschränkungen:
https://img.shields.io/badge/dynamic/xml.json?url=<XML-URL>&query=<XPath>
Das Ergebnis steht im Feld message.
Für XML-Elemente mit Namespace müsste man im XPath-Ausdruck auf die Funktion local-name() zurückgreifen.
Beispiel:
// https://img.shields.io/badge/dynamic/xml.json
// ?url=https%3A%2F%2Fcentral.sonatype.com%2Frepository%2Fmaven-snapshots
// %2Forg%2Fmycore%2Fmir%2Fmir-webapp%2F2026.06.1-SNAPSHOT%2Fmaven-metadata.xml
// &query=%2F%2FsnapshotVersion%5Bextension%3D%27war%27%5D%5Blast%28%29%5D%2Fvalue
// &label=war
{"label":"war","message":"2026.06.1-20261016.132145-35","color":"blue","link":[]}
Für die notwendige URL-Kodierung der Parameter kann die JavaScript-Funktion encodeURIComponent() verwendet werden, zum Beispiel:
encodeURIComponent("//snapshotVersion[extension='war'][last()]/value")
Die Grundlage bildet ein <a>-Element, das über data-Attribute alle nötigen Angaben zum Maven-Artefakt erhält.
<a class="maven-snapshot-link"
data-maven-groupid="org.mycore" data-maven-artifactid="mycore-base"
data-maven-version="2026.06" data-maven-extension="jar">mycore-base</a>
Die weitere Verarbeitung erfolgt in zwei Schritten:
async function retrieveMavenMetadataURL(e){
const snapshotsMetadataUrl = "https://central.sonatype.com/repository/maven-snapshots/"
+ e.dataset.mavenGroupid.replaceAll(".","/") + "/" + e.dataset.mavenArtifactid
+ "/maven-metadata.xml";
const query = e.dataset.mavenVersion
? ('//version[starts-with(., "'+ e.dataset.mavenVersion +'")][last()]')
: ('//version[last()');
const jsonUrl = 'https://img.shields.io/badge/dynamic/xml.json'
+ '?url=' + encodeURIComponent(snapshotsMetadataUrl)
+ '&query=' + encodeURIComponent(query);
const json = await fetch(jsonUrl)
.then((response) => response.json());
const snapshotVersion = json.message;
let metadataXmlURL = "https://central.sonatype.com/repository/maven-snapshots/"
+ e.dataset.mavenGroupid.replaceAll(".","/") + "/" + e.dataset.mavenArtifactid
+ "/" + snapshotVersion + "/maven-metadata.xml";
return metadataXmlURL;
}
async function retrieveMavenDataViaShieldsIo(e){
const metadataURL = await retrieveMavenMetadataURL(e);
if(metadataURL != null){
const ext = e.dataset.mavenExtension;
const jsonSnapshotVersionUrl = 'https://img.shields.io/badge/dynamic/xml.json'
+ '?url=' + encodeURIComponent(metadataURL)
+ '&query=' + encodeURIComponent("//snapshotVersion[extension='" + ext + "'][last()]/value")
+ '&label=war';
const jsonSnapshotVersion = await fetch(jsonSnapshotVersionUrl)
.then((response) => response.json());
const snapshotVersion = jsonSnapshotVersion.message;
const jsonVersionUrl = 'https://img.shields.io/badge/dynamic/xml.json'
+ '?url=' + encodeURIComponent(metadataURL)
+ '&query=' + encodeURIComponent("//version");
const jsonVersion = await fetch(jsonVersionUrl)
.then((response) => response.json());
const version = jsonVersion.message;
const downloadURL = "https://central.sonatype.com/repository/maven-snapshots/"
+ e.dataset.mavenGroupid.replaceAll(".", "/")
+ "/" + e.dataset.mavenArtifactid+ "/" +version
+ "/" +e.dataset.mavenArtifactid + "-" + snapshotVersion
+ "." +e.dataset.mavenExtension;
return downloadURL;
}
else { return null; }
}
Die zurückgegebene Download-URL kann als href-Attribut des obigen <a>-Elements gesetzt werden. Für dessen Inhalt kann ein weiteres
shields.io-Badge genutzt werden.
shields.io ist mehr als ein Badge-Generator. Über die .json-Endung und das Dynamic XML Badge kann es als XPath-Dienst für öffentliche XML-Dateien eingesetzt werden und umgeht so die fehlenden CORS-Header der Quelle.