大家好. 在这篇文章中, 我将解释如何使用 Sonatype Nexus 逐步发布 Maven Repository.
在这里, 我将创建一个示例 Android 库, 以展示从头到尾的全貌.创建完库后, 我们将继续创建 Sonatype 账户, 创建 Sonatype 项目, 生成密钥, 为发布设置 Android 项目, 最后发布库.
创建 Android 库
- 创建一个新的 Android 项目.
- 点击
File->New Module, 然后选择Android Library, 创建一个新的Android Library. - 根据需要更改库名和包名, 然后点击
Finish.
我创建的库名称为 =mylibrary, 软件包名称为com.example.mylibrary.
- 在
com.example.mylibrary中创建库文件.
我创建了一个带有方法('greet')的类'Greet.java'[该方法将在发布后用于检查我们的库]:
package com.example.mylibrary;
public class Greet{
public void greet(String name){
System.out.println("Hi, " + name);
}
}
然后, 我们要确保库在本地正常运行.
- 在应用 build.gradle 中执行库
mylibrary, 以测试库是否正常工作.
implementation project(':mylibrary')
- 在
MainActivity.java中, 尝试调用mylibrary的greet方法. 如果运行正常, 我们就可以继续设置Sonatype JIRA账户.
设置 Sonatype Jira 账户
- 转到 issues.sonatype.org.
- 如果没有账户, 请创建一个.
- 登录到你的账户.
- 创建一个新的 Jira Ticket, 包含项目(Community Support — Open-Source Project Repository Hosting (OSSRH))和Issue Type (New Project)
- 在下一个页面中, 输入Summary, Description, Group Id, Project URL (GitHub URL), SCM URL (GitHub URL) 并且设置‘Already Synced to Central’为‘No’, 然后提交请求.
- 在 GitHub 中创建一个新的空仓库进行验证(仓库名称必须与 sonatype 建议的名称一致).我的仓库名称是
OSSRH-88179. 检查你的注释.
Sonatype Bot Central-OSSRH 注释
- 然后, 创建的问题会处于
waiting for response或opened之类的状态. 如果仓库被批准, 状态将变为closed. 批准问题的过程可能需要 1-2 天
访问 Nexus 资源库的profileId
- 收到批准后, 访问 s01.oss.sonatype.org/ 并使用你的凭据(与 Jira 凭据相同)登录
- 点击左侧面板中的
Staging Profiles, 然后点击包含软件包名称, 模式, 仓库目标, 发布仓库的配置文件... - 然后页面的URL会变成这样: s01.oss.sonatype.org/#stagingPro…. 记下这个
sonatypeStagingProfileId, 它将在以后的gradle脚本中发挥作用.
密钥生成
- 从 GnuPG 下载
GnuPg软件 - 像安装其他软件一样安装该软件.
- 为该软件设置
Environment Variables. - 然后打开
Command Prompt并导航到你的库文件夹(cd Project,cd mylibrary), 键入以下命令:
gpg --full-gen-key
你也可以用 CLI 工具,
Homebrew和gnupg生成密钥, 如果你不愿意使用 GUI 工具 GnuPG 的话.GnuPG软件是一款带有GUI的软件. 之后你可以尝试使用以下命令:
brew update
brew upgrade
brew install gnupg
这会询问一系列值. 请按照我下面提到的方式给出数值:
gpg (GnuPG) 2.4.0; Copyright (C) 2021 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extend permitted by law.
Please select what kind of key you want:
(1) RSA and RSA
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(9) ECC (sign and encrypt) *default*
(10) ECC (sign only)
(14) Existing key from card
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: YOUR NAME
Email address: youremail@youremail.com
Comment: Your Comment
You selected this USER-ID:
"YOUR NAME (Your Comment) <youremail@youremail.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate lot of random bytes. It is good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entrophy.
gpg: revocation certificate stored as 'C:\Users\USERNAME\AppData\Roaming\gnupg\openpgp-revocs.d\1234567890.rev'
public and secret key created and signed.
pub rsa 2023-02-02 [SC]
1234567890
uid YOUR NAME (Your Comment) <youremail@youremail.com>
sub rsa4096 2023-02-02 [E]
这里, 1234567890 是样本密钥. 你将收到更长的密钥.
将密钥上传到密钥服务器
- 运行以下命令:
gpg --keyserver hkp://keys.gnupg.net --send-keys 34567890
这里, 34567890 = 上一步生成的公钥(keyId)的最后 8 位数字.
如果上述keyserver出错, 比如:
gpg --keyserver hkp://keys.gnupg.net --send-keys 34567890
gpg: sending key FC335B2034567890 to hkp://keyserver.ubuntu.com
gpg: keyserver send failed: Network is unreachable
gpg: keyserver send failed: Network is unreachable
你可以尝试使用下面的命令来查找更多的keyserver:
gpg-connect-agent --dirmngr 'keyserver --hosttable'
S # hosttable (idx, ipv6, ipv4, dead, name, time):
S # 0 keyserver.ubuntu.com (185.125.188.27)
S # 1 hkps.pool.sks-keyservers.net
S # 2 sks-keyservers.net
S # 3 ipv4.pool.sks-keyservers.net
OK
如果每个keyserver都出错, 你可以尝试用下面的命令手动提交密钥到一个keyserver:
gpg --output mykey.asc --export --armor 34567890
然后打开mykey.asc, 复制内容并提交到以下任一网站, 如:
我如何知道密钥已成功提交到keyserver?:
gpg -i --batch --keyserver <keyserver> --recv-keys <keyIDs>
如果你忘记了设备上由
gpg生成的密钥, 可以通过以下命令取回:
gpg --list-keys
生成密匙(Secret Ring Key)文件.
- 运行以下命令:
gpg --export-secret-keys 34567890 > SecretRingKey.gpg
- 确保在
mylibrary目录下生成密匙文件.
发布项目设置
- 在你的 GitHub 账户中创建一个新仓库. 我们之前在 Jira 账户中创建的仓库仅用于验证. 现在可以删除该仓库. 创建一个全新的仓库.
- 在根项目
build.gradle中添加 nexus 发布插件和签名凭据:
buildscript {
dependencies {
classpath 'io.github.gradle-nexus:publish-plugin:1.1.0'
}
}
apply plugin: 'io.github.gradle-nexus.publish-plugin'
ext["signing.keyId"] = '34567890'
ext["signing.password"] = 'ENTERPASSPHRASEOFGNUPGHERE'
ext["signing.secretKeyRingFile"] = 'SecretRingKey.gpg'
- 在项目级目录中, 创建一个包含两个文件的新目录
scripts:publish-root.gradle和publish-module.gradle - 在
publish-root.gradle中, 添加以下代码:
// Create variables with empty default values
ext["signing.keyId"] = ''
ext["signing.password"] = ''
ext["signing.secretKeyRingFile"] = ''
ext["ossrhUsername"] = ''
ext["ossrhPassword"] = ''
ext["sonatypeStagingProfileId"] = ''
File secretPropsFile = project.rootProject.file('local.properties')
if (secretPropsFile.exists()) {
// Read local.properties file first if it exists
Properties p = new Properties()
new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }
p.each { name, value -> ext[name] = value }
} else {
// Use system environment variables
ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE')
}
// Set up Sonatype repository
nexusPublishing {
repositories {
sonatype {
stagingProfileId = sonatypeStagingProfileId
username = ossrhUsername
password = ossrhPassword
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
}
}
}
- 在
publish-module.gradle中添加如下代码:
apply plugin: 'maven-publish'
apply plugin: 'signing'
task androidSourcesJar(type: Jar) {
archiveClassifier.set('sources')
if (project.plugins.findPlugin("com.android.library")) {
from android.sourceSets.main.java.srcDirs
} else {
from sourceSets.main.java.srcDirs
}
}
artifacts {
archives androidSourcesJar
}
group = PUBLISH_GROUP_ID
version = PUBLISH_VERSION
afterEvaluate {
publishing {
publications {
release(MavenPublication) {
// The coordinates of the library, being set from variables that
// we'll set up later
groupId PUBLISH_GROUP_ID
artifactId PUBLISH_ARTIFACT_ID
version PUBLISH_VERSION
// Two artifacts, the `aar` (or `jar`) and the sources
if (project.plugins.findPlugin("com.android.library")) {
from components.release
} else {
artifact("$buildDir/libs/${project.getName()}-${version}.jar")
}
artifact androidSourcesJar
// Mostly self-explanatory metadata
pom {
name = PUBLISH_ARTIFACT_ID
description = PUBLISH_DESCRIPTION
url = PUBLISH_URL
licenses {
license {
name = PUBLISH_LICENSE_NAME
url = PUBLISH_LICENSE_URL
}
}
developers {
developer {
id = PUBLISH_DEVELOPER_ID
name = PUBLISH_DEVELOPER_NAME
email = PUBLISH_DEVELOPER_EMAIL
}
}
scm {
connection = PUBLISH_SCM_CONNECTION
developerConnection = PUBLISH_SCM_DEVELOPER_CONNECTION
url = PUBLISH_SCM_URL
}
}
}
}
}
}
ext["signing.keyId"] = rootProject.ext["signing.keyId"]
ext["signing.password"] = rootProject.ext["signing.password"]
ext["signing.secretKeyRingFile"] = rootProject.ext["signing.secretKeyRingFile"]
signing {
sign publishing.publications
}
- 在库
build.gradle(mylibrary ->build.gradle)中, 添加以下代码, 并输入你的证书和 GitHub URL:
ext {
PUBLISH_GROUP_ID = 'io.github.yourProfile'
PUBLISH_VERSION = '1.0.0'
PUBLISH_ARTIFACT_ID = 'libraryName'
PUBLISH_DESCRIPTION = 'mylibrary Android SDK'
PUBLISH_URL = 'https://github.com/username/libraryName'
PUBLISH_LICENSE_NAME = 'Apache License'
PUBLISH_LICENSE_URL = 'https://github.com/username/libraryName/blob/master/LICENSE'
PUBLISH_DEVELOPER_ID = 'userName'
PUBLISH_DEVELOPER_NAME = 'YOUR NAME'
PUBLISH_DEVELOPER_EMAIL = 'email@email.com'
PUBLISH_SCM_CONNECTION = 'scm:git:github.com/username/libraryName.git'
PUBLISH_SCM_DEVELOPER_CONNECTION =
'scm:git:ssh://github.com/userName/libraryName.git'
PUBLISH_SCM_URL =
'https://github.com/username/libraryName/tree/master'
}
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
- 在
local.properties中设置 sonatype 凭据(在此处添加 sonatype 账户凭据作为每个属性的值)
ossrhUsername=<SonatypeAccountName>
ossrhPassword=<SonatypeAccountPassword>
sonatypeStagingProfileId=<SonatypeStagingProfileId>
上传到Staging仓库
- 在运行发布命令之前, 需要将项目推送到之前创建的 git 仓库.
- 运行以下命令发布发布版本(确保在项目根目录下运行此命令):
gradlew mylibrary:publishReleasePublicationToMavenRepository
- 成功执行上述命令(BUILD SUCCESSFUL)后, 你现在可以在 s01.oss.sonatype.org/ 的
Staging Repositories部分看到一个仓库.
发布库
- 访问 s01.oss.sonatype.org/ 并使用凭据登录.
- 在
Staging Repositories中选择要发布的仓库. - 点击
close并给出描述, 然后点击submit. - 检查关闭活动是否完成, 点击
release并给出说明. - 就这样! 我们的库(仓库)发布了!! 现在我们可以通过
Artifact search找到我们的库了.
将发布的库实施到测试项目中
- 创建测试项目
- 在应用
build.gradle中添加以下代码(根据你的需要更改 groupId, artifactId 和版本)
implementation "io.github.karthikrathinavel:mylibrary:1.0.0"
//implementation "groupId:artifactId:version"
感谢阅读哦! Stay GOLDEN!