Utilizarea acțiunilor Gradle și Github pentru a publica proiectul Java în depozitul central Sonatype Maven

În acest articol, vreau să arunc o privire detaliată asupra procesului de publicare a unui artefact Java de la zero prin Github Actions în Repository central Sonatype Maven folosind constructorul Gradle.

Am decis să scriu acest articol din cauza lipsei unui tutorial normal într-un singur loc. Toate informațiile trebuiau adunate bucată cu bucată din diverse surse, în plus, nu în totalitate proaspete. Cui îi pasă, bine ai venit sub pisica.

Crearea unui depozit în Sonatype

Primul pas este să creați un depozit în Sonatype Maven Central. Pentru asta mergem aici, înregistrați-vă și creați o nouă sarcină, solicitându-ne să creăm un depozit. Conducem în noul nostru GroupId proiect, Adresa URL a proiectului link de proiect și URL SCM o legătură către sistemul de control al versiunilor în care se află proiectul. GroupId aici ar trebui să aibă forma com.example, com.example.domain, com.example.testsupport și poate fi, de asemenea, sub forma unui link către github: github.com/numele dvs. de utilizator -> io.github.youruusername. În orice caz, va trebui să verificați calitatea de proprietar al acestui domeniu sau profil. Dacă ați specificat un profil github, vi se va cere să creați un depozit public cu numele dorit.

La ceva timp după confirmare, GroupId-ul tău va fi creat și putem trece la pasul următor, configurarea Gradle.

Configurarea Gradle

La momentul scrierii, nu am găsit pluginuri Gradle care ar putea ajuta la publicarea artefactului. Aceasta singurul plugin pe care l-am găsit, totuși, autorul a refuzat să-l susțină în continuare. Prin urmare, am decis să fac totul singur, deoarece nu este prea dificil să fac asta.

Primul lucru de care trebuie să vă dați seama sunt cerințele Sonatype pentru publicare. Acestea sunt următoarele:

  • Disponibilitatea codurilor sursă și JavaDoc, de ex. Trebuie să participe -sources.jar и-javadoc.jar fișiere. După cum se menționează în documentație, dacă nu este posibil să furnizați coduri sursă sau documentație, puteți crea un fals -sources.jar sau -javadoc.jar cu un simplu README în interior pentru a trece testul.
  • Toate fișierele trebuie să fie semnate cu GPG/PGPși .asc pentru fiecare dosar trebuie inclus dosarul care contine semnatura.
  • disponibilitate pom fişier
  • Valori corecte groupId, artifactId и version. Versiunea poate fi un șir arbitrar și nu se poate termina cu -SNAPSHOT
  • Prezența necesară name, description и url
  • Prezența informațiilor despre licență, dezvoltatori și sistemul de control al versiunilor

Acestea sunt regulile de bază care trebuie respectate la publicare. Informații complete disponibile aici.

Implementăm aceste cerințe în build.gradle fişier. Mai întâi, să adăugăm toate informațiile necesare despre dezvoltatori, licențe, sistemul de control al versiunilor și, de asemenea, să setăm adresa URL, numele și descrierea proiectului. Să scriem o metodă simplă pentru asta:

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

Apoi, trebuie să specificați asta în timpul ansamblului generat -sources.jar и-javadoc.jar fișiere. Pentru aceasta sectiune java trebuie să adăugați următoarele:

java {
    withJavadocJar()
    withSourcesJar()
}

Să trecem la ultima cerință, configurarea unei semnături GPG/PGP. Pentru a face acest lucru, conectați pluginul signing:

plugins {
    id 'signing'
}

Și adăugați o secțiune:

signing {
    sign publishing.publications
}

În sfârșit, să adăugăm o secțiune 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
            }
        }
    }
}

Aici sonatypeNume de utilizator и sonatypePassword variabile care conțin autentificarea și parola create în timpul înregistrării pe sonatype.org.

Astfel finala build.gradle va arata asa:

Cod complet 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 = '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]'
                }
            }
        }
    }
}

Vreau să notez că obținem versiunea din variabila de mediu: System.getenv('RELEASE_VERSION'). Îl vom expune în timpul asamblarii și îl vom lua din numele etichetei.

Generarea cheii PGP

Una dintre cerințele Sonatype este ca toate fișierele să fie semnate cu o cheie GPG/PGP. Pentru asta mergem aici și descărcați utilitarul GnuPG pentru sistemul dvs. de operare.

  • Generam o pereche de chei: gpg --gen-key, introduceți un nume de utilizator, un e-mail și, de asemenea, setați o parolă.
  • Aflăm id cheia noastră cu comanda: gpg --list-secret-keys --keyid-format short. Id-ul va fi specificat după bară oblică, de exemplu: rsa2048/9B695056
  • Publicarea cheii publice pe server https://keys.openpgp.org comanda: gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • Exportăm cheia secretă într-un loc arbitrar, vom avea nevoie de ea în viitor: gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

Configurarea acțiunilor Github

Să trecem la etapa finală, să setăm construirea și publicarea automată folosind Github Actions.
Github Actions este o caracteristică care vă permite să automatizați fluxul de lucru prin implementarea unui ciclu complet CI / CD. Construirea, testarea și implementarea pot fi declanșate de diverse evenimente: introducerea codului, crearea lansării sau probleme. Această funcționalitate este absolut gratuită pentru depozitele publice.

În această secțiune, vă voi arăta cum să configurați codul de compilare și push și să implementați în depozitul Sonatype la lansare, precum și să configurați secretele.

Stabilim secrete

Pentru asamblarea și implementarea automată, avem nevoie de o serie de valori secrete, cum ar fi ID-ul cheii, parola pe care am introdus-o la generarea cheii, cheia PGP în sine și autentificarea/parola Sonatype. Le puteți seta într-o secțiune specială din setările depozitului:

Utilizarea acțiunilor Gradle și Github pentru a publica proiectul Java în depozitul central Sonatype Maven

Setăm următoarele variabile:

  • SONATYPE_USERNAME / SONATYPE_PASSWORD - login / parola pe care am introdus-o la înregistrarea cu Sonatype
  • SIGNING_KEYID/SIGNING_PASSWORD — ID-ul cheii PGP și parola setate în timpul generării.

Vreau să mă opresc asupra variabilei GPG_KEY_CONTENTS mai detaliat. Faptul este că pentru publicare avem nevoie de o cheie PGP privată. Pentru a-l posta în secrete, am folosit instrucțiuni și în plus a făcut o serie de acțiuni.

  • Să ne criptăm cheia cu gpg: gpg --symmetric --cipher-algo AES256 9B695056.gpgprin introducerea unei parole. Ar trebui plasat într-o variabilă: SECRET_PASSPHRASE
  • Să traducem cheia criptată primită într-o formă text folosind base64: base64 9B695056.gpg.gpg > 9B695056.txt. Conținutul va fi plasat în variabila: GPG_KEY_CONTENTS.

Construiți configurarea când împingeți codul și creați PR

Mai întâi trebuie să creați un folder în rădăcina proiectului dvs.: .github/workflows.

În el, marcați fișierul, de exemplu, gradle-ci-build.yml cu urmatorul continut:

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

Acest flux de lucru va fi executat la împingerea către ramuri master, dev и testing, de asemenea la crearea cererilor de extragere.

Secțiunea de joburi specifică pașii care trebuie executați pentru evenimentele specificate. În acest caz, vom construi pe cea mai recentă versiune de ubuntu, vom folosi Java 8 și, de asemenea, vom folosi pluginul pentru Gradle eskatos/gradle-command-action@v1care, folosind cea mai recentă versiune a constructorului, va rula comenzile specificate în arguments. Variabile secrets.SONATYPE_USERNAME и secrets.SONATYPE_PASSWORD acestea sunt secretele pe care le-am întrebat mai devreme.

Rezultatele construcției vor fi reflectate în fila Acțiuni:

Utilizarea acțiunilor Gradle și Github pentru a publica proiectul Java în depozitul central Sonatype Maven

Implementează automat când este lansată o nouă versiune

Să creăm un fișier de flux de lucru separat pentru implementare automată 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}}

Fișierul este aproape identic cu cel precedent, cu excepția evenimentului în care va fi declanșat. În acest caz, acesta este evenimentul creării unei etichete cu un nume care începe cu v.

Înainte de implementare, trebuie să extragem cheia PGP din secrete și să o plasăm în rădăcina proiectului, precum și să o decriptăm. Apoi, trebuie să setăm o variabilă de mediu specială RELEASE_VERSION la care ne referim gradle.build fişier. Toate acestea se fac în secțiune Prepare to publish. Ne obținem cheia din variabila GPG_KEY_CONTENTS, o traducem într-un fișier gpg, apoi o decriptăm introducând-o în fișier secret.gpg.

În continuare, ne întoarcem la o variabilă specială GITHUB_REF, de la care putem obține versiunea pe care am setat-o ​​la crearea etichetei. Această variabilă este relevantă în acest caz. refs/tags/v0.0.2 din care tăiem primele 11 caractere pentru a obține o versiune specifică. În continuare, folosim comenzile Gradle standard pentru publicare: test publish

Verificarea rezultatelor implementării în depozitul Sonatype

După crearea ediției, fluxul de lucru descris în secțiunea anterioară ar trebui să înceapă. Pentru a face acest lucru, creați o versiune:

Utilizarea acțiunilor Gradle și Github pentru a publica proiectul Java în depozitul central Sonatype Maven

numele etichetei trebuie să înceapă cu v. Dacă, după ce facem clic pe Publicare versiune, fluxul de lucru se încheie cu succes, putem accesa Sonatype Nexus pentru a te asigura:

Utilizarea acțiunilor Gradle și Github pentru a publica proiectul Java în depozitul central Sonatype Maven

Artefactul a apărut în depozitul Staging. Apare imediat în starea Deschis, apoi trebuie transferat manual în starea Închidere apăsând butonul corespunzător. După ce a verificat dacă toate cerințele sunt îndeplinite, artefactul intră în starea Închidere și nu mai este disponibil pentru modificare. În această formă, va ajunge în MavenCentral. Dacă totul este bine, puteți apăsa butonul Eliberați, iar artefactul va ajunge în depozitul Sonatype.

Pentru ca artefactul să intre în MavenCentral, trebuie să îl solicitați în sarcina pe care am creat-o la început. Trebuie să faceți acest lucru o singură dată, așa că publicăm pentru prima dată. În perioadele ulterioare, acest lucru nu este necesar, totul va fi sincronizat automat. Mi-au activat sincronizarea rapid, dar a durat aproximativ 5 zile pentru ca artefactul să devină disponibil în MavenCentral.

Asta e tot, am publicat artefactul nostru în MavenCentral.

Link-uri utile

  • Similar articol, publică doar prin maven
  • Înscenare repertoriu Sonatip
  • JIRA Sonatip în care să creați sarcina
  • Exemplu depozitul în care este configurat totul

Sursa: www.habr.com