声明:经过下面一系列操作之后,以后想发布新版本,只要修改好要升级的版本,然后在 Maven的 Lifecycle 里双击 deploy 即可~


前言

自使用maven以来,没少使用maven中央仓库中的各种jar包,方便有效,但是咱们也不能总是只取不予,也应该懂得奉献,当你写好了一个十分好用的jar包,想贡献出去给大家使用的时候,应该怎么做呢?当然是发布到maven的中央仓库了,不过要说这个发布过程,还真是比较复杂,本文就来详细说下如何发布jar包到maven中央仓库。 


注意事项

1、工单管理:​https://issues.sonatype.org/secure/Dashboard.jspa​

说明:注册账号、创建和管理issue,Jar包的发布是以解决issue的方式起步的。这里的用户名与密码是非常重要的,后面会用到,一定要保存好。

2、构件仓库:​https://oss.sonatype.org/#welcome​

说明:算是正式发布前的一个过段仓库,使用maven提交后的jar包先到这个库中。

3、镜像仓库:​http://search.maven.org​

说明:最终成功发布的jar可以在这里搜到。


一、创建工单

在上述的​​工单管理​​​的地址中进行创建,如果没有账号,需要先注册一个,记住用户名密码,后边要配置到​​setting.xml​​中。 

Create Issue 填写内容说明: 

Maven - 发布JAR包到Maven远程中央仓库_Maven

===Step 1===
Project:Community Support - Open Source Project Repository Hosting
Issue Type:New Project

===Step 2===
Summary:JAR包名称,如:requestjson
Group Id:你懂得,不用多说,如com.luxsuen
Project URL:项目站点,如:https://github.com/LuxSun/requestjson
SCM url:项目源码仓库,如:https://github.com/LuxSun/requestjson.git

 其他内容不用填写,创建Issue后需要等待一小段时间,Sonatype的工作人员审核处理,速度还是很快的,一般一个工作日以内,当Issue的Status变为RESOLVED后,就可以进行下一步操作了,否则,就等待… 

到这里,需要跟客服唠嗑一下~(以前这一步审核很简单,跟客服说是你的即可,现在开始要提供证明)

1、如果是用Github,就无需注册域名,直接根据客服提示证明一下即可(新手推荐)。

2、如果是用自己的域名,就需要根据客服的提示配置下DNS。

Ps:无论以上是哪种情况,最后只要呈现如下图所示,即代表成功!

Maven - 发布JAR包到Maven远程中央仓库_json_02


二、配置Maven(pom.xml)

接下来就是重头戏了,pom.xml是一个maven项目的重点配置,一个项目的所有配置都可以由这个文件来描述,文件中的所有配置都有默认值,也就是说所有的配置都是可选配置,但是为了把构件发布到中央仓库,我们必须配置一些关键信息,否则再发布时是不会通过了。

在工程的pom.xml文件中,引入Sonatype官方的一个通用配置​​oss-parent​​,这样做的好处是很多pom.xml的发布配置不需要自己配置了:

<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>

并增加Licenses、SCM、Developers信息:

<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>

<developers>
<developer>
<name>Lux Sun</name>
<email>28554482@qq.com</email>
<organization>Lux Sun</organization>
<url>https://github.com/LuxSun</url>
</developer>
</developers>

<scm>
<url>https://github.com/LuxSun/requestjson</url>
<connection>https://github.com/LuxSun/requestjson.git</connection>
</scm>

修改maven配置文件setting.xml,在servers中增加server配置。

<servers>
<server>
<id>sonatype-nexus-snapshots</id>
<username>Sonatype 账号</username>
<password>Sonatype 密码</password>
</server>

<server>
<id>sonatype-nexus-staging</id>
<username>Sonatype 账号</username>
<password>Sonatype 密码</password>
</server>
</servers>

根据官方指南,这里需要4个插件,

  1. maven-source-plugin 用来生成Source Jar文件
  2. maven-javadoc-plugin 用来生成 javadoc 文档
  3. maven-gpg-plugin 用来对工程文件进行自动签名
  4. nexus-staging-maven-plugin 用来将工程发布到中央仓库

