Verwenden von Gradle- und Github-Aktionen zum Veröffentlichen von Java-Projekten im Sonatype Maven Central Repository

In diesem Artikel möchte ich den Prozess der Veröffentlichung eines Java-Artefakts von Grund auf über Github Actions im Sonatype Maven Central Repository mithilfe des Gradle-Builders genauer betrachten.

Ich habe mich entschieden, diesen Artikel zu schreiben, da es an einer Stelle kein normales Tutorial gibt. Alle Informationen mussten Stück für Stück aus verschiedenen Quellen zusammengetragen werden und waren zudem nicht ganz aktuell. Wen kümmert's, willkommen unter Katze.

Erstellen eines Repositorys in Sonatype

Der erste Schritt besteht darin, ein Repository in Sonatype Maven Central zu erstellen. Dafür gehen wir hierher, registrieren Sie sich, erstellen Sie eine neue Aufgabe und bitten Sie uns, ein Repository zu erstellen. Wir fahren in unserem Groupid Projekt Projekt-URL Projektlink und SCM-URL ein Link zum Versionskontrollsystem, in dem sich das Projekt befindet. Groupid hier sollte die Form com.example, com.example.domain, com.example.testsupport haben und kann auch in Form eines Links zu Ihrem Github vorliegen: github.com/yourusername -> io.github.IhrBenutzername. In jedem Fall müssen Sie den Besitz dieser Domain oder dieses Profils bestätigen. Wenn Sie ein Github-Profil angegeben haben, werden Sie aufgefordert, ein öffentliches Repository mit dem gewünschten Namen zu erstellen.

Einige Zeit nach der Bestätigung wird Ihre GroupId erstellt und wir können mit dem nächsten Schritt, der Gradle-Konfiguration, fortfahren.

Gradle konfigurieren

Zum Zeitpunkt des Schreibens habe ich keine Gradle-Plugins gefunden, die bei der Veröffentlichung des Artefakts helfen könnten. Es Das einzige Plugin, das ich gefunden habe, weigerte sich der Autor jedoch, es weiter zu unterstützen. Deshalb habe ich beschlossen, alles selbst zu machen, da dies nicht allzu schwierig ist.

Das erste, was Sie herausfinden müssen, sind die Anforderungen von Sonatype für die Veröffentlichung. Dies sind die folgenden:

  • Verfügbarkeit von Quellcodes und JavaDoc, d. h. muss teilnehmen -sources.jar и-javadoc.jar Dateien. Wie in der Dokumentation angegeben, können Sie einen Dummy erstellen, wenn es nicht möglich ist, Quellcodes oder Dokumentation bereitzustellen -sources.jar oder -javadoc.jar mit einer einfachen README-Datei im Inneren, um den Test zu bestehen.
  • Alle Dateien müssen mit signiert sein GPG/PGPUnd .asc Für jede Datei muss die Datei mit der Signatur enthalten sein.
  • Verfügbarkeit pom Datei
  • Richtige Werte groupId, artifactId и version. Die Version kann eine beliebige Zeichenfolge sein und darf nicht mit enden -SNAPSHOT
  • Anwesenheit erforderlich name, description и url
  • Das Vorhandensein von Informationen über die Lizenz, die Entwickler und das Versionskontrollsystem

Dies sind die Grundregeln, die beim Veröffentlichen beachtet werden müssen. Vollständige Informationen verfügbar hier.

Wir setzen diese Anforderungen um build.gradle Datei. Fügen wir zunächst alle notwendigen Informationen zu den Entwicklern, Lizenzen und dem Versionskontrollsystem hinzu und legen außerdem die URL, den Namen und die Beschreibung des Projekts fest. Schreiben wir hierfür 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 'Some description of artifact'
            name 'Artifct name'
            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 'The Apache License, 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 protected]'
                }
            }
        }
    }
}

Als nächstes müssen Sie dies während der generierten Baugruppe angeben -sources.jar и-javadoc.jar Dateien. Für diesen Abschnitt java Sie müssen Folgendes hinzufügen:

java {
    withJavadocJar()
    withSourcesJar()
}

Kommen wir zur letzten Anforderung, dem Einrichten einer GPG/PGP-Signatur. Schließen Sie dazu das Plugin an signing:

plugins {
    id 'signing'
}

Und fügen Sie einen Abschnitt hinzu:

signing {
    sign publishing.publications
}

Zum Schluss fügen wir noch einen 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 sonatypeBenutzername и sonatypePassword Variablen, die den bei der Registrierung erstellten Benutzernamen und das Passwort enthalten sonatype.org.

Somit das Finale build.gradle wird so aussehen:

Vollständiger build.gradle-Code

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 = 'projectname'
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 'Some description of artifact'
            name 'Artifct name'
            url 'https://github.com/login/projectname'
            organization {
                name 'com.github.login'
                url 'https://github.com/githublogin'
            }
            issueManagement {
                system 'GitHub'
                url 'https://github.com/githublogin/projectname/issues'
            }
            licenses {
                license {
                    name 'The Apache License, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            scm {
                url 'https://github.com/githublogin/projectname'
                connection 'scm:https://github.com/githublogin/projectname.git'
                developerConnection 'scm:git://github.com/githublogin/projectname.git'
            }
            developers {
                developer {
                    id 'dev'
                    name 'DevName'
                    email '[email protected]'
                }
            }
        }
    }
}

Ich möchte darauf hinweisen, dass wir die Version aus der Umgebungsvariablen erhalten: System.getenv('RELEASE_VERSION'). Wir werden es während der Montage offenlegen und es aus dem Tag-Namen übernehmen.

Generierung von PGP-Schlüsseln

Eine der Anforderungen von Sonatype besteht darin, dass alle Dateien mit einem GPG/PGP-Schlüssel signiert sein müssen. Dafür gehen wir hierher und laden Sie das GnuPG-Dienstprogramm für Ihr Betriebssystem herunter.

  • Wir generieren ein Schlüsselpaar: gpg --gen-key, geben Sie einen Benutzernamen und eine E-Mail-Adresse ein und legen Sie auch ein Passwort fest.
  • Herausfinden id unser Schlüssel mit dem Befehl: gpg --list-secret-keys --keyid-format short. Die ID wird nach dem Schrägstrich angegeben, zum Beispiel: rsa2048/9B695056
  • Veröffentlichung des öffentlichen Schlüssels auf dem Server https://keys.openpgp.org per Befehl: gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • Wir exportieren den geheimen Schlüssel an einen beliebigen Ort, wir werden ihn in Zukunft benötigen: gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

Github-Aktionen einrichten

Fahren wir mit der letzten Phase fort und richten Sie den Build und die automatische Veröffentlichung mithilfe von Github-Aktionen ein.
Github Actions ist eine Funktion, mit der Sie den Workflow automatisieren können, indem Sie einen vollständigen CI/CD-Zyklus implementieren. Erstellen, Testen und Bereitstellen können durch verschiedene Ereignisse ausgelöst werden: Code-Push, Release-Erstellung oder Probleme. Diese Funktionalität ist für öffentliche Repositories völlig kostenlos.

In diesem Abschnitt zeige ich Ihnen, wie Sie Build- und Push-Code einrichten und bei der Veröffentlichung im Sonatype-Repository bereitstellen sowie Geheimnisse einrichten.

Wir legen Geheimnisse fest

Für die automatische Zusammenstellung und Bereitstellung benötigen wir eine Reihe geheimer Werte, wie z. B. die Schlüssel-ID, das Passwort, das wir beim Generieren des Schlüssels eingegeben haben, den PGP-Schlüssel selbst und den Sonatype-Anmeldenamen/das Sonatype-Passwort. Sie können sie in einem speziellen Abschnitt in den Repository-Einstellungen festlegen:

Verwenden von Gradle- und Github-Aktionen zum Veröffentlichen von Java-Projekten im Sonatype Maven Central Repository

Wir setzen folgende Variablen:

  • SONATYPE_USERNAME / SONATYPE_PASSWORD – Login/Passwort, das wir bei der Registrierung bei Sonatype eingegeben haben
  • SIGNING_KEYID/SIGNING_PASSWORD – PGP-Schlüssel-ID und Passwort, die während der Generierung festgelegt wurden.

Ich möchte näher auf die Variable GPG_KEY_CONTENTS eingehen. Tatsache ist, dass wir für die Veröffentlichung einen privaten PGP-Schlüssel benötigen. Um es in den Geheimnissen zu posten, habe ich verwendet Anweisung und zusätzlich eine Reihe von Aktionen durchgeführt.

  • Verschlüsseln wir unseren Schlüssel mit gpg: gpg --symmetric --cipher-algo AES256 9B695056.gpgdurch Eingabe eines Passwortes. Es sollte in einer Variablen platziert werden: SECRET_PASSPHRASE
  • Lassen Sie uns den empfangenen verschlüsselten Schlüssel mithilfe von base64 in eine Textform übersetzen: base64 9B695056.gpg.gpg > 9B695056.txt. Der Inhalt wird in der Variablen GPG_KEY_CONTENTS platziert.

Build-Setup beim Pushen von Code und Erstellen von PR

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

Markieren Sie darin die Datei, z. B. 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: Set up JDK 8
        uses: actions/setup-java@v1
        with:
          java-version: 8

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

