如何将Android Library发布到Sonatype Nexus的Maven Central Repository

639 阅读6分钟

大家好. 在这篇文章中, 我将解释如何使用 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 中, 尝试调用mylibrarygreet方法. 如果运行正常, 我们就可以继续设置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 responseopened之类的状态. 如果仓库被批准, 状态将变为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 工具, Homebrewgnupg生成密钥, 如果你不愿意使用 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.gradlepublish-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!