另外注意生成javadoc文档时需要指定关闭doclint,不然可能因为使用了不规范的javadoc注解而导致失败,完整配置如下。

完整版

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.luxsuen</groupId>
<artifactId>requestjson</artifactId>
<version>1.0.1</version>
<packaging>jar</packaging>

<name>requestjson</name>
<description>RequestJson</description>
<url>https://github.com/LuxSun/requestjson</url>

<properties>
<spring.version>4.3.2.RELEASE</spring.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>

<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>

<developers>
<developer>
<name>Lux Sun</name>
<email>28554482@qq.com</email>
<organization>Lux Sun</organization>
<url>https://github.com/LuxSun</url>
</developer>
</developers>

<scm>
<url>https://github.com/LuxSun/requestjson</url>
<connection>https://github.com/LuxSun/requestjson.git</connection>
</scm>

<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<name>oss Snapshots Repository</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<name>oss Staging Repository</name>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>default-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>**/spring/</exclude>
<exclude>**/com/luxsuen/requestjson/entity/</exclude>
<exclude>**/com/luxsuen/requestjson/web/</exclude>
<exclude>**/META-INF/*.kotlin_module</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
<configuration>
<excludes>
<exclude>**/spring/</exclude>
<exclude>**/com/luxsuen/requestjson/entity/</exclude>
<exclude>**/com/luxsuen/requestjson/web/</exclude>
<exclude>**/META-INF/*.kotlin_module</exclude>
</excludes>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<excludePackageNames>com.luxsuen.requestjson.entity:com.luxsuen.requestjson.web</excludePackageNames>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.7</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

注意1:以上 pom.xml 必须包括:name、description、url、licenses、developers、scm 等基本信息,此外,使用了 Maven 的 profile 功能,只有在 release 的时候,创建源码包、创建文档包、使用 GPG 进行数字签名。此外,snapshotRepository 与 repository 中的 id 一定要与 setting.xml 中 server 的 id 保持一致。

注意2:这里在 nexus-staging-maven-plugin 插件里开启了自动 Release。也可以关掉,然后登录构件仓库 ​​https://oss.sonatype.org​​ 手动去 close 然后 relseae。


三、配置gpg-key

1、可视化配置

Windows的话先到 ​​https://gpg4win.org/download.html​​ 下载最新的版本,直接安装即可打开即可。

1.1、新建密钥对(OpenPGP类型)

这里需要填入名字和电子邮件,然后注意可以在高级设置那里将有效期一栏的小勾取消掉,即有效期无限,如下。

Maven - 发布JAR包到Maven远程中央仓库_maven_03

1.2、确认信息并输入密码

1.3、选择将公钥上传到目录服务

注意:如果这里不点的话后面分发后将无法在服务器找到对应公钥。

Maven - 发布JAR包到Maven远程中央仓库_maven_04

1.4、右键选择生成的证书在服务器上发布

1.5、根据密钥ID在服务器上查找公钥

最终必须保证这一步成功!如下。

Maven - 发布JAR包到Maven远程中央仓库_maven_05

Ps:注意:这里有个bug,如果生成的密钥ID的第一个字符是数字0的话,在下面使用maven-gpg插件的时候可能会被自动去掉导致密钥找不到,所以保险起见遇到这种情况就重新生成证书。


2、命令行配置

Ps:Windows系统使用“gpg”命令开头,而Mac系统使用“gpg2”命令开头。

如果是使用的windows,可以下载gpg4win,地址:​​https://www.gpg4win.org/download.html​​​,安装后在命令行中执行 ​​gpg --gen-key​​生成,过程中需要填写名字、邮箱等,其他步骤可以使用默认值,不过有个叫:Passphase的参数需要记住,这个相当于是是密钥的密码,下一步发布过程中进行签名操作的时候会用到。 

Maven - 发布JAR包到Maven远程中央仓库_Maven_06

建议大家下载 Gpg4win-Vanilla 版本,因为它仅包括 GnuPG,这个工具才是我们所需要的。

安装 GPG 软件后,打开命令行窗口,依次做以下操作:

2.1、查看是否安装成功

gpg --version

能够显示 GPG 的版本信息,说明安装成功了。

2.2、生成密钥对

gpg --gen-key

此时需要输入姓名、邮箱等字段,其它字段可使用默认值,此外,还需要输入一个 Passphase,相当于一个密钥库的密码,一定不要忘了,也不要告诉别人,最好记下来,因为后面会用到。

2.3、查看公钥

gpg --list-keys

输出如下信息(新版本可能格式会有点区别,不用担心,是正常的):

C:/Users/huangyong/AppData/Roaming/gnupg/pubring.gpg
----------------------------------------------------
pub 2048R/82DC852E 2014-04-24
uid hy_think <hy_think@163.com>
sub 2048R/3ACA39AF 2014-04-24

可见这里的公钥的 ID 是:82DC852E,很明显是一个 16 进制的数字,马上就会用到。

2.4、将公钥发布到 PGP 密钥服务器(后面还需要操作此步骤)

gpg --keyserver hkp://pool.sks-keyservers.net --send-keys 82DC852E

此后,可使用本地的私钥来对上传构件进行数字签名,而下载该构件的用户可通过上传的公钥来验证签名,也就是说,大家可以验证这个构件是否由本人上传的,因为有可能该构件被坏人给篡改了。

2.5、查询公钥是否发布成功

gpg --keyserver hkp://pool.sks-keyservers.net --recv-keys 82DC852E

实际上就是从 key server 上通过公钥 ID 来接收公钥,此外,也可以到 sks-keyservers.net 上通过公钥 ID 去查询。


四、上传构件到 OSS 中

这步就简单了,就是一套命令:

mvn clean deploy -P sonatype-oss-release -Darguments="gpg.passphrase=密钥密码"

如果使用eclipse的mvn插件发布的话,配置如下(IDEA类似):

 Maven - 发布JAR包到Maven远程中央仓库_maven_07

如果发布成功,就可以到构件仓库中查看了。

或者

先通过 cmd 进入到项目的这个目录下。

Maven - 发布JAR包到Maven远程中央仓库_json_08

再执行下面命令。

mvn clean deploy -P release

当执行以上 Maven 命令时,会自动弹出一个对话框,需要输入上面提到的 Passphase,它就是通过 GPG 密钥对的密码,只有自己才知道。随后会看到大量的 upload 信息,而且速度比较慢,经常会 timeout,需要反复尝试。

注意:此时上传的构件并未正式发布到中央仓库中,只是部署到 OSS 中了,下面才是真正的发布。


五、在 OSS 中发布构件

进入​​https://oss.sonatype.org/#stagingRepositories​​​查看发布好的构件,点击左侧的​​Staging Repositories​​​,一般最后一个就是刚刚发布的jar了,此时的构件状态为​​open​​。 

打开命令行窗口,查看gpg key并上传到第三方的key验证库:

E:\98_code\workSpace\marathon-client>gpg --list-keys
C:/Users/VF/AppData/Roaming/gnupg/pubring.gpg
---------------------------------------------
pub 2048R/824B4D7A 2016-01-06
uid [ultimate] cloudnil <cloudnil@126.com>
sub 2048R/7A10AD69 2016-01-06


E:\98_code\workSpace\marathon-client>gpg --keyserver hkp://keyserver.ubuntu.com:11371 --send-keys 824B4D7A
gpg: sending key 824B4D7A to hkp server keyserver.ubuntu.com

E:\98_code\workSpace\marathon-client>

回到 ​​https://oss.sonatype.org/#stagingRepositories​​,选中刚才发布的构件,并点击上方的close–>Confirm,在下边的Activity选项卡中查看状态,当状态变成closed后,执行Release–>Confirm,并在下边的Activity选项卡中查看状态,成功后构件自动删除。

需要在曾经创建的 Issue 下面回复一条“构件已成功发布”的评论,这是为了通知 Sonatype 的工作人员为需要发布的构件做审批,发布后会关闭该 Issue。

一小段时间(约1-2个小时)后即可同步到​​https://search.maven.org​​​中央仓库(Maven中央仓库可能需要24-48小时:​​https://mvnrepository.com​​)。

Maven - 发布JAR包到Maven远程中央仓库_json_09

Maven - 发布JAR包到Maven远程中央仓库_apache_10

Ps:注意这里有个大坑,之前很多教程都会把这个步骤提前到最开始(我不是说生成密钥这个环节,这个密钥生成环节的确是一开始就可以弄了的),但下面这句解析需要到这一步再开始搞,之前一直在https://oss.sonatype.org/#stagingRepositories这里审核一直失败,大坑呀~

最后,想说一句:第一次都是很痛的,以后就舒服了。没错,只有第一次发布才如此痛苦,以后 deploy 的构件会自动部发布到中央仓库,无需再这样折腾了。(附加:成功效果图)

Maven - 发布JAR包到Maven远程中央仓库_maven_11

Maven - 发布JAR包到Maven远程中央仓库_Maven_12


附:

在编码过程中,有些通用的代码模块,有时候我们不想通过复制拷贝来粗暴地复用,因为这样不仅体现不了变化,也不利于统一管理。这里我们使用maven deploy的方式,将通用的模块打成jar包,发布到nexus,让其他的项目来引用,以更简洁高效的方式来实现复用和管理。

第一:maven的settings.xml文件中设置<server>标签

<server>
<id>my-deploy-release</id>
<username>admin</username>
<password>admin123</password>
</server>

<server>
<id>my-deploy-snapshot</id>
<username>admin</username>
<password>admin123</password>
</server>

此处设置的用户名和密码都是nexus的登陆配置。


第二:在项目的pom.xml文件中设置

<distributionManagement>
<repository>
<id>my-deploy-release</id>
<url>http://192.168.1.123:8081/nexus/content/repositories/releases/</url>
</repository>

<snapshotRepository>
<id>my-deploy-snapshot</id>
<url>http://192.168.1.123:8081/nexus/content/repositories/snapshots/</url>
</snapshotRepository>
</distributionManagement>

在此,url都是nexus相应仓库的链接地址,这一步做完之后,已经完成了发布所需要的基本配置。【试试命令:mvn deploy

注意:<server>中的<id>要和<repository>、<snapshotRepository>的<id>一致,maven在发布时,会根据此id来查找相应的用户名密码进行登录验证并上传文件。


第三:发布的灵活性配置

maven会判断版本后面是否带了-SNAPSHOT,如果带了就发布到snapshots仓库,否则发布到release仓库。这里我们可以在pom.xml文件中,设置。

<groupId>com.test</groupId>
<artifactId>my-test</artifactId>
<packaging>jar</packaging>
<version>${project.release.version}</version>

<properties>
<java.version>1.8</java.version>
<project.release.version>1.0-SNAPSHOT</project.release.version>
</properties>

<profiles>
<profile>
<id>product</id>
<properties>
<project.release.version>1.0</project.release.version>
</properties>
</profile>
</profiles>

说明:通过占位符${project.release.version}来控制需要发布的版本,用命令mvn deploy -P product,发布my-test的1.0版本到releases库。如果使用命令mvn deploy,则默认使用 1.0-SNAPSHOT版本号,将发布my-test的1.0-SNAPSHOT版本到snapshots库。


第四:发布时遇到的一些问题

  1. 部署到snapshot仓库时,jar包会带上时间戳,这没关系,maven会自动取相应版本最新的jar包;
  2. Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy (default-deploy) on project my-test: Failed to deploy artifacts: Could not transfer artifact...from/to release...部署到release仓库时,相同版本的jar包不能提交。
    原因:因为release的部署策略是【disable redeploy】,不允许覆盖更新组件。
    解决办法:修改一下版本号,再提交即可。之前其实还可以通过人工客服聊天帮忙删除不想要的Jar包,但是现在不行了,只能通过上传新版本,所以有些童鞋想做测试的需要注意这一点。
  3. 打包指定的文件/文件夹在代码中也有体现(含:doc、sources、class)。 
  4. Maven deploy 时,为什么没有生成对应的 javadoc 和 sources (前提已经有插件部署好)?

    Maven - 发布JAR包到Maven远程中央仓库_apache_13

    Ps:也许是图上的 id = “release” 没有打勾造成没有执行 javadoc 和 sources 的插件。

  5. 待更新...