在本文中,我想仔細看看使用 Gradle 構建器通過 Github Actions 從頭開始將 Java 工件發佈到 Sonatype Maven Central Repository 的過程。
由於一處缺少正常的教程,我決定寫這篇文章。 所有的信息都必須從各種來源一點一點地收集,而且不是完全新鮮的。 誰在乎,歡迎在貓下。
在 Sonatype 中創建存儲庫
第一步是在 Sonatype Maven Central 中創建一個存儲庫。 為此我們去
確認後的某個時間,您的 GroupId 將被創建,我們可以繼續下一步,Gradle 配置。
配置搖籃
在撰寫本文時,我沒有找到可以幫助發布工件的 Gradle 插件。
首先要搞清楚的是Sonatype對發布的要求。 它們是:
- 源代碼和 JavaDoc 的可用性,即。 必須參加
-sources.jar
и-javadoc.jar
文件。 如文檔中所述,如果無法提供源代碼或文檔,您可以製作一個虛擬-sources.jar
或-javadoc.jar
裡面有一個簡單的自述文件來通過測試。 - 所有文件必須簽名
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 protected]'
}
}
}
}
}
接下來,您需要指定在程序集生成的過程中 -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
}
}
}
}
這裡 sonatype用戶名 и sonatype密碼 包含在註冊期間創建的登錄名和密碼的變量
因此決賽 build.gradle
看起來像這樣:
完整的 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 protected]'
}
}
}
}
}
我想指出,我們從環境變量中獲取版本: System.getenv('RELEASE_VERSION')
. 我們將在組裝期間公開它並從標籤名稱中獲取它。
PGP 密鑰生成
Sonatype 的要求之一是所有文件都使用 GPG/PGP 密鑰簽名。 為此我們去
- 我們生成一個密鑰對:
gpg --gen-key
,輸入用戶名、電子郵件,並設置密碼。 - 找出答案
id
我們的密鑰與命令:gpg --list-secret-keys --keyid-format short
. id 將在斜杠後指定,例如:rsa2048/9B695056 - 將公鑰發佈到服務器
https://keys.openpgp.org 使用命令:gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
- 我們將密鑰導出到任意位置,我們將來需要它:
gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg
設置 Github 操作
讓我們進入最後階段,使用 Github Actions 設置構建和自動發布。
Github Actions 是一項功能,可讓您通過實施完整的 CI/CD 週期來自動化工作流程。 構建、測試和部署可以由各種事件觸發:代碼推送、發布創建或問題。 此功能對公共存儲庫完全免費。
在本節中,我將向您展示如何設置構建和推送代碼以及如何在發佈時部署到 Sonatype 存儲庫,以及設置機密。
我們設下秘密
對於自動組裝和部署,我們需要一些秘密值,例如密鑰 ID、我們在生成密鑰時輸入的密碼、PGP 密鑰本身以及 Sonatype 登錄名/密碼。 您可以在存儲庫設置的特殊部分中設置它們:
我們設置以下變量:
- SONATYPE_USERNAME / SONATYPE_PASSWORD - 我們在註冊 Sonatype 時輸入的登錄名/密碼
- SIGNING_KEYID/SIGNING_PASSWORD — PGP 密鑰 ID 和密碼在生成期間設置。
我想更詳細地討論 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
,也在創建拉取請求時。
作業部分指定要對指定事件執行的步驟。 在這種情況下,我們將在最新版本的 ubuntu 上構建,使用 Java 8,同時使用 Gradle 的插件 eskatos/gradle-command-action@v1
其中,使用最新版本的構建器,將運行中指定的命令 arguments
. 變量 secrets.SONATYPE_USERNAME
и secrets.SONATYPE_PASSWORD
這些是我們之前詢問的秘密。
構建結果將反映在“操作”選項卡中:
發布新版本時自動部署
讓我們為自動部署創建一個單獨的工作流文件 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 開頭的標籤的事件。
在部署之前,我們需要從 secrets 中提取 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 之後,工作流成功完成,我們可以去
該工件出現在登台存儲庫中。 它立即出現在 Open 狀態,然後必須通過按相應的按鈕手動將其轉換為 Close 狀態。 檢查是否滿足所有要求後,工件進入關閉狀態,不再可供修改。 以這種形式,它將最終出現在 MavenCentral 中。 如果一切順利,您可以按下按鈕 發行, 工件將最終出現在 Sonatype 存儲庫中。
為了讓工件進入 MavenCentral,您需要在我們一開始創建的任務中請求它。 你只需要這樣做一次,所以我們第一次發布。 在以後的時間裡,這不是必需的,一切都會自動同步。 他們很快就為我打開了同步功能,但是花了大約 5 天的時間才在 MavenCentral 中提供該工件。
就是這樣,我們已經在 MavenCentral 中發布了我們的工件。
有用的鏈接
來源: www.habr.com