V tomto článku chci podrobně prozkoumat proces publikace Java artefaktu od nuly prostřednictvím Github Actions v repozitáři Sonatype Maven Central s využitím build systému Gradle.
Tento článek jsem se rozhodl napsat vzhledem k nedostatku kvalitního tutoriálu na jednom místě. Všechny informace jsem musel shromažďovat po útržcích z různých zdrojů, přičemž mnohé z nich nebyly zcela aktuální. Kdo má zájem, vítáme vás pod kat.
Vytvoření repozitáře v Sonatype
Prvním krokem je vytvoření repozitáře v Sonatype Maven Central. K tomu přejdeme , zaregistrujeme se a vytvoříme novou žádost o vytvoření repozitáře. Zadejte svůj GroupId projektu, URL projektu odkaz na projekt a SCM url odkaz na systém pro správu verzí, ve kterém projekt leží. GroupId měli by mít formát com.example, com.example.domain, com.example.testsupport, a také může být ve formě odkazu na váš GitHub: -> io.github.yourusername. V každém případě budete muset potvrdit vlastnictví tohoto doménového jména nebo profilu. Pokud jste uvedli profil GitHub, požádají vás o vytvoření veřejného repozitáře s požadovaným názvem.
Po nějakém čase po potvrzení bude váš GroupId vytvořen a můžeme přejít k dalšímu kroku, konfiguraci Gradle.
Konfigurace Gradle
V době psaní tohoto článku jsem nenašel žádné pluginy pro Gradle, které by mohly pomoci s publikací artefaktů. jediný plugin, který jsem našel, však jeho autor přestal podporovat. Proto jsem se rozhodl udělat vše sám, což není příliš obtížné.
První, co je třeba zjistit, jsou požadavky Sonatype na publikaci. Tyto požadavky jsou následující:
- Přítomnost zdrojového kódu a JavaDoc, tedy musí být přítomny
-sources.jara-javadoc.jarsouborů. Jak je uvedeno v dokumentaci, pokud není možné poskytnout zdrojové kódy nebo dokumentaci, lze vytvořit prázdný soubor-sources.jarnebo-javadoc.jars jednoduchým README uvnitř, aby prošel kontrolou. - Všechny soubory musí být podepsány pomocí
GPG/PGP, a.ascsoubor obsahující podpis, musí být přiložen ke každému souboru. - Přítomnost
pomsoubory - Správné hodnoty
groupId,artifactIdaversion. Verze může být libovolný řetězec a nemůže končit<\/changelist> - Musí být přítomny
name,descriptionaurl - Přítomnost informací o licenci, vývojářích a systému správy verzí
Toto jsou základní pravidla, která musí být dodržena při publikaci. Kompletní informace jsou k dispozici .
Zrealizujeme tyto požadavky v build.gradle souboru. Nejprve přidáme všechny potřebné informace o vývojářích, licenci, systému verzování a také nastavíme URL, název a popis projektu. K tomu napíšeme jednoduchou metodu:
def customizePom(pom) {
pom.withXml {
def root = asNode()
root.dependencies.removeAll { dep ->
dep.scope == "test"
}
root.children().last() + {
resolveStrategy = DELEGATE_FIRST
description 'Nějaký popis artefaktu'
name 'Název artefaktu'
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'
}
}
}
}
}Dále je potřeba uvést, aby se při sestavení generovaly -sources.jar a-javadoc.jar souborů. K tomu do sekce java je potřeba přidat následující:
java {
withJavadocJar()
withSourcesJar()
}Přejděme k poslednímu požadavku, nastavení GPG/PGP podpisu. K tomu připojíme plugin signing:
plugins {
id 'signing'
}A přidáme sekci:
signing {
sign publishing.publications
}Nakonec přidáme sekci 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
}
}
}
}Zde sonatypeUsername a sonatypePassword proměnné, obsahující přihlašovací údaje vytvořené při registraci na .
Takže finální build.gradle bude vypadat následovně:
Úplný kód 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ějaký popis artefaktu'
name 'Název artefaktu'
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 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'
}
}
}
}
}Rád bych poznamenal, že verzi získáváme z proměnné prostředí: System.getenv('RELEASE_VERSION'). Stanovíme ji při sestavení a vezmeme z názvu tagu.
Generování PGP klíče
Jedním z požadavků Sonatype je podpis všech souborů pomocí klíče GPG/PGP. K tomu přejdeme a stáhneme nástroj GnuPG pro svůj operační systém.
- Vytvoříme pár klíčů:
gpg --gen-key, zadáme uživatelské jméno, e-mail a také nastavíme heslo. - Zjistíme
idnáš klíč příkazem:gpg --list-secret-keys --keyid-format short. Id bude uvedeno za lomítkem, například: rsa2048/9B695056 - Zveřejníme veřejný klíč na serveru příkazem:
gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056 - Exportujeme tajný klíč na libovolné místo, budeme ho potřebovat později:
gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg
Nastavujeme Github Actions
Přejdeme k závěrečnému kroku, nastavíme sestavení a automatické publikování pomocí Github Actions.
Github Actions – nástroj, který umožňuje automatizovat pracovní postup a realizovat plný cyklus CI/CD. Sestavení, testování a nasazení mohou být spuštěny různými událostmi: pushování kódu, vytváření vydání nebo issues. Tato funkce je zcela zdarma pro veřejné repozitáře.
V této části ukážu, jak nastavit sestavení a push kódu a nasazení do repozitáře Sonatype při vydání, stejně jako nastavení tajemství.
Nastavení tajemství
Pro automatické sestavení a nasazení potřebujeme řadu tajných hodnot, jako je id klíče, heslo, které jsme zadali při generování klíče, samotný PGP klíč a také přihlašovací jméno/hESlo k Sonatype. Tyto hodnoty je možné nastavit ve speciální sekci v nastavení repozitáře:

