I denna artikel vill jag detaljerat gÄ igenom processen för publicering frÄn grunden av en Java-artifact via Github Actions i Sonatype Maven Central Repository med hjÀlp av Gradle.
Jag beslutade mig för att skriva denna artikel pÄ grund av bristen pÄ en ordentlig handledning pÄ ett stÀlle. All information fick jag samla in bit för bit frÄn olika kÀllor, som dessutom inte var helt aktuella. Om nÄgon Àr intresserad, varmt vÀlkommen under kat.
Skapande av repository i Sonatype
Som första steg mÄste vi skapa ett repository i Sonatype Maven Central. För att göra detta gÄr vi till , registrerar oss och skapar en ny uppgift, dÀr vi ber om att fÄ ett repository skapat. Vi anger vÄr GroupId projektet, Projekt-URL lÀnken till projektet och SCM-url lÀnken till versionshanteringssystemet dÀr projektet ligger. GroupId hÀr bör vara i formatet com.example, com.example.domain, com.example.testsupport, och kan ocksÄ vara en lÀnk till din Github: -> io.github.yourusername. I vilket fall som helst behöver du bekrÀfta Àgande av denna domÀn eller profil. Om du angav din Github-profil kommer de att be dig skapa ett offentligt repository med det önskade namnet.
Efter en tid, efter bekrÀftelse, kommer din GroupId att skapas och vi kan gÄ vidare till nÀsta steg, konfiguration av Gradle.
Konfigurera Gradle
Vid tidpunkten för denna artikels skrivande fann jag inga plugins för Gradle som kunde hjÀlpa till med publiceringen av artifactet. Den enda plugin jag hittade, men författaren har avsagt sig vidare support. DÀrför beslutade jag mig för att göra allt sjÀlv, tack och lov Àr det inte alltför svÄrt.
Det första som behöver klargöras Àr kraven frÄn Sonatype för publicering. De Àr följande:
- NÀrvaro av kÀllkoder och JavaDoc, d.v.s. de mÄste finnas
-sources.jaroch-javadoc.jarfiler. Som det anges i dokumentationen, om det inte Àr möjligt att tillhandahÄlla kÀllkoder eller dokumentation, kan man göra en dummy-sources.jareller-javadoc.jarmed en enkel README inuti för att klara av inspektionen. - Alla filer mÄste signeras med hjÀlp av
GPG/PGP, och.ascfilen som innehÄller signaturen mÄste inkluderas för varje fil. - NÀrvaron av
pomfilen - Korrekt vÀrden
groupId,artifactIdochversion. Versionen kan vara en godtycklig strÀng och fÄr inte sluta med-SNAPSHOT - Det mÄste finnas
name,descriptionochurl - NĂ€rvaro av information om licens, utvecklare och versionshanteringssystem
Detta Àr de grundlÀggande regler som mÄste följas vid publicering. FullstÀndig information finns tillgÀnglig .
Vi genomför dessa krav i build.gradle filen. För att börja lÀgger vi till all nödvÀndig information om utvecklarna, licensen, versionshanteringssystemet, samt stÀller in url, namn och beskrivning av projektet. För detta ska vi skriva en enkel metod:
def customizePom(pom) {
pom.withXml {
def root = asNode()
root.dependencies.removeAll { dep ->
dep.scope == "test"
}
root.children().last() + {
resolveStrategy = DELEGATE_FIRST
description 'NÄgon beskrivning av artefakten'
name 'Artefaktnamn'
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@dev.ru'
}
}
}
}
}DÀrefter behöver vi ange att filer ska genereras vid byggandet -sources.jar och-javadoc.jar filer. För detta lÀgger vi till följande i sektionen java vi behöver lÀgga till följande:
java {
withJavadocJar()
withSourcesJar()
}LÄt oss gÄ vidare till det sista kravet, konfigurationen av GPG/PGP signaturen. För detta ansluter vi till pluginen signing:
plugins {
id 'signing'
}Och lÀgger till sektionen:
signing {
sign publishing.publications
}Slutligen lÀgger vi till sektionen 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
}
}
}
}HÀr sonatypeUsername och sonatypePassword variabler som innehÄller inloggning och lösenord som skapades vid registreringen pÄ .
SÄledes kommer den slutgiltiga build.gradle att se ut pÄ följande sÀtt:
Hela koden 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 'NÄgon beskrivning av artefakten'
name 'Artefaktnamn'
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@dev.ru'
}
}
}
}
}Jag vill pÄpeka att vi fÄr versionen frÄn miljövariabeln: System.getenv('RELEASE_VERSION'). Vi kommer att stÀlla in den vid byggandet och hÀmta den frÄn taggens namn.
Generering av PGP-nyckel
Ett av kraven frÄn Sonatype Àr att signera alla filer med GPG/PGP-nyckeln. För detta gÄr vi och laddar ner GnuPG-verktyget för vÄrt operativsystem.
- Vi genererar ett nyckelpar:
gpg --gen-key, ange anvÀndarnamn, e-post och stÀll Àven in ett lösenord. - Vi tar reda pÄ
idvÄr nyckel med kommandot:gpg --list-secret-keys --keyid-format short. Id kommer att anges efter snedstrecket, till exempel: rsa2048/9B695056 - Publicera den offentliga nyckeln pÄ servern med kommandot:
gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056 - Exportera den hemliga nyckeln till en valfri plats, vi kommer att behöva den senare:
gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg
Konfigurera Github Actions
LÄt oss gÄ vidare till den avslutande fasen, vi konfigurerar byggandet och automatisk publicering med hjÀlp av Github Actions.
Github Actions â en funktionalitet som möjliggör automatisering av arbetsflödet genom att implementera hela CI/CD-cykeln. Byggande, testning och distribution kan utlösas av olika hĂ€ndelser: kodpush, skapande av releaser eller issues. Denna funktionalitet Ă€r helt gratis för offentliga repositorier.
I det hÀr avsnittet visar jag hur man stÀller in bygg och kodpush samt distribution till Sonatype-repositoriet vid release, samt hur man hanterar hemligheter.
Ange hemligheter
För automatisk bygg och distribution behöver vi en rad hemliga vÀrden, sÄsom nyckel-ID, lösenord som vi angav vid nyckelgenerering, sjÀlva PGP-nyckeln samt anvÀndarnamn/lösenord till Sonatype. Dessa kan definieras i ett sÀrskilt avsnitt i repositoryinstÀllningarna:

Ange följande variabler:
- SONATYPE_USERNAME/SONATYPE_PASSWORD â anvĂ€ndarnamn/lösenord som vi angav vid registreringen i Sonatype
- SIGNING_KEYID/SIGNING_PASSWORD â ID för PGP-nyckeln och lösenordet som stĂ€lldes in vid generering.
Jag vill stanna vid variabeln GPG_KEY_CONTENTS och gÄ in pÄ den lite mer. Problemet Àr att vi behöver den privata PGP-nyckeln för publicering. För att placera den i hemligheterna anvÀnde jag och utförde dessutom en rad ÄtgÀrder.
- Vi krypterar vÄr nyckel med hjÀlp av gpg:
gpg --symmetric --cipher-algo AES256 9B695056.gpg, ange lösenordet. Det bör placeras i variabeln: SECRET_PASSPHRASE - ĂversĂ€tt den erhĂ„llna krypterade nyckeln till textformat med hjĂ€lp av base64:
base64 9B695056.gpg.gpg > 9B695056.txt. InnehÄllet lÀggs i variabeln: GPG_KEY_CONTENTS.
InstÀllning av bygg vid kodpush och skapande av PR
Först behöver du skapa en mapp i rotkatalogen av ditt projekt: .github/workflows.
I den placerar du en fil, till exempel, gradle-ci-build.yml med följande innehÄll:
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}}Denna arbetsprocess kommer att köras vid kodpush till grenarna master, dev och testing, samt vid skapande av pull requests.
I avsnittet jobs anges stegen som ska utföras vid angivna hÀndelser. I det hÀr fallet kommer vi att bygga pÄ den senaste versionen av ubuntu, anvÀnda Java 8 och Àven anvÀnda pluginet för Gradle eskatos/gradle-command-action@v1, som, med den senaste versionen av byggverktyget, kommer att köra kommandona som angivits i argument. Variabler secrets.SONATYPE_USERNAME och secrets.SONATYPE_PASSWORD det hÀr Àr hemligheter som vi angav tidigare.
Byggresultaten kommer att Äterspeglas i fliken Actions:

Automatisk distribution vid utgivning av en ny release
För automatisk distribution kommer vi att skapa en separat arbetsflödesfil gradle-ci-publish.yml:
name: publish
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: StÀll in JDK 8
uses: actions/setup-java@v1
with:
java-version: 8
- name: Förbered för publicering
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: Publicera med 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}}Denna fil Àr praktiskt taget identisk med den föregÄende förutom hÀndelsen som den kommer att utlösas av. I detta fall Àr det en hÀndelse av taggskapande med ett namn som börjar med v.
Innan distributionen mÄste vi hÀmta PGP-nyckeln frÄn hemligheterna och placera den i projektets rot samt avkryptera den. DÀrefter behöver vi stÀlla in en speciell miljövariabel RELEASE_VERSION som vi hÀnvisar till i gradle.build filen. Allt detta görs i avsnittet Förbered för publicering. Vi hÀmtar vÄr nyckel frÄn variabeln GPG_KEY_CONTENTS, konverterar den till en gpg-fil, och avkrypterar den och placerar den i filen secret.gpg.
DÀrefter hÀnvisar vi till en speciell variabel GITHUB_REF, frÄn vilken vi kan hÀmta versionen vi angav nÀr vi skapade taggen. Denna variabel har i det hÀr fallet vÀrdet refs/tags/v0.0.2 frÄn vilken vi klipper bort de första 11 tecknen för att fÄ den faktiska versionen. DÀrefter anvÀnder vi standardkommandon för Gradle för publicering: test publish
Kontrollera resultaten av distributionen till Sonatype-repositoriet
Efter att ha skapat en release bör arbetsflödet som beskrivs i föregÄende avsnitt starta. För att göra detta skapar vi en release:

namnet pÄ taggen mÄste börja med v. Om arbetsflödet lyckas efter att du har klickat pÄ Publicera release, kan vi gÄ till för att kontrollera detta:

Artefakten har dykt upp i Staging-arkivet. Den visas omedelbart med status Ăppen och mĂ„ste sedan manuellt Ă€ndras till status StĂ€ngd genom att trycka pĂ„ motsvarande knapp. Efter att alla krav har verifierats kommer artefakten att övergĂ„ till status StĂ€ngd och kan inte lĂ€ngre Ă€ndras. I den formen kommer den att hamna i MavenCentral. Om allt ser bra ut kan man trycka pĂ„ knappen Release, varvid artefakten hamnar i Sonatype-arkivet.
För att artefakten ska hamna i MavenCentral mÄste man be om detta i uppgiften som vi skapade i början. Detta behöver göras endast en gÄng, eftersom vi publicerar för första gÄngen. Vid senare tillfÀllen behövs detta inte, allt kommer att synkroniseras automatiskt. Jag fick snabbt pÄ plats synkroniseringen, men det tog ungefÀr 5 dagar innan artefakten blev tillgÀnglig i MavenCentral.
Det Àr allt, vi har publicerat vÄr artefakt i MavenCentral.
Nyttiga lÀnkar
- Liknande , men endast publicering via maven
- Staging Sonatype
- Sonatype, dÀr en uppgift mÄste skapas
- arkivet dÀr allt detta Àr konfigurerat
KĂ€lla: habr.com
