Dans cet article, je souhaite examiner en détail le processus de publication d'un artefact Java à partir de zéro via les actions Github dans le référentiel central Sonatype Maven à l'aide du générateur Gradle.
J'ai décidé d'écrire cet article en raison de l'absence d'un tutoriel normal en un seul endroit. Toutes les informations ont dû être recueillies pièce par pièce à partir de diverses sources, d'ailleurs pas entièrement fraîches. Qui s'en soucie, bienvenue sous le chat.
Créer un référentiel dans Sonatype
La première étape consiste à créer un référentiel dans Sonatype Maven Central. Pour cela nous allons , enregistrez-vous et créez une nouvelle tâche en nous demandant de créer un référentiel. Nous conduisons dans notre Groupid projet, URL du projet lien du projet et URL SCM un lien vers le système de contrôle de version dans lequel se trouve le projet. Groupid ici devrait être de la forme com.example, com.example.domain, com.example.testsupport, et peut également être sous la forme d'un lien vers votre github : -> io.github.votrenomd'utilisateur. Dans tous les cas, vous devrez vérifier la propriété de ce domaine ou de ce profil. Si vous avez spécifié un profil github, il vous sera demandé de créer un référentiel public avec le nom souhaité.
Quelque temps après la confirmation, votre GroupId sera créé et nous pourrons passer à l'étape suivante, la configuration de Gradle.
Configurer Gradle
Au moment d'écrire ces lignes, je n'ai pas trouvé de plugins Gradle qui pourraient aider à publier l'artefact. le seul plugin que j'ai trouvé, cependant, l'auteur a refusé de le soutenir davantage. Par conséquent, j'ai décidé de tout faire moi-même, car ce n'est pas trop difficile à faire.
La première chose à comprendre est les exigences de Sonatype en matière de publication. Ce sont les suivants :
- Disponibilité des codes sources et JavaDoc, c'est-à-dire. doivent assister
-sources.jarи-javadoc.jardes dossiers. Comme indiqué dans la documentation, s'il n'est pas possible de fournir les codes sources ou la documentation, vous pouvez faire un mannequin-sources.jarou-javadoc.jaravec un simple README à l'intérieur pour réussir le test. - Tous les fichiers doivent être signés avec
GPG/PGPEt.ascle fichier contenant la signature doit être inclus pour chaque fichier. - disponibilité
pomdossier - Valeurs correctes
groupId,artifactIdиversion. La version peut être une chaîne arbitraire et ne peut pas se terminer par-SNAPSHOT - Présence requise
name,descriptionиurl - La présence d'informations sur la licence, les développeurs et le système de contrôle de version
Ce sont les règles de base qui doivent être suivies lors de la publication. Informations complètes disponibles .
Nous mettons en œuvre ces exigences dans build.gradle déposer. Tout d'abord, ajoutons toutes les informations nécessaires sur les développeurs, les licences, le système de contrôle de version, et définissons également l'URL, le nom et la description du projet. Écrivons une méthode simple pour cela:
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@dev.ru'
}
}
}
}
}Ensuite, vous devez spécifier que lors de l'assemblage généré -sources.jar и-javadoc.jar des dossiers. Pour cette rubrique java vous devez ajouter ce qui suit :
java {
withJavadocJar()
withSourcesJar()
}Passons à la dernière exigence, la mise en place d'une signature GPG/PGP. Pour cela, connectez le plugin signing:
plugins {
id 'signing'
}Et ajoutez une section :
signing {
sign publishing.publications
}Enfin, ajoutons une section 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
}
}
}
}il est sonatypeNom d'utilisateur и sonatypeMot de passe variables contenant le login et le mot de passe créés lors de l'inscription sur .
Ainsi la finale build.gradle ressemblera à ceci:
Code build.gradle complet
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@dev.ru'
}
}
}
}
}Je tiens à noter que nous obtenons la version de la variable d'environnement : System.getenv('RELEASE_VERSION'). Nous l'exposerons lors de l'assemblage et le prendrons à partir du nom de la balise.
Génération de clé PGP
L'une des exigences de Sonatype est que tous les fichiers soient signés avec une clé GPG/PGP. Pour cela nous allons et téléchargez l'utilitaire GnuPG pour votre système d'exploitation.
- Nous générons une paire de clés :
gpg --gen-key, entrez un nom d'utilisateur, un e-mail et définissez également un mot de passe. - Nous découvrons
idnotre clé avec la commande :gpg --list-secret-keys --keyid-format short. L'ID sera spécifié après la barre oblique, par exemple : rsa2048/9B695056 - Publication de la clé publique sur le serveur par commande :
gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056 - Nous exportons la clé secrète vers un endroit arbitraire, nous en aurons besoin à l'avenir :
gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg
Configuration des actions Github
Passons à l'étape finale, configurez la construction et la publication automatique à l'aide des actions Github.
Github Actions est une fonctionnalité qui permet d'automatiser le workflow en mettant en place un cycle CI/CD complet. La création, le test et le déploiement peuvent être déclenchés par divers événements : envoi de code, création de version ou problèmes. Cette fonctionnalité est absolument gratuite pour les référentiels publics.
Dans cette section, je vais vous montrer comment configurer le code de construction et de transmission et le déployer dans le référentiel Sonatype lors de la publication, ainsi que configurer les secrets.
Nous plaçons des secrets
Pour l'assemblage et le déploiement automatiques, nous avons besoin d'un certain nombre de valeurs secrètes, telles que l'identifiant de la clé, le mot de passe que nous avons saisi lors de la génération de la clé, la clé PGP elle-même et le login/mot de passe Sonatype. Vous pouvez les définir dans une section spéciale des paramètres du référentiel :

