Maven-Repository in Eigenregie hosten

Wenn mittels Maven eine Abhängigkeit zur pom.xml-Datei, wie in folgendem Beispiel, hinzugefügt wird:

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.7.26</version>
</dependency>

versucht Maven diese Abhängigkeit von Maven Central, dem zentralen Repository, zu beziehen. Es ist allerdings möglich Abhängigkeiten aus anderen Repositories zu beziehen. Dazu muss der repositories-Block zur pom.xml hinzugefügt werden.

<repositories>
	<repository>
		<id>example</id>
		<url>https://repository.example.org</url>
	</repository>
</repositories>

Aus Nutzersicht ist die Konfiguration von Maven damit erledigt. Soll ein solches Maven-Reposity aufgesetzt werden, ist dies relativ einfach möglich. Dazu wird ein Verzeichnis per HTTP über den Webserver Nginx ausgeliefert. Innerhalb dieses Verzeichnisses wird ein gesicherter Ordner erstellt, welcher über WebDAV erreicht werden kann. Die entsprechende Nginx-Konfiguration sieht für diesen Fall wie folgt aus:

server {
        listen   443 ssl;
        listen [::]:443 ssl;

        ssl_certificate /etc/letsencrypt/live/repository.example.org/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/repository.example.org/privkey.pem;

        root /var/www/example/repository;

        server_name repository.example.org;

        location / {
                autoindex     on;
        }

        location /upload {
                dav_methods     PUT DELETE MKCOL COPY MOVE;
                dav_ext_methods   PROPFIND OPTIONS;

                dav_access    user:rw group:rw all:rw;

                create_full_put_path  on;

                autoindex     on;

                auth_basic "restricted";
                auth_basic_user_file /var/www/example/repository/upload/.htpasswd;
        }
}

Die .htpasswd-Datei, welche für die Authentifizierung benötigt wird, kann mit dem Tool htpasswd erstellt werden:

htpasswd -c .htpasswd nutzer1

Bei der Nutzung wird das gewünschte Passwort erfragt. Soll ein weiterer Nutzer hinzugefügt werden, so muss der Parameter -c entfernt werden:

htpasswd .htpasswd nutzer2

Anschließend wird die Konfiguration von Nginx mittels:

service nginx reload

aktualisiert. Danach kann WebDAV für die Ressource verwendet werden. In der pom.xml-Datei für das Projekt, welches ein Artefakt für das Repository erzeugt, wird der build-Bereich erweitert und der distributionManagment-Bereich hinzugefügt:

            </plugin>
        </plugins>
        <extensions>
            <extension>
                <groupId>org.apache.maven.wagon</groupId>
                <artifactId>wagon-webdav-jackrabbit</artifactId>
                <version>3.2.0</version>
            </extension>
        </extensions>
    </build>
    <distributionManagement>
        <repository>
            <id>example</id>
            <url>dav:https://repository.example.org/upload/</url>
            <layout>default</layout>
        </repository>
    </distributionManagement>
</project>

Im Extension-Bereich wird das Wagon-Plugin für WebDAV aktiviert. Dieses sorgt dafür das die Artefakte per WebDAV zum Repository hochgeladen werden. In dem distributionManagment-Bereich wird die WebDAV-URL definiert. Wenn nun ein:

mvn deploy

durchgeführt wird, wird dies allerdings nicht funktionieren. Hintergrund ist das der upload-Ordner per Basic authentication geschützt ist. Die Zugangsdaten für diese Authentifikation müssen noch hinterlegt werden. Natürlich werden diese nicht in der pom.xml hinterlegt, da dies aus Gründen der Sicherheit keine gute Idee wäre. Stattdessen werden die Daten in der settings.xml hinterlegt. Diese befindet sich im Pfad:

~/.m2/settings.xml

In dieser Datei wird eine server-Sektion mit den Zugangsdaten hinzugefügt:

<settings>
	<servers>
		<server>
			<id>example</id>
			<username>example</username>
			<password>password</password>
		</server>
	</servers>
</settings>

Anschließend kann das Artefakt erfolgreich mittels:

mvn deploy

in das Repository hochgeladen werden. Problematisch ist das sich die Daten nur im upload-Verzeichnis und nicht im eigentlichen Verzeichnis des Repository liegen. Zur Lösung hierfür wird ein Cronjob, im Kontext des Nutzers www-data, angelegt:

sudo -u www-data crontab -e

Ziel des Cronjobs ist es die Daten aus dem upload-Verzeichnis regelmäßig in das eigentliche Verzeichnis des Repository zu kopieren:

