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 collecteur 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 devaient être collectées en morceaux de diverses sources, mais pas entièrement fraîches. Peu importe, bienvenue au chat.
Création d'un référentiel dans Sonatype
La première étape consiste à créer un référentiel dans Sonatype Maven Central. Pour ce faire, allez ici , inscrivez-vous et créez une nouvelle tâche, en nous demandant de créer un référentiel pour nous. Nous lançons dans notre GroupId du projet, le lien URL du projet vers le projet et le lien URL SCM vers le système de contrôle de version dans lequel se trouve le projet. Le GroupId ici doit être de la forme com.example, com.example.domain, com.example.testsupport, et il peut également être un lien vers votre github: github.com/yourusername -> io.github.yourusername. Dans tous les cas, vous devrez confirmer la propriété de ce domaine ou profil. Si vous avez spécifié un profil github, ils vous demanderont de créer un référentiel public avec le nom souhaité.
Quelque temps après la confirmation, votre GroupId sera créé et nous pouvons passer à l'étape suivante, la configuration de Gradle.
Configuration de Gradle
Au moment d'écrire ces lignes, je n'ai trouvé aucun plugin pour Gradle qui pourrait aider à la publication de l'artefact. C'est le seul plugin que j'ai trouvé, mais l'auteur a refusé son soutien supplémentaire. J'ai donc décidé de tout faire moi-même, car ce n'est pas trop difficile de le faire.
La première chose à savoir est les exigences de publication de Sonatype. Ils sont les suivants:
- La présence de codes sources et JavaDoc, c'est-à-dire doit être présent
-sources.jar
et -javadoc.jar
fichiers. Comme indiqué dans la documentation, s'il n'est pas possible de fournir des codes sources ou de la documentation, vous pouvez créer un mannequin -sources.jar
ou -javadoc.jar
un simple README à l'intérieur pour passer le test. GPG/PGP
, .asc
, , .pom
groupId
, artifactId
version
. -SNAPSHOT
name
, description
url
- ,
, . .
build.gradle
. , , , url, . :
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'
}
}
}
}
}
, -sources.jar
-javadoc.jar
. java
:
java {
withJavadocJar()
withSourcesJar()
}
, GPG/PGP . signing
:
plugins {
id 'signing'
}
:
signing {
sign publishing.publications
}
, 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
}
}
}
}
sonatypeUsername sonatypePassword , , sonatype.org.
, build.gradle
:
build.gradleplugins {
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'
}
}
}
}
}
, : System.getenv('RELEASE_VERSION')
. .
PGP
Sonatype GPG/PGP . GnuPG .
- :
gpg --gen-key
, , e-mail, . id
: gpg --list-secret-keys --keyid-format short
. Id , : rsa2048/9B695056- https://keys.openpgp.org :
gpg --keyserver https://keys.openpgp.org/ --send-keys 9B695056
- , :
gpg --export-secret-key 9B695056 > D:\\gpg\\9B695056.gpg
Github Actions
, , Github Actions.
Github Actions – , , CI/CD. , : , issues. .
Sonatype , .
, id , , , PGP , / Sonatype. :

:
- SONATYPE_USERNAME/SONATYPE_PASSWORD — /, Sonatype
- SIGNING_KEYID/SIGNING_PASSWORD — id PGP , .
GPG_KEY_CONTENTS . , PGP . , , .
- gpg:
gpg --symmetric --cipher-algo AES256 9B695056.gpg
, . : SECRET_PASSPHRASE - base64:
base64 9B695056.gpg.gpg > 9B695056.txt
. : GPG_KEY_CONTENTS.
PR
: .github/workflows
.
, , gradle-ci-build.yml
:
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}}
master
, dev
testing
, .
jobs , . ubuntu, Java 8, Gradle eskatos/gradle-command-action@v1
, , arguments
. secrets.SONATYPE_USERNAME
secrets.SONATYPE_PASSWORD
, .
Actions:

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}}
, . , v.
PGP , . RELEASE_VERSION
gradle.build
. Prepare to publish
. GPG_KEY_CONTENTS, gpg , , secret.gpg
.
GITHUB_REF
, , . refs/tags/v0.0.2
11 , . Gradle : test publish
Sonatype
, . :

v. Publish release, , Sonatype Nexus :

Staging . Open, Close, . , Close . MavenCentral. , Release, Sonatype.
, MavenCentral, , . , . , . , MavenCentral 5 .
C'est tout, nous avons publié notre artefact dans MavenCentral.
Liens utiles
- Article similaire , publication uniquement via maven
- Type de son du référentiel intermédiaire
- Jira Sonatype, dans lequel vous devez créer une tâche
- Un exemple de référentiel où tout est configuré