Nous définissons les variables suivantes :
- SONATYPE_USERNAME / SONATYPE_PASSWORD - identifiant / mot de passe que nous avons saisi lors de l'enregistrement auprès de Sonatype
- SIGNING_KEYID/SIGNING_PASSWORD — ID de clé PGP et mot de passe définis lors de la génération.
Je veux m'attarder plus en détail sur la variable GPG_KEY_CONTENTS. Le fait est que pour la publication, nous avons besoin d'une clé PGP privée. Afin de le poster dans les secrets, j'ai utilisé et a en outre fait un certain nombre d'actions.
- Chiffrez notre clé avec gpg :
gpg --symmetric --cipher-algo AES256 9B695056.gpgen saisissant un mot de passe. Il doit être placé dans une variable : SECRET_PASSPHRASE - Traduisons la clé chiffrée reçue sous forme de texte en utilisant base64 :
base64 9B695056.gpg.gpg > 9B695056.txt. Le contenu sera placé dans la variable : GPG_KEY_CONTENTS.
Construire la configuration lors de l'envoi de code et de la création de relations publiques
Vous devez d'abord créer un dossier à la racine de votre projet : .github/workflows.
Dans celui-ci, marquez le fichier, par exemple, gradle-ci-build.yml avec le contenu suivant :
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}}Ce workflow sera exécuté lors du push vers les branches master, dev и testing, également lors de la création de demandes d'extraction.
La section jobs spécifie les étapes à exécuter sur les événements spécifiés. Dans ce cas, nous nous baserons sur la dernière version d'ubuntu, utiliserons Java 8 et utiliserons également le plugin pour Gradle eskatos/gradle-command-action@v1qui, en utilisant la dernière version du constructeur, exécutera les commandes spécifiées dans arguments. variables secrets.SONATYPE_USERNAME и secrets.SONATYPE_PASSWORD ce sont les secrets que nous avons demandés plus tôt.
Les résultats de la génération seront reflétés dans l'onglet Actions :

Déploiement automatique lorsqu'une nouvelle version est publiée
Créons un fichier de workflow séparé pour le déploiement automatique 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}}Le fichier est presque identique au précédent, à l'exception de l'événement dans lequel il sera déclenché. Dans ce cas, il s'agit de l'événement de création d'une balise dont le nom commence par v.
Avant le déploiement, nous devons extraire la clé PGP des secrets et la placer à la racine du projet, ainsi que la déchiffrer. Ensuite, nous devons définir une variable d'environnement spéciale RELEASE_VERSION auquel nous nous référons gradle.build déposer. Tout cela se fait dans la section Prepare to publish. Nous obtenons notre clé de la variable GPG_KEY_CONTENTS, la traduisons dans un fichier gpg, puis la déchiffrons en la mettant dans le fichier secret.gpg.
Ensuite, nous nous tournons vers une variable spéciale GITHUB_REF, à partir duquel nous pouvons obtenir la version que nous avons définie lors de la création de la balise. Cette variable est pertinente dans ce cas. refs/tags/v0.0.2 dont nous avons coupé les 11 premiers caractères pour obtenir une version spécifique. Ensuite, nous utilisons les commandes Gradle standard pour la publication : test publish
Vérification des résultats du déploiement dans le référentiel Sonatype
Une fois la version créée, le flux de travail décrit dans la section précédente doit démarrer. Pour ce faire, créez une version :

le nom de la balise doit commencer par v. Si, après avoir cliqué sur Publier la version, le flux de travail se termine avec succès, nous pouvons accéder à assurer:

L'artefact est apparu dans le référentiel Staging. Il apparaît immédiatement dans l'état Ouvert, puis il doit être manuellement transféré dans l'état Fermé en appuyant sur le bouton approprié. Après avoir vérifié que toutes les conditions sont remplies, l'artefact passe à l'état Fermer et n'est plus disponible pour modification. Sous cette forme, il se retrouvera dans MavenCentral. Si tout va bien, vous pouvez appuyer sur le bouton Libération, et l'artefact se retrouvera dans le référentiel Sonatype.
Pour que l'artefact entre dans MavenCentral, vous devez le demander dans la tâche que nous avons créée au tout début. Vous n'avez besoin de le faire qu'une seule fois, nous publions donc pour la première fois. Dans les temps suivants, ce n'est pas nécessaire, tout sera synchronisé automatiquement. Ils ont rapidement activé la synchronisation pour moi, mais il a fallu environ 5 jours pour que l'artefact soit disponible dans MavenCentral.
C'est tout, nous avons publié notre artefact dans MavenCentral.
Liens utiles
- Similaire , publier uniquement via maven
- Staging Sonatype
- Sonatype dans lequel créer la tâche
- référentiel où tout est configuré
Source: habr.com