Nastavujeme následující proměnné:
- SONATYPE_USERNAME/SONATYPE_PASSWORD — přihlašovací jméno/hESlo, které jsme zadali při registraci v Sonatype
- SIGNING_KEYID/SIGNING_PASSWORD — id PGP klíče a heslo nastavené při generování.
U proměnné GPG_KEY_CONTENTS bych se chtěl zastavit podrobněji. Důvodem je, že k publikaci potřebujeme soukromý PGP klíč. Abychom ho umístili do tajemství, použil jsem a navíc jsem provedl řadu kroků.
- Zašifrujeme náš klíč pomocí gpg:
gpg --symmetric --cipher-algo AES256 9B695056.gpg, zadejte heslo. Mělo by být umístěno do proměnné: SECRET_PASSPHRASE - Převedeme získaný zašifrovaný klíč na textový formát pomocí base64:
base64 9B695056.gpg.gpg > 9B695056.txt. Obsah umístíme do proměnné: GPG_KEY_CONTENTS.
Nastavení sestavení při pushování kódu a vytváření PR
Nejprve je třeba vytvořit složku v kořenovém adresáři vašeho projektu: .github/workflows.
V ní umístíme soubor, například gradle-ci-build.yml s následujícím obsahem:
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}}Tento pracovní postup se bude spouštět při pushování do větví master, dev a testing, stejně jako při vytváření pull requestů.
V sekci jobs jsou uvedeny kroky, které mají být provedeny podle uvedených událostí. V tomto případě budeme sestavovat na nejnovější verzi ubuntua, používat Java 8 a také použijeme plugin pro Gradle eskatos/gradle-command-action@v1, který pomocí nejnovější verze builderu spustí příkazy, uvedené v arguments. Proměnné secrets.SONATYPE_USERNAME a secrets.SONATYPE_PASSWORD to jsou tajné informace, které jsme nastavili dříve.
Výsledky sestavení se zobrazí na kartě Akce:

Automatické nasazení při vydání nové verze
Pro automatické nasazení vytvoříme samostatný soubor pracovního postupu gradle-ci-publish.yml:
name: publish
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Nastavení JDK 8
uses: actions/setup-java@v1
with:
java-version: 8
- name: Příprava k publikaci
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: Publikace s 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}}Soubor je prakticky identický s předchozím, s výjimkou události, při které se spustí. V tomto případě se jedná o událost vytvoření tagu, který začíná písmenem v.
Před nasazením musíme vytáhnout PGP klíč z tajných informací a umístit ho do kořenového adresáře projektu, a také ho dešifrovat. Poté potřebujeme nastavit speciální proměnnou prostředí RELEASE_VERSION na kterou odkazujeme v gradle.build souboru. To vše se provádí v oddílu Příprava k publikaci. Z proměnné GPG_KEY_CONTENTS získáme náš klíč, převedeme ho na gpg soubor a poté ho dešifrujeme, čímž ho umístíme do souboru secret.gpg.
Nyní se odkazujeme na speciální proměnnou GITHUB_REF, ze které můžeme vytáhnout verzi, kterou jsme nastavili při vytváření tagu. Tato proměnná v tomto případě má hodnotu refs/tags/v0.0.2 , ze které odstraníme prvních 11 znaků, abychom přesně získali verzi. Poté standardně používáme příkazy Gradle pro publikaci: test publish
Kontrola výsledků nasazení do repozitáře Sonatype
Po vytvoření vydání by se měl spustit pracovní postup popsaný v předchozí části. Pro tento účel vytvořte vydání:

při čemž název tagu musí začínat na v. Pokud po kliknutí na Publikovat vydání pracovní postup úspěšně běží, můžeme se podívat do abychom se o tom ujistili:

Artefakt se objevil v stagingovém repozitáři. Okamžitě se zobrazuje ve stavu Open, poté je třeba jej ručně převést do stavu Close stisknutím příslušného tlačítka. Po ověření splnění všech požadavků artefakt přechází do stavu Close a již není možné jej měnit. V této podobě se dostane do MavenCentral. Pokud je vše v pořádku, můžete stisknout tlačítko Release, v tomto případě artefakt přejde do repozitáře Sonatype.
Aby se artefakt dostal do MavenCentral, je třeba o to požádat v úkolu, který jsme vytvořili na začátku. To je třeba udělat pouze jednou, protože publikujeme poprvé. Při dalších publikacích to není nutné, vše se bude synchronizovat automaticky. Rychle mi zapnuli synchronizaci, ale aby byl artefakt dostupný v MavenCentral, trvalo to přibližně 5 dní.
To je vše, publikovali jsme náš artefakt v MavenCentral.
Užitečné odkazy
- Podobné , pouze publikace přes maven
- Staging Sonatype
- Sonatype, kde je nutné vytvořit úkol
- repozitáře, kde je to vše nastaveno
Zdroj: habr.com
