Wir verwenden Gradle und Github Actions zur Veröffentlichung des Java-Projekts im Sonatype Maven Central Repository.

In diesem Artikel möchte ich den Prozess der Veröffentlichung eines Java-Artefakts von Grund auf durch Github Actions im Sonatype Maven Central Repository mit dem Build-Tool Gradle im Detail betrachten.

Ich habe diesen Artikel verfasst, da es keinen umfassenden Tutorium an einem Ort gab. Alle Informationen mussten aus verschiedenen, nicht immer aktuellen Quellen zusammengesucht werden. Wer interessiert ist, ist herzlich eingeladen, weiterzulesen.

Erstellung eines Repositories in Sonatype

Der erste Schritt besteht darin, ein Repository im Sonatype Maven Central zu erstellen. Dazu gehen wir hierhin, registrieren uns und erstellen eine neue Anfrage zur Erstellung eines Repositories. Wir geben unseren GroupId ein Projekt, Projekt-URL den Link zum Projekt und SCM-URL den Link zum Versionsverwaltungssystem, in dem das Projekt liegt, ein. GroupId Hier sollte das Format com.example, com.example.domain, com.example.testsupport haben, oder auch als Link zu Ihrem GitHub: github.com/yourusername -> io.github.yourusername. In jedem Fall müssen Sie den Besitz dieser Domain oder Ihres Profils bestätigen. Wenn Sie ein GitHub-Profil angegeben haben, wird darum gebeten, ein öffentliches Repository mit dem entsprechenden Namen zu erstellen.

Nachdem Ihre Bestätigung erfolgt ist, wird Ihr GroupId erstellt und wir können mit dem nächsten Schritt, der Gradle-Konfiguration, fortfahren.

Gradle konfigurieren

Zum Zeitpunkt des Schreibens dieses Artikels konnte ich keine Plugins für Gradle finden, die bei der Veröffentlichung von Artefakten helfen könnten. Das Das einzige Plugin, das ich gefunden habe, wurde jedoch vom Autor nicht mehr unterstützt. Daher habe ich mich entschieden, alles selbst zu machen, da dies nicht allzu schwierig ist.

Das erste, was es herauszufinden gilt, sind die Anforderungen von Sonatype zur Veröffentlichung. Sie lauten wie folgt:

  • Vorhandensein von Quellcode und JavaDoc, d.h. es müssen vorhanden sein -sources.jar und-javadoc.jar Dateien. Wie in der Dokumentation angegeben, wenn es nicht möglich ist, den Quellcode oder die Dokumentation bereitzustellen, kann man eine Platzhalterdatei erstellen -sources.jar oder -javadoc.jar mit einer einfachen README-Datei, um die Überprüfung zu bestehen.
  • Alle Dateien müssen mit GPG/PGP, und .asc Datei, die die Signatur enthält, muss für jede Datei beigefügt werden.
  • Vorhandensein von pom Datei
  • Korrekte Werte für groupId, artifactId und version. Die Version kann eine beliebige Zeichenfolge sein und darf nicht mit -SNAPSHOT
  • enden. Es muss ein name, Beschreibung und url
  • vorhanden sein. Informationen über die Lizenz, Entwickler und das Versionskontrollsystem müssen angegeben werden.

Dies sind die grundlegenden Regeln, die beim Veröffentlichen beachtet werden müssen. Detaillierte Informationen sind verfügbar. hier.

Wir setzen diese Anforderungen in build.gradle der Datei um. Zunächst fügen wir alle erforderlichen Informationen über die Entwickler, die Lizenz, das Versionskontrollsystem hinzu und legen die URL, den Namen und die Beschreibung des Projekts fest. Dafür schreiben wir eine einfache Methode:

def customizePom(pom) {
    pom.withXml {
        def root = asNode()

        root.dependencies.removeAll { dep ->
            dep.scope == "test"
        }

        root.children().last() + {
            resolveStrategy = DELEGATE_FIRST

            description 'Eine Beschreibung des Artefakts'
            name 'Name des Artefakts'
            url 'https://github.com/login/projectname'
            organization {
                name 'com.github.login'
                url 'https://github.com/login'
            }
            issueManagement {
                system 'GitHub'
                url 'https://github.com/login/projectname/issues'
            }
            licenses {
                license {
                    name 'Die Apache-Lizenz, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            scm {
                url 'https://github.com/login/projectname'
                connection 'scm:https://github.com/login/projectname.git'
                developerConnection 'scm:git://github.com/login/projectname.git'
            }
            developers {
                developer {
                    id 'dev'
                    name 'DevName'
                    email 'email@dev.ru'
                }
            }
        }
    }
}

Anschließend müssen wir angeben, dass bei der Erstellung generiert werden soll. -sources.jar und-javadoc.jar Dateien. Dazu fügen Sie im Abschnitt java das Folgende hinzu:

java {
    withJavadocJar()
    withSourcesJar()
}

Kommen wir zur letzten Anforderung, der Konfiguration der GPG/PGP-Signatur. Dazu binden wir das Plugin signing:

plugins {
    id 'signing'
}

Und fügen Sie den Abschnitt hinzu:

signing {
    sign publishing.publications
}

Zum Schluss fügen wir den Abschnitt hinzu publishing:

publishing {
    publications {
        mavenJava(MavenPublication) {
            customizePom(pom)
            groupId group
            artifactId archivesBaseName
            version version

            from components.java
        }
    }
    repositories {
        maven {
            url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            credentials {
                username sonatypeUsername
                password sonatypePassword
            }
        }
    }
}

Hier sonatypeUsername und sonatypePassword Variablen, die den Benutzernamen und das Passwort enthalten, die bei der Registrierung auf sonatype.org.

Somit wird das finale build.gradle wie folgt aussehen:

Der vollständige Code build.gradle

plugins {
    id 'java'
    id 'maven-publish'
    id 'signing'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
    withJavadocJar()
    withSourcesJar()
}

group 'io.github.githublogin'
archivesBaseName = 'projektname'
version = System.getenv('RELEASE_VERSION') ?: "0.0.1"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
}

test {
    useJUnitPlatform()
}

jar {
    from sourceSets.main.output
    from sourceSets.main.allJava
}

signing {
    sign publishing.publications
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            customizePom(pom)
            groupId group
            artifactId archivesBaseName
            version version

            from components.java
        }
    }
    repositories {
        maven {
            url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            credentials {
                username sonatypeUsername
                password sonatypePassword
            }
        }
    }
}

