Gradle- en Github-acties gebruiken om Java-project te publiceren naar Sonatype Maven Central Repository

In dit artikel wil ik uitgebreid ingaan op het proces van het vanaf het begin publiceren van een Java-artefact via Github Actions naar de Sonatype Maven Central Repository met behulp van de Gradle-builder.

Ik besloot dit artikel te schrijven vanwege het ontbreken van een normale tutorial op één plek. Alle informatie moest stuk voor stuk uit verschillende bronnen worden verzameld, bovendien niet helemaal vers. Wat maakt het uit, welkom onder kat.

Een repository maken in Sonatype

De eerste stap is het maken van een repository in Sonatype Maven Central. Hiervoor gaan we hier, registreer en maak een nieuwe taak aan en vraag ons om een ​​repository te maken. We rijden in onze Groeps-ID projecteren, Project-URL projectlink en SCM-URL een koppeling naar het versiebeheersysteem waarin het project zich bevindt. Groeps-ID hier zou de vorm com.example, com.example.domain, com.example.testsupport moeten hebben, en kan ook de vorm hebben van een link naar je github: github.com/uwgebruikersnaam -> io.github.uwgebruikersnaam. In elk geval moet u het eigendom van dit domein of dit profiel verifiëren. Als je een github-profiel hebt opgegeven, wordt je gevraagd om een ​​openbare repository met de gewenste naam te maken.

Enige tijd na bevestiging wordt uw GroupId aangemaakt en kunnen we doorgaan naar de volgende stap, Gradle-configuratie.

Gradle configureren

Op het moment van schrijven heb ik geen Gradle-plug-ins gevonden die kunnen helpen bij het publiceren van het artefact. Het de enige plug-in die ik vond, maar de auteur weigerde deze verder te ondersteunen. Daarom besloot ik alles zelf te doen, aangezien het niet zo moeilijk is om dit te doen.

Het eerste dat u moet uitzoeken, zijn de publicatievereisten van Sonatype. Dit zijn de volgende:

  • Beschikbaarheid van broncodes en JavaDoc, dwz. moet aanwezig zijn -sources.jar и-javadoc.jar bestanden. Zoals vermeld in de documentatie, als het niet mogelijk is om broncodes of documentatie te verstrekken, kunt u een dummy maken -sources.jar of -javadoc.jar met een eenvoudige README erin om de test te halen.
  • Alle bestanden moeten worden ondertekend met GPG/PGPEn .asc het bestand met de handtekening moet voor elk bestand worden toegevoegd.
  • beschikbaarheid pom het dossier
  • Correcte waarden groupId, artifactId и version. De versie kan een willekeurige tekenreeks zijn en kan niet eindigen op -SNAPSHOT
  • Aanwezigheid vereist name, description и url
  • De aanwezigheid van informatie over de licentie, ontwikkelaars en het versiebeheersysteem

Dit zijn de basisregels die moeten worden gevolgd bij het publiceren. Volledige informatie beschikbaar hier.

We implementeren deze eisen in build.gradle bestand. Laten we eerst alle benodigde informatie over de ontwikkelaars, licenties, versiebeheersysteem toevoegen en ook de url, naam en beschrijving van het project instellen. Laten we hiervoor een eenvoudige methode schrijven:

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]'
                }
            }
        }
    }
}

Vervolgens moet u dat specificeren tijdens de gegenereerde assemblage -sources.jar и-javadoc.jar bestanden. Voor dit gedeelte java je moet het volgende toevoegen:

java {
    withJavadocJar()
    withSourcesJar()
}

Laten we verder gaan met de laatste vereiste, het instellen van een GPG/PGP-handtekening. Sluit hiervoor de plug-in aan signing:

plugins {
    id 'signing'
}

En voeg een sectie toe:

signing {
    sign publishing.publications
}