Dieser Workflow wird beim Pushen an Zweige ausgeführt master, dev и testing, auch beim Erstellen von Pull-Requests.

Der Abschnitt „Jobs“ gibt die Schritte an, die bei den angegebenen Ereignissen ausgeführt werden sollen. In diesem Fall werden wir auf der neuesten Version von Ubuntu aufbauen, Java 8 verwenden und auch das Plugin für Gradle verwenden eskatos/gradle-command-action@v1der unter Verwendung der neuesten Version des Builders die in angegebenen Befehle ausführt arguments. Variablen secrets.SONATYPE_USERNAME и secrets.SONATYPE_PASSWORD Das sind die Geheimnisse, nach denen wir vorhin gefragt haben.

Die Build-Ergebnisse werden auf der Registerkarte „Aktionen“ angezeigt:

Verwenden von Gradle- und Github-Aktionen zum Veröffentlichen von Java-Projekten im Sonatype Maven Central Repository

Automatische Bereitstellung, wenn eine neue Version veröffentlicht wird

Erstellen wir eine separate Workflowdatei für die automatische Bereitstellung gradle-ci-publish.yml:

name: publish

on:
  push:
    tags:
      - 'v*'

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

      - name: Prepare to publish
        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: Publish with Gradle
        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 bis auf das Ereignis, bei dem sie ausgelöst wird, fast identisch mit der vorherigen. In diesem Fall handelt es sich um das Ereignis, bei dem ein Tag erstellt wird, dessen Name mit v beginnt.

Vor der Bereitstellung müssen wir den PGP-Schlüssel aus den Geheimnissen extrahieren, ihn im Stammverzeichnis des Projekts ablegen und ihn entschlüsseln. Als nächstes müssen wir eine spezielle Umgebungsvariable festlegen RELEASE_VERSION worauf wir uns beziehen gradle.build Datei. All dies geschieht im Abschnitt Prepare to publish. Wir erhalten unseren Schlüssel aus der Variablen GPG_KEY_CONTENTS, übersetzen ihn in eine GPG-Datei und entschlüsseln ihn dann, indem wir ihn in die Datei einfügen secret.gpg.

Als nächstes wenden wir uns einer speziellen Variablen zu GITHUB_REF, von dem wir die Version erhalten können, die wir beim Erstellen des Tags festgelegt haben. Diese Variable ist in diesem Fall relevant. refs/tags/v0.0.2 von dem wir die ersten 11 Zeichen abgeschnitten haben, um eine spezifische Version zu erhalten. Als nächstes verwenden wir die Standard-Gradle-Befehle zum Veröffentlichen: test publish

Überprüfung der Bereitstellungsergebnisse im Sonatype-Repository

Sobald das Release erstellt ist, sollte der im vorherigen Abschnitt beschriebene Workflow beginnen. Erstellen Sie dazu ein Release:

Verwenden von Gradle- und Github-Aktionen zum Veröffentlichen von Java-Projekten im Sonatype Maven Central Repository

Der Tag-Name muss mit v beginnen. Wenn der Workflow nach dem Klicken auf „Veröffentlichung veröffentlichen“ erfolgreich abgeschlossen wird, können wir mit fortfahren Sonatyp Nexus sichergehen:

Verwenden von Gradle- und Github-Aktionen zum Veröffentlichen von Java-Projekten im Sonatype Maven Central Repository

Das Artefakt wurde im Staging-Repository angezeigt. Es erscheint sofort im Status „Offen“, anschließend muss es manuell durch Drücken der entsprechenden Schaltfläche in den Status „Geschlossen“ überführt werden. Nachdem überprüft wurde, dass alle Anforderungen erfüllt sind, wechselt das Artefakt in den Status „Geschlossen“ und steht nicht mehr zur Änderung zur Verfügung. In dieser Form landet es in MavenCentral. Wenn alles in Ordnung ist, können Sie den Knopf drücken Loslassen, und das Artefakt landet im Sonatype-Repository.

Damit das Artefakt in MavenCentral gelangt, müssen Sie es in der Aufgabe anfordern, die wir ganz am Anfang erstellt haben. Da Sie dies nur einmal tun müssen, veröffentlichen wir es zum ersten Mal. In späteren Zeiten ist dies nicht mehr erforderlich, alles wird automatisch synchronisiert. Sie haben die Synchronisierung schnell für mich aktiviert, aber es dauerte etwa fünf Tage, bis das Artefakt in MavenCentral verfügbar war.

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

Nützliche Links

  • Ähnlich Beitrag, nur über Maven veröffentlichen
  • Staging Repository Sonatyp
  • Jira Sonatype, in dem die Aufgabe erstellt werden soll
  • Beispiel Repository, in dem alles eingerichtet ist

Source: habr.com