def customizePom(pom) {
    pom.withXml {
        def root = asNode()

        root.dependencies.removeAll { dep ->
            dep.scope == "test"
        }

        root.children().last() + {
            resolveStrategy = DELEGATE_FIRST

            description 'Eine Beschreibung des Artefakts'
            name 'Artefaktnamen'
            url 'https://github.com/login/projektname'
            organization {
                name 'com.github.login'
                url 'https://github.com/githublogin'
            }
            issueManagement {
                system 'GitHub'
                url 'https://github.com/githublogin/projektname/issues'
            }
            licenses {
                license {
                    name 'Die Apache-Lizenz, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            scm {
                url 'https://github.com/githublogin/projektname'
                connection 'scm:https://github.com/githublogin/projektname.git'
                developerConnection 'scm:git://github.com/githublogin/projektname.git'
            }
            developers {
                developer {
                    id 'dev'
                    name 'EntwicklerName'
                    email 'email@dev.de'
                }
            }
        }
    }
}

Ich möchte darauf hinweisen, dass wir die Version aus der Umgebungsvariablen erhalten: System.getenv('RELEASE_VERSION'). Wir werden sie beim Build festlegen und aus dem Tag-Namen entnehmen.

Generierung eines PGP-Schlüssels

Eine der Anforderungen von Sonatype ist die Signierung aller Dateien mit einem GPG/PGP-Schlüssel. Dazu gehen wir hierhin und laden das GnuPG-Tool für Ihr Betriebssystem herunter.

  • Wir generieren ein Schlüsselpaar: gpg --gen-key, geben Benutzername, E-Mail und ein Passwort ein.
  • Wir ermitteln id unseren Schlüssel mit dem Befehl: gpg --list-secret-keys --keyid-format short. Die ID wird nach dem Schrägstrich angegeben, z. B.: rsa2048/9B695056
  • Wir veröffentlichen den öffentlichen Schlüssel auf dem Server https://keys.openpgp.org mit dem Befehl: gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • Wir exportieren den geheimen Schlüssel an einen beliebigen Ort, da wir ihn später benötigen: gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

Wir konfigurieren Github Actions

Kommen wir zum letzten Schritt, bei dem wir den Build und die automatische Veröffentlichung mit Github Actions einrichten.
Github Actions – eine Funktion, die es ermöglicht, den Arbeitsprozess zu automatisieren und einen vollständigen CI/CD-Zyklus zu realisieren. Das Bauen, Testen und Deployen kann durch verschiedene Ereignisse ausgelöst werden: Code-Push, Erstellung von Releases oder Issues. Diese Funktion ist völlig kostenlos für öffentliche Repositories.

In diesem Abschnitt zeige ich, wie man den Code-Build, das Pushen und das Deployment in ein Sonatype-Repository bei der Veröffentlichung einer Version einrichtet sowie die Konfiguration von Geheimnissen.

Geheimnisse festlegen

Für den automatischen Build und das Deployment benötigen wir eine Reihe von geheimen Werten, wie z. B. die Schlüssel-ID, das Passwort, das wir bei der Generierung des Schlüssels eingegeben haben, den PGP-Schlüssel selbst sowie das Login/Passwort für Sonatype. Diese können im speziellen Abschnitt der Repository-Einstellungen festgelegt werden:

Wir verwenden Gradle und Github Actions zur Veröffentlichung des Java-Projekts im Sonatype Maven Central Repository.

Die folgenden Variablen festlegen:

  • SONATYPE_USERNAME/SONATYPE_PASSWORD — Login/Passwort, das wir bei der Registrierung bei Sonatype eingegeben haben.
  • SIGNING_KEYID/SIGNING_PASSWORD — ID des PGP-Schlüssels und Passwort, das bei der Generierung festgelegt wurde.

Beim GPG_KEY_CONTENTS möchte ich etwas ausführlicher verweilen. Das Problem ist, dass wir für die Veröffentlichung den privaten PGP-Schlüssel benötigen. Um ihn in den Geheimnissen abzulegen, habe ich mich auf die Anleitung gestützt und zusätzlich einige Schritte unternommen.

  • Wir verschlüsseln unseren Schlüssel mit gpg: gpg --symmetric --cipher-algo AES256 9B695056.gpg, dabei geben wir das Passwort ein. Dieses sollte in die Variable: SECRET_PASSPHRASE eingefügt werden.
  • Wir konvertieren den erhaltenen verschlüsselten Schlüssel mit base64 in ein Textformat: base64 9B695056.gpg.gpg > 9B695056.txt. Den Inhalt platzieren wir in der Variable: GPG_KEY_CONTENTS.

Konfiguration des Builds bei Code-Pushes und Pull-Requests

Zunächst müssen Sie einen Ordner im Stammverzeichnis Ihres Projekts erstellen: .github/workflows.

Dort erstellen Sie eine Datei, zum Beispiel gradle-ci-build.yml mit folgendem Inhalt:

name: build

on:
  push:
    branches:
      - master
      - dev
      - testing
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: JDK 8 einrichten
        uses: actions/setup-java@v1
        with:
          java-version: 8

      - name: Mit Gradle bauen
        uses: eskatos/gradle-command-action@v1
        with:
          gradle-version: current
          arguments: build -PsonatypeUsername=${{secrets.SONATYPE_USERNAME}} -PsonatypePassword=${{secrets.SONATYPE_PASSWORD}}

Dieser Arbeitsablauf wird bei Pushes in die Branches master, dev und testingund auch bei der Erstellung von Pull-Requests ausgeführt.

Im Abschnitt jobs sind die Schritte aufgeführt, die bei den angegebenen Ereignissen ausgeführt werden sollen. In diesem Fall werden wir auf der neuesten Version von Ubuntu bauen, Java 8 verwenden und das Plugin für Gradle eskatos/gradle-command-action@v1, das mit der neuesten Version des Build-Tools die angegebenen Befehle ausführt, die in Argumente. Die Variablen secrets.SONATYPE_USERNAME und secrets.SONATYPE_PASSWORD sind die Geheimnisse, die wir zuvor festgelegt haben.

Die Ergebnisse des Builds werden im Tab Actions angezeigt:

Wir verwenden Gradle und Github Actions zur Veröffentlichung des Java-Projekts im Sonatype Maven Central Repository.

Automatisches Deployment bei Veröffentlichung eines neuen Releases

Für das automatische Deployment erstellen wir eine separate Datei für den Arbeitsablauf gradle-ci-publish.yml:

name: publish

on:
  push:
    tags:
      - 'v*'

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: JDK 8 einrichten
        uses: actions/setup-java@v1
        with:
          java-version: 8

      - name: Vorbereitung für die Veröffentlichung
        run: |
          echo '${{secrets.GPG_KEY_CONTENTS}}' | base64 -d > publish_key.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="${{secrets.SECRET_PASSPHRASE}}" 
          --output secret.gpg publish_key.gpg
          echo "::set-env name=RELEASE_VERSION::${GITHUB_REF:11}"

      - name: Mit Gradle veröffentlichen
        uses: eskatos/gradle-command-action@v1
        with:
          gradle-version: current
          arguments: test publish -Psigning.secretKeyRingFile=secret.gpg -Psigning.keyId=${{secrets.SIGNING_KEYID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -PsonatypeUsername=${{secrets.SONATYPE_USERNAME}} -PsonatypePassword=${{secrets.SONATYPE_PASSWORD}}

Die Datei ist nahezu identisch mit der vorherigen, abgesehen von dem Ereignis, das sie auslöst. In diesem Fall handelt es sich um das Ereignis, ein Tag mit einem Namen, der mit v beginnt, zu erstellen.

Vor dem Deployment müssen wir den PGP-Schlüssel aus den Geheimnissen abrufen und im Stammverzeichnis des Projekts ablegen sowie ihn entschlüsseln. Außerdem müssen wir eine spezielle Umgebungsvariable setzen. RELEASE_VERSION auf die wir in der gradle.build Datei zugreifen. All dies geschieht im Abschnitt Vorbereitung für die Veröffentlichung. Wir erhalten unseren Schlüssel aus der Variablen GPG_KEY_CONTENTS, konvertieren ihn in eine gpg-Datei, entschlüsseln ihn und legen ihn in die Datei secret.gpg.

In diesem Abschnitt wenden wir uns an die spezielle Variable GITHUB_REF, aus der wir die Version abrufen können, die wir bei der Erstellung des Tags festgelegt haben. In diesem Fall hat diese Variable den Wert refs/tags/v0.0.2 , von dem wir die ersten 11 Zeichen abschneiden, um die konkrete Version zu erhalten. Danach verwenden wir standardmäßig die Gradle-Befehle zur Veröffentlichung: test publish

Überprüfung der Ergebnisse des Deployments im Sonatype-Repository

Nach der Erstellung des Releases sollte der in der vorherigen Sektion beschriebene Workflow gestartet werden. Um dies zu tun, erstellen wir ein Release:

Wir verwenden Gradle und Github Actions zur Veröffentlichung des Java-Projekts im Sonatype Maven Central Repository.

wobei der Tag mit v beginnen muss. Wenn nach dem Klick auf "Publish release" der Workflow erfolgreich ausgeführt wird, können wir zu Sonatype Nexus gehen, um dies zu überprüfen:

Wir verwenden Gradle und Github Actions zur Veröffentlichung des Java-Projekts im Sonatype Maven Central Repository.

Das Artefakt erschien im Staging-Repository. Es erscheint sofort im Status "Open" und muss dann manuell in den Status "Close" überführt werden, indem die entsprechende Taste gedrückt wird. Nach der Überprüfung aller Anforderungen wird das Artefakt in den Status "Close" überführt und kann nicht mehr geändert werden. In diesem Zustand wird es in MavenCentral bereitgestellt. Wenn alles gut aussieht, können Sie die Taste Release, drücken, wodurch das Artefakt in das Sonatype-Repository gelangt.

Um ein Artefakt in MavenCentral zu bringen, müssen Sie einmalig eine Anfrage in der Aufgabe stellen, die wir zu Beginn erstellt haben. Das ist nur beim ersten Mal erforderlich, da wir zum ersten Mal veröffentlichen. Bei den folgenden Veröffentlichungen ist dies nicht nötig, alles wird automatisch synchronisiert. Die Synchronisierung wurde mir schnell ermöglicht, aber es dauerte etwa 5 Tage, bis das Artefakt in MavenCentral verfügbar war.

Das wars, wir haben unser Artefakt in MavenCentral veröffentlicht.

Nützliche Links

  • Ähnlich Artikel, nur die Veröffentlichung über Maven
  • Staging Repository Sonatype
  • Jira Sonatype, in dem eine Aufgabe erstellt werden muss
  • Beispiel Repository, in dem alles eingerichtet ist

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster