U ovom članku želim detaljno razmotriti proces objavljivanja Java artefakta putem Github Actions u Sonatype Maven Central Repository koristeći Gradle.
Ovaj članak sam odlučio napisati zbog nedostatka kvalitetnog tutoriala na jednom mestu. Svaku informaciju bilo je potrebno prikupljati delić po delić iz raznih izvora, pri čemu nisu svi bili posve aktuelni. Kome je zanimljivo, dobrodošli ispod.
Kreiranje repozitorija u Sonatype
Prvi korak je da kreiramo repozitorij u Sonatype Maven Central. Za to idemo , registrujemo se i kreiramo novi zahtev, sa molbom da nam naprave repozitorij. Upisujemo svoj GroupId projekta, Project URL link na projekat i SCM url link na sistem kontrole verzija u kojem se projekat nalazi. GroupId ovde treba biti u obliku com.example, com.example.domain, com.example.testsupport, ali može biti i kao link na vaš GitHub: -> io.github.yourusername. U svakom slučaju, potrebno je da potvrdite vlasništvo nad tim domenom ili profilom. Ako ste navedli GitHub profil, zatražiće da kreirate javni repozitorij sa potrebnim imenom.
Nakon nekog vremena, nakon potvrde, vaš GroupId će biti kreiran i možemo preći na sledeći korak, konfiguraciju Gradle-a.
Konfiguriramo Gradle
U trenutku pisanja ovog članka nisam našao plugine za Gradle koji bi mogli pomoći s objavljivanjem artefakta. jedini plugin koji sam našao, međutim, autor je odustao od daljnje podrške. Zato sam odlučio da sve uradim sam, dobro je da to nije previše teško.
Prvo što treba utvrditi su zahtevi Sonatype za objavljivanje. Oni su sledeći:
- Postojanje izvornih kodova i JavaDoc, tj. treba da postoje
-sources.jari-javadoc.jardatoteke. Kao što je navedeno u dokumentaciji, ako nije moguće dostaviti izvornik ili dokumentaciju, može se napraviti prazan fajl-sources.jarili-javadoc.jars jednostavnom README unutar, kako bi se prošlo provere. - Sve datoteke moraju biti potpisane pomoću
GPG/PGP, i.ascfajl, koji sadrži potpis, mora biti uključen za svaku datoteku. - Prisutnost
pomdatoteke - Ispravne vrednosti
groupId,artifactIdiversion. Verzija može biti proizvoljna niz i ne može da se završava-SNAPSHOT - Potrebna je prisutnost
ime,opisiurl - Prisutnost informacija o licenci, programerima i sistemu kontrole verzija
Ovo su osnovna pravila koja moraju biti ispoštovana prilikom objavljivanja. Potpune informacije su dostupne .
Implementiramo ove zahtjeve u build.gradle datoteci. Za početak ćemo dodati sve potrebne informacije o developerima, licenci, sustavu kontrole verzija, kao i postaviti URL, naziv i opis projekta. U tu svrhu ćemo napisati jednostavnu metodu:
def customizePom(pom) {
pom.withXml {
def root = asNode()
root.dependencies.removeAll { dep ->
dep.scope == "test"
}
root.children().last() + {
resolveStrategy = DELEGATE_FIRST
description 'Neki opis artifacta'
name 'Naziv artifacta'
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 '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'
}
}
}
}
}Nakon toga treba naznačiti da se prilikom kompajliranja generiraju -sources.jar i-javadoc.jar datoteke. U tu svrhu u sekciju java treba dodati sljedeće:
java {
withJavadocJar()
withSourcesJar()
}Prelazimo na posljednji zahtjev, podešavanje GPG/PGP potpisa. Za to ćemo uključiti dodatak signing:
plugins {
id 'signing'
}I dodati sekciju:
signing {
sign publishing.publications
}Konačno, dodajemo sekciju 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
}
}
}
}Ovdje sonatypeUsername i sonatypePassword varijable koje sadrže korisničko ime i lozinku kreirane prilikom registracije na .
Tako da je konačni build.gradle trebao bi izgledati ovako:
Potpuni kod 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 'Neki opis artefakta'
name 'Naziv artefakta'
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 '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.hr'
}
}
}
}
}Želim napomenuti da verziju dobivamo iz varijable okruženja: System.getenv('RELEASE_VERSION'). Postavljat ćemo je tijekom gradnje i uzimati iz imena oznake.
Generiranje PGP ključa
Jedan od zahtjeva Sonatypea je potpisivanje svih datoteka pomoću GPG/PGP ključa. U tu svrhu idemo i preuzimamo GnuPG alat za svoj operativni sustav.
- Generiramo par ključeva:
gpg --gen-key, unosimo korisničko ime, e-mail, kao i postavljamo lozinku. - Provjeravamo
idnaš ključ naredbom:gpg --list-secret-keys --keyid-format short. Id će biti naveden nakon kose crte, na primjer: rsa2048/9B695056 - Objavljujemo javni ključ na poslužitelj komandom:
gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056 - Izvozimo tajni ključ na proizvoljno mjesto, trebat će nam kasnije:
gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg
Podešavamo Github Actions
Prelazimo na završnu fazu, postavljamo izgradnju i automatsku objavu, koristeći Github Actions.
Github Actions – funkcionalnost koja omogućava automatizaciju radnih procesa kroz cjeloviti CI/CD ciklus. Izgradnja, testiranje i implementacija mogu se pokrenuti raznim događajima: slanjem koda, stvaranjem izdanja ili issues. Ova funkcionalnost je potpuno besplatna za javne repozitorije.
U ovom odjeljku ću pokazati kako postaviti izgradnju i slanje koda te implementaciju u Sonatype repozitorij prilikom izdavanja, kao i postavku tajni.
Postavimo tajne
Za automatsku izgradnju i implementaciju trebat će nam niz tajnih vrijednosti, poput ID-a ključa, lozinke koju smo unijeli prilikom generiranja ključa, samog PGP ključa, kao i korisničkog imena/lozinke za Sonatype. Mogu se postaviti u posebnom odjeljku u postavkama repozitorija:

Postavljamo sljedeće varijable:
- SONATYPE_USERNAME/SONATYPE_PASSWORD – korisničko ime/lozinka koju smo unijeli prilikom registracije na Sonatype
- SIGNING_KEYID/SIGNING_PASSWORD – ID PGP ključa i lozinka postavljena prilikom generacije.
Želim se više posvetiti varijabli GPG_KEY_CONTENTS. Naime, za objavljivanje nam je potreban privatni PGP ključ. Kako bih ga postavio u tajne, koristio sam i dodatno učinio nekoliko koraka.
- Šifrirajmo naš ključ pomoću gpg:
gpg --symmetric --cipher-algo AES256 9B695056.gpg, unosom lozinke. Treba je smjestiti u varijablu: SECRET_PASSPHRASE - Pretvorimo dobiveni šifrirani ključ u tekstualni oblik pomoću base64:
base64 9B695056.gpg.gpg > 9B695056.txt. Sadržaj smjestimo u varijablu: GPG_KEY_CONTENTS.
Postavljanje izgradnje prilikom slanja koda i stvaranja PR
Za početak, potrebno je stvoriti mapu u korijenu vašeg projekta: .github/workflows.
U njoj postavite datoteku, na primjer, gradle-ci-build.yml s sljedećim sadržajem:
name: build
on:
push:
branches:
- master
- dev
- testing
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Postavi JDK 8
uses: actions/setup-java@v1
with:
java-version: 8
- name: Izgradi s Gradleom
uses: eskatos/gradle-command-action@v1
with:
gradle-version: current
arguments: build -PsonatypeUsername=${{secrets.SONATYPE_USERNAME}} -PsonatypePassword=${{secrets.SONATYPE_PASSWORD}}Ovaj radni proces će se izvoditi prilikom slanja u grane master, dev i testing, također prilikom stvaranja pull zahtjeva.
U odjeljku jobs navedeni su koraci koji trebaju biti izvršeni prilikom zadanih događaja. U ovom slučaju, izgradnju ćemo vršiti na najnovijoj verziji ubuntu, koristiti Java 8, kao i koristiti dodatak za Gradle eskatos/gradle-command-action@v1, koji će koristeći najnoviju verziju graditelja pokrenuti naredbe navedene u argumenti. Varijable tajne.SONATYPE_USERNAME i tajne.SONATYPE_PASSWORD to su tajne koje smo postavili ranije.
Rezultati izrade bit će odraženi na kartici Radnje:

Automatizirano postavljanje pri izdavanju nove verzije
Za automatizirano postavljanje stvorit ćemo zasebnu datoteku radnog toka gradle-ci-publish.yml:
name: publish
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Postavi JDK 8
uses: actions/setup-java@v1
with:
java-version: 8
- name: Pripremi za objavu
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: Objavi s Gradleom
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}}Datoteka je gotovo identična prethodnoj, osim događaja uz kojeg će se aktivirati. U ovom slučaju, to je događaj stvaranja taga s imenom koje počinje na v.
Prije postavljanja trebamo izvući PGP ključ iz tajni i smjestiti ga u korijensku mapu projekta, kao i dešifrirati ga. Zatim trebamo postaviti posebnu varijablu okruženja RELEASE_VERSION na koju se pozivamo u gradle.build datoteci. Sve je to napravljeno u dijelu Pripremi za objavu. Naš ključ dobivamo iz varijable GPG_KEY_CONTENTS, pretvaramo ga u gpg datoteku, a zatim ga dešifriramo, smještajući u datoteku secret.gpg.
Nakon toga pozivamo posebnu varijablu GITHUB_REF, iz koje možemo izvući verziju koju smo postavili pri stvaranju taga. Ova varijabla u ovom slučaju ima vrijednost refs/tags/v0.0.2 iz koje izrezujemo prvih 11 znakova kako bismo dobili konkretnu verziju. Nakon toga standardno koristimo komande Gradle za objavu: test publish
Provjera rezultata postavljanja u Sonatype repozitorij
Nakon stvaranja izdanja trebao bi se pokrenuti radni proces opisan u prethodnom odlomku. Za to stvaramo izdanje:

pri čemu ime taga mora početi s v. Ako nakon pritiska na Objavi izdanje, radni proces uspješno završi, možemo otići u da bismo se uvjerili u to:

Artefakt je pojavio u Staging repozitoriju. Odmah se pojavljuje u statusu Otvoreno, a zatim ga je potrebno ručno prebaciti u status Zatvoreno klikom na odgovarajući gumb. Nakon provjere ispunjavanja svih zahtjeva, artefakt prelazi u status Zatvoreno i više nije dostupan za promjene. U tom obliku će se poslati u MavenCentral. Ako je sve u redu, možete kliknuti na gumb Objava, pri čemu artefakt ide u repozitorij Sonatype.
Da bi artefakt dospio u MavenCentral, potrebno je to zatražiti u zadatku koji smo kreirali na samom početku. To treba učiniti samo jednom, jer objavljujemo po prvi put. U sljedećim slučajevima to nije potrebno, sve će se automatski sinkronizirati. Brzo su mi aktivirali sinkronizaciju, ali da artefakt postane dostupan u MavenCentralu prošlo je oko 5 dana.
Na tome je gotovo, objavili smo naš artefakt u MavenCentral.
Koristitne veze
- Slična , samo objava preko maven
- Staging Sonatype
- Sonatype, u kojem je potrebno otvoriti zadatak
- repozitorija gdje je sve to podešeno
Izvor: habr.com