Laten we tot slot een sectie toevoegen 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 sonatypeGebruikersnaam и sonatypeWachtwoord variabelen die de gebruikersnaam en het wachtwoord bevatten die zijn gemaakt tijdens de registratie op sonatype.org.

Aldus de finale build.gradle zal er zo uitzien:

Volledige 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]'
                }
            }
        }
    }
}

Ik wil opmerken dat we de versie krijgen van de omgevingsvariabele: System.getenv('RELEASE_VERSION'). We zullen het tijdens de montage blootleggen en het uit de tagnaam halen.

Generatie van PGP-sleutels

Een van de vereisten van Sonatype is dat alle bestanden zijn ondertekend met een GPG/PGP-sleutel. Hiervoor gaan we hier en download het GnuPG-hulpprogramma voor uw besturingssysteem.

  • We genereren een sleutelpaar: gpg --gen-key, voer een gebruikersnaam en e-mailadres in en stel ook een wachtwoord in.
  • We komen er achter id onze sleutel met het commando: gpg --list-secret-keys --keyid-format short. Id wordt gespecificeerd na de schuine streep, bijvoorbeeld: rsa2048/9B695056
  • Publicatie van de openbare sleutel naar de server https://keys.openpgp.org met het commando: gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • We exporteren de geheime sleutel naar een willekeurige plaats, we zullen deze in de toekomst nodig hebben: gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

Github-acties instellen

Laten we doorgaan naar de laatste fase, de build instellen en automatisch publiceren met Github Actions.
Github Actions is een functie waarmee u de workflow kunt automatiseren door een volledige CI/CD-cyclus te implementeren. Het bouwen, testen en implementeren kan worden geactiveerd door verschillende gebeurtenissen: het pushen van code, het maken van een release of problemen. Deze functionaliteit is helemaal gratis voor openbare repositories.

In deze sectie laat ik je zien hoe je build- en pushcode instelt en implementeert in de Sonatype-repository bij release, en hoe je geheimen instelt.

We stellen geheimen

Voor automatische montage en implementatie hebben we een aantal geheime waarden nodig, zoals de sleutel-ID, het wachtwoord dat we hebben ingevoerd bij het genereren van de sleutel, de PGP-sleutel zelf en de Sonatype login/wachtwoord. Je kunt ze instellen in een speciaal gedeelte in de repository-instellingen:

Gradle- en Github-acties gebruiken om Java-project te publiceren naar Sonatype Maven Central Repository

We stellen de volgende variabelen in:

  • SONATYPE_USERNAME / SONATYPE_PASSWORD - login / wachtwoord dat we hebben ingevoerd bij het registreren bij Sonatype
  • SIGNING_KEYID/SIGNING_PASSWORD — PGP-sleutel-ID en wachtwoord ingesteld tijdens het genereren.

Ik wil dieper ingaan op de variabele GPG_KEY_CONTENTS. Feit is dat we voor publicatie een private PGP-sleutel nodig hebben. Om het in de geheimen te plaatsen, gebruikte ik instructies en voerde daarnaast een aantal acties uit.

  • Laten we onze sleutel coderen met gpg: gpg --symmetric --cipher-algo AES256 9B695056.gpgdoor een wachtwoord in te voeren. Het moet in een variabele worden geplaatst: SECRET_PASSPHRASE
  • Laten we de ontvangen versleutelde sleutel in een tekstvorm vertalen met behulp van base64: base64 9B695056.gpg.gpg > 9B695056.txt. De inhoud wordt geplaatst in de variabele: GPG_KEY_CONTENTS.

Bouw setup bij het pushen van code en het creëren van PR

Eerst moet u een map maken in de hoofdmap van uw project: .github/workflows.

Markeer daarin het bestand, bijvoorbeeld gradle-ci-build.yml met de volgende inhoud:

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}}

Deze workflow wordt uitgevoerd bij het pushen naar branches master, dev и testing, ook bij het maken van pull-aanvragen.

De sectie Jobs specificeert de stappen die moeten worden uitgevoerd op de opgegeven gebeurtenissen. In dit geval bouwen we voort op de nieuwste versie van ubuntu, gebruiken we Java 8 en gebruiken we ook de plug-in voor Gradle eskatos/gradle-command-action@v1die, met behulp van de nieuwste versie van de builder, de opdrachten zal uitvoeren die zijn gespecificeerd in arguments. Variabelen secrets.SONATYPE_USERNAME и secrets.SONATYPE_PASSWORD dit zijn de geheimen die we eerder hebben gevraagd.

De buildresultaten worden weergegeven op het tabblad Acties:

Gradle- en Github-acties gebruiken om Java-project te publiceren naar Sonatype Maven Central Repository

Automatisch implementeren wanneer een nieuwe release wordt uitgebracht

Laten we een apart werkstroombestand maken voor automatische implementatie 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}}

Het bestand is bijna identiek aan het vorige, behalve de gebeurtenis waarin het wordt geactiveerd. In dit geval is dit het geval waarbij een tag wordt gemaakt met een naam die begint met v.

Voor de implementatie moeten we de PGP-sleutel uit de geheimen halen en in de hoofdmap van het project plaatsen, en deze ook decoderen. Vervolgens moeten we een speciale omgevingsvariabele instellen RELEASE_VERSION waarnaar we verwijzen gradle.build bestand. Dit alles gebeurt in de sectie Prepare to publish. We halen onze sleutel uit de variabele GPG_KEY_CONTENTS, vertalen deze naar een gpg-bestand en decoderen deze vervolgens door deze in het bestand te plaatsen secret.gpg.

Vervolgens gaan we naar een speciale variabele GITHUB_REF, waaruit we de versie kunnen halen die we hebben ingesteld bij het maken van de tag. Deze variabele is in dit geval relevant. refs/tags/v0.0.2 waarvan we de eerste 11 tekens hebben afgesneden om een ​​specifieke versie te krijgen. Vervolgens gebruiken we de standaard Gradle-commando's voor publicatie: test publish

Implementatieresultaten controleren in de Sonatype-repository

Nadat de release is gemaakt, zou de workflow moeten starten die in de vorige sectie is beschreven. Maak hiervoor een release aan:

Gradle- en Github-acties gebruiken om Java-project te publiceren naar Sonatype Maven Central Repository

de tagnaam moet beginnen met v. Als de workflow na het klikken op Release publiceren is voltooid, kunnen we naar Sonatype Nexus om er zeker van te zijn:

Gradle- en Github-acties gebruiken om Java-project te publiceren naar Sonatype Maven Central Repository

Het artefact verscheen in de Staging-repository. Het verschijnt onmiddellijk in de Open-status, daarna moet het handmatig worden overgebracht naar de Close-status door op de juiste knop te drukken. Nadat is gecontroleerd of aan alle vereisten is voldaan, krijgt het artefact de status Sluiten en is het niet langer beschikbaar voor wijziging. In deze vorm komt het terecht in MavenCentral. Als het goed is, kun je op de knop drukken Sinds, en het artefact komt terecht in de Sonatype-repository.

Om het artefact in MavenCentral te krijgen, moet je erom vragen in de taak die we aan het begin hebben gemaakt. Je hoeft dit maar één keer te doen, dus we publiceren voor het eerst. In latere tijden is dit niet nodig, alles wordt automatisch gesynchroniseerd. Ze hebben de synchronisatie snel voor me ingeschakeld, maar het duurde ongeveer 5 dagen voordat het artefact beschikbaar kwam in MavenCentral.

Dat is alles, we hebben ons artefact gepubliceerd in MavenCentral.

Nuttige links

  • Vergelijkbaar artikel, alleen publiceren via maven
  • Regie opslagplaats sonatype
  • Jira Sonatype waarin de taak moet worden gemaakt
  • Voorbeeld repository waar het allemaal is ingesteld

Bron: www.habr.com