- PVSM.RU - https://www.pvsm.ru -
В данной статье я хочу подробно рассмотреть процесс публикации с нуля Java артефакта через Github Actions в Sonatype Maven Central Repository используя сборщик Gradle.
Данную статью решил написать ввиду отсутствия нормального туториала в одном месте. Всю информацию приходилось собирать по кускам из различных источников, при том, не совсем свежих. Кому интересно, добро пожаловать под кат.
Первым этапом нам нужно создать репозиторий в Sonatype Maven Central. Для этого идем сюда [1], регистрируемся и создаем новую задачу, с просьбой создать нам репозиторий. Вбиваем свой GroupId проекта, Project URL ссылку на проект и SCM url ссылку на систему контроля версий, в которой проект лежит. GroupId здесь должен быть вида com.example, com.example.domain, com.example.testsupport, а также может быть в виде ссылки на ваш гитхаб: github.com/yourusername [2] -> io.github.yourusername. В любом случае, вам нужно будет подтвердить владение данным доменом или профилем. Если вы указали профиль гитхаба, попросят создать публичный репозиторий с нужным именем.
Через некоторое время после подтверждения ваш GroupId будет создан и мы можем перейти к следующему шагу, конфигурации Gradle.
На момент написания статьи я не нашел плагинов для Gradle, которые могли бы помочь с публикацией артефакта. Это [3] единственный плагин, который я нашел, однако автор отказался от его дальнейшей поддержки. Поэтому я решил сделать все самостоятельно, благо сделать это не слишком затруднительно.
Первое, что нужно выяснить, это требования Sonatype для публикации. Они следующие:
-sources.jar
и-javadoc.jar
файлы. Как сказано в документации, если нет возможно предоставить исходные коды или документацию, можно сделать пустышку -sources.jar
или -javadoc.jar
c простым README внутри, чтобы пройти проверку.GPG/PGP
, и .asc
файл, содержащий подпись, должен быть включен для каждого файла.pom
файлаgroupId
, artifactId
и version
. Версия может быть произвольной строкой и не может заканчиваться -SNAPSHOT
name
, description
и url
Это основные правила, которые должны быть соблюдены при публикации. Полная информация доступна здесь [4].
Реализуем эти требования в 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 [5].
Таким образом, финальный 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 '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')
. Выставлять ее мы будем при сборке и брать из имени тега.
Одно из требований Sonatype это подписание всех файлов с помощью GPG/PGP ключа. Для этого идем сюда [6] и качаем утилиту GnuPG под свою операционную систему.
gpg --gen-key
, вводим имя пользователя, e-mail, а также задаем пароль.id
нашего ключа командой: gpg --list-secret-keys --keyid-format short
. Id будет указан после слеша, например: rsa2048/9B695056gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg
Перейдем к завершающему этапу, настроим сборку и авто публикацию, используя Github Actions.
Github Actions – функционал, позволяющий автоматизировать рабочий процесс, реализовав полный цикл CI/CD. Сборка, тестирование и деплой могут быть вызваны различными событиями: пушинг кода, создание релиза или issues. Данный функционал абсолютно бесплатен для публичных репозиториев.
В этом разделе я покажу как настроить сборку и пуше кода и деплой в Sonatype репозиторий при выпуске релиза, а также настройку секретов.
Для автоматической сборки и деплоя нам понадобится ряд секретных значений, таких как id ключа, пароль, который мы вводили при генерации ключа, непосредственно сам PGP ключ, а также логин/пароль к Sonatype. Задать их можно в специальном разделе в настройках репозитория:
Задаем следующие переменные:
На переменной GPG_KEY_CONTENTS хочу остановится поподробнее. Дело в том, что для публикации нам необходим закрытый PGP ключ. Для того, чтобы разместить его в секретах, я воспользовался инструкцией [8] и дополнительно сделал ряд действий.
gpg --symmetric --cipher-algo AES256 9B695056.gpg
, введя пароль. Его следует поместить в переменную: SECRET_PASSPHRASEbase64 9B695056.gpg.gpg > 9B695056.txt
. Содержимое разместим в переменной: GPG_KEY_CONTENTS.Для начала нужно создать папку в корне вашего проекта: .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
После создания релиза должен запустится рабочий процесс, описанный в предыдущем разделе. Для этого создаем релиз:
при этом имя тега должно начинаться с v. Если после нажатия Publish release, рабочий процесс успешно отработает, мы можем зайти в Sonatype Nexus [9] чтобы в этом убедиться:
Артефакт появился в Staging репозитории. Сразу он появляется в статусе Open, далее его необходимо вручную перевести в статус Close, нажав соответствующую кнопку. После проверки выполнения всех требований, артефакт переходит в статус Close и более не доступен для изменения. В таком виде он попадет в MavenCentral. Если все хорошо, можно нажать кнопку Release, при этом артефакт попадет в репозиторий Sonatype.
Для того, чтобы артефакт попал в MavenCentral, нужно попросить об этом в задаче, которую мы создали в самом начале. Сделать это нужно только один раз, так мы публикуем в первый раз. В последующие разы это делать не требуется, все будет синхронизироваться автоматически. Включили мне синхронизацию быстро, но чтобы артефакт стал доступен в MavenCentral прошло около 5 дней.
На этом все, мы опубликовали наш артефакт в MavenCentral.
Автор: eaxdev
Источник [12]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/348275
Ссылки в тексте:
[1] сюда: https://issues.sonatype.org/projects/OSSRH/issues
[2] github.com/yourusername: http://github.com/yourusername
[3] Это: https://github.com/bmuschko/gradle-nexus-plugin
[4] здесь: https://central.sonatype.org/pages/requirements.html
[5] sonatype.org: http://sonatype.org/
[6] сюда: https://www.gnupg.org/download/index.html
[7] https://keys.openpgp.org: https://keys.openpgp.org/
[8] инструкцией: https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets
[9] Sonatype Nexus: https://oss.sonatype.org/#stagingRepositories
[10] статья: https://habr.com/ru/post/171493/
[11] Пример: https://github.com/eaxdev/Java-JsonResume-Validator
[12] Источник: https://habr.com/ru/post/490604/?utm_source=habrahabr&utm_medium=rss&utm_campaign=490604
Нажмите здесь для печати.