*/5  *    * * *   cp /var/www/example/repository/upload/* /var/www/example/repository/ -r

Damit wird alle 5 Minuten der Inhalt des uploadVerzeichnisses in das Repository kopiert. Ein Verschieben kann leider nicht durchgeführt werden, da die Metadaten beim jeden Deployment von Maven eingelesen werden und erweitert werden. Hier könnten Optimierungen durchgeführt werden, so das alle Dateien bis auf die Metadaten verschoben werden. Soll ein nicht öffentliches Repository erstellt werden, kann auf den zusätzlichen upload-Ordner verzeichnet werden und das Hauptverzeichnis selber per WebDAV befüllt werden. So lange die Zugangsdaten für den Server in der settings.xml-Datei hinterlegt sind, können seitens Maven die entsprechenden Artefakte bezogen werden.

Git-Branch ohne History erstellen

Eine wichtige Eigenschaft moderner Versionskontrollsysteme ist die Möglichkeit Branches zu erstellen. Ein neu erstellter Branch stellt aus Anwendersicht eine Kopie des Quellbranches da. Manchmal soll allerdings ein Branch erstellt werden, welcher nicht von der Versionsgeschichte eines Quellbranches beeinflusst ist. Unter Git kann ein solcher Branch mit dem Befehl:

git checkout --orphan branchName

erstellt werden. Dadurch wird ein Branch ohne Elternteil erstellt. Dies wiederum führt dazu das der Branch keinerlei Versionsgeschichte verfügt und unabhängig von anderen Branches des gleichen Repository existiert.

Git Repository mit Submodulen klonen

In der Git Welt existieren sogenannte Submodule. Diese kann man dem Repository hinzufügen und damit ein Git Repository in ein anderes Git Repository einbetten. Nach dem klonen eines solchen Projektes mittels:

git clone git@example.org:seeseekey/project.git

wird man feststellen das sich nur das Hauptprojekt auf der Festplatte befindet. Möchte man die Submodule ebenfalls mit klonen so muss der Parameter recursive zusätzlich übergeben werden:

git clone --recursive git@example.org:seeseekey/project.git

Mit Hilfe des Parameters werden alle Submodule ebenfalls geklont und landen somit auf der Festplatte.

Submodule unter Git nutzen

Manchmal möchte man Git-Repository in einer bestimmten Art strukturieren. So will man unter Umständen mehrere Repositories logisch zu einem Repository gesellen. Dafür gibt es unter Git Submodule. Gegeben sei folgende Repositorystruktur:

Framework
Library1
Library2
Library3

Möchte man die Bibliotheken Library1, Library2 und Library3 logisch in das Repository Framework einbinden, kann man die Submodule nutzen. Dazu geht man in das Repository Framework und fügt die andere Repositories als Submodule hinzu:

git submodule add git@example.org:Library1
git submodule add git@example.org:Library2
git submodule add git@example.org:Library3

Damit wird im Repository Framework eine Datei mit dem Namen .gitmodules angelegt, in welcher folgender Inhalt zu finden ist:

[submodule "Library1"]
	path = Library1
	url = git@example.org:Library1

[submodule "Library2"]
	path = Library2
	url = git@example.org:Library2

[submodule "Library3"]
	path = Library3
	url = git@example.org:Library3

Diese Datei kann dann per Commit dem Repository hinzugefügt werden. Beim klonen eines solchen Repositories, muss man nur darauf achten das es rekursiv geklont und gepullt (git submodule foreach git pull) wird, damit die Submodule ebenfalls aktualisiert werden.

Git Repository in Subrepository verwandeln

Auf mactricks.de gibt es eine schöne Anleitung um aus einem Teil eines Git Repositories ein Subrepository zu erzeugen. Allerdings gibt es mit der Variante ein Problem. Wenn man das ganze mehr als zwei oder dreimal machen möchte, wird es mit der Zeit nervig all diese Befehle einzugeben.

Aus diesem Grund habe ich für das Extrahieren eines Subprojektes aus einem Git Repository ein Skript geschrieben:

#!/bin/sh
# extractSubproject <orignal repopath> <new repopath> <subfolder> <new remote (optional)>

# clone repository
git clone --no-hardlinks $1 $2

# extract subproject
cd $2
git filter-branch --subdirectory-filter $3 HEAD
git reset --hard
git remote rm origin
rm -r .git/refs/original/
git reflog expire --expire=now --all
git gc --aggressive
git prune

# Add optional remote and push
if [ "$4" != "" ]; then
git remote add origin $4
  git push origin master
fi

Heruntergeladen werden kann sich das Skript auch unter https://github.com/seeseekey/archive/blob/master/Bash/Git/extractSubproject.sh.