maven 项目发布 jar 包至中央仓库

记录 maven 项目发布 jar 包至中央仓库的过程。

推荐我的第一个开源项目:https://github.com/llnancy/mojian,魔剑 - 业务开发工具集。

注册 sonatype 帐号

sonatype 通过 Jira 系统管理任务。访问地址:https://issues.sonatype.org/secure/Dashboard.jspa

  • Email:个人邮箱。sonatype 上的每个步骤都会通知到该邮箱。
  • Full name:姓名。
  • Username:登录账户名。(记录下来,后面会用到)
  • Password:登录密码。(规则较复杂,至少 12 位,且包含大小写和特殊字符,记录下来,避免遗忘)

注册完成后首次登录时,会进入语言选择界面,此处选择只会影响 Jira 系统界面的语言。

创建问题

点击页面顶部新建按钮创建问题,在弹出的模态框中填写以下信息:

  • Project:项目类型。选择 Community Support - Open Source Project Repository Hosting (OSSRH)
  • Issue Type:问题类型。选择 New Project
  • Summary:摘要。填写 jar 包简要信息。
  • Description:描述(可选)。
  • Group Id:组织 ID。可填写自定义域名或者 GitHub 二级域名。需和 jar 包中 pom.xml 文件中的 groupId 保持一致。
  • Project URL:项目主页。可填写代码仓库或文档地址。
  • SCM url:项目 GitHub 地址。

Group ID

自定义域名

如果拥有自定义域名,则可将域名反写作为 Group Id,例如域名为 lilu.org.cn,则 Group Id 填写 cn.org.lilu

GitHub 二级域名

GitHub 默认给每个账户提供一个二级域名,例如我的 GitHub 账户为 llnancy,则二级域名为 https://llnancy.github.io,对应 Group Idio.github.llnancy

机器人回复

问题创建完成后,初始为 OPEN 状态,耐心等待机器人回复,机器人回复后状态变为 Waiting for Response,根据回复内容验证自定义域名或 GitHub 二级域名的所有权。

  • 自定义域名添加 TXT 域名解析。
  • GitHub 二级域名创建指定仓库。

完成后点击 Respond 等待机器人审核(问题状态重新变为 Open)。

审核通过

审核通过后,机器人会添加一条评论,告知我们可以发布 jar 包了。问题状态变更为 Resolved

配置 GPG 秘钥

Mac 为例。

安装 gpg

1
brew install gpg

生成秘钥对:

1
gpg --gen-key

按照提示输入用户名、邮箱和密码(需记住,配置时会用到)。

执行完成后会得到以下 pub 串:

1
2
3
4
pub   ed25519 2022-12-08 [SC] [有效至:2024-12-07]
589EC2518C91A4765DCA9D9BCAF3EBDCFAC4F733
uid llnancy <admin@lilu.org.cn>
sub cv25519 2022-12-08 [E] [有效至:2024-12-07]

其中 589EC2518C91A4765DCA9D9BCAF3EBDCFAC4F733 是公钥 ID

发送公钥 ID 至秘钥服务器:

1
gpg --keyserver keyserver.ubuntu.com --send-keys 589EC2518C91A4765DCA9D9BCAF3EBDCFAC4F733

为防止更换电脑或其它原因遗失秘钥,最好将公私钥进行导出并生成吊销证书单独保存:

1
2
3
4
5
6
# 导出公钥
gpg -a -o public.key --export 589EC2518C91A4765DCA9D9BCAF3EBDCFAC4F733
# 导出私钥
gpg -a -o private.key --export-secret-keys 589EC2518C91A4765DCA9D9BCAF3EBDCFAC4F733
# 生成吊销证书
gpg --gen-revoke -ao revoke.gpg 589EC2518C91A4765DCA9D9BCAF3EBDCFAC4F733

maven 配置

setting.xml 配置

server 节点配置 sonatype 账号密码:

1
2
3
4
5
6
7
8
9
<servers>
<server>
<!-- id 与 pom.xml 中 distributionManagement 节点下的 id 对应 -->
<id>ossrh</id>
<!-- https://issues.sonatype.org/ 的账号和密码 -->
<username>${username}</username>
<password>${password}</password>
</server>
</servers>

profile 节点配置 gpg 密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<profiles>
<profile>
<!-- id 与 pom.xml 中 maven-gpg-plugin 插件中的 id 对应-->
<id>ossrh</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<gpg.executable>gpg</gpg.executable>
<!-- 生成 gpg 秘钥对时的密码 -->
<gpg.passphrase>{password}</gpg.passphrase>
</properties>
</profile>
</profiles>

项目 pom.xml 配置

POM GAV 坐标:

1
2
3
4
5
6
<!-- ====================================================== -->
<!-- POM GAV -->
<!-- ====================================================== -->
<groupId>io.github.llnancy</groupId>
<artifactId>mojian-parent</artifactId>
<version>${revision}</version>

项目名称、描述和 url

1
2
3
4
5
6
<!-- ====================================================== -->
<!-- 项目名和组织 -->
<!-- ====================================================== -->
<name>mojian</name>
<description>魔剑 - 业务开发工具集</description>
<url>https://github.com/llnancy/mojian</url>

开源许可证信息:

1
2
3
4
5
6
7
8
9
<!-- ====================================================== -->
<!-- 开源许可证 -->
<!-- ====================================================== -->
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>

开发者信息:

1
2
3
4
5
6
7
8
9
10
<!-- ====================================================== -->
<!-- 开发者介绍 -->
<!-- ====================================================== -->
<developers>
<developer>
<name>llnancy</name>
<email>admin@lilu.org.cn</email>
<url>https://lilu.org.cn</url>
</developer>
</developers>

scm 信息:

1
2
3
4
5
6
7
8
<!-- ====================================================== -->
<!-- SCM 信息 -->
<!-- ====================================================== -->
<scm>
<url>https://github.com/llnancy/mojian</url>
<connection>scm:git:https://github.com/llnancy/mojian.git</connection>
<developerConnection>scm:git:https://github.com/llnancy/mojian.git</developerConnection>
</scm>

源码包和文档插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- 源码包插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- javadoc 插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>

GPG 签名插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- GPG 签名插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<!-- id 与 setting.xml 的 gpg 配置中的 id 对应 -->
<id>ossrh</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>

Nexus 部署插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- Nexus 部署插件 -->
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.13</version>
<extensions>true</extensions>
<configuration>
<!-- 与 setting.xml 的 server 配置中的 ossrh 对应-->
<serverId>ossrh</serverId>
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>

远程仓库配置:

1
2
3
4
5
6
7
8
9
10
11
12
<distributionManagement>
<!-- 快照版本 -->
<snapshotRepository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<!-- 正式版本 -->
<repository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>

完整 pom.xml 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<?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">
<!-- ====================================================== -->
<!-- POM 版本 -->
<!-- ====================================================== -->
<modelVersion>4.0.0</modelVersion>

<!-- ====================================================== -->
<!-- POM GAV -->
<!-- ====================================================== -->
<groupId>io.github.llnancy</groupId>
<artifactId>mojian-parent</artifactId>
<version>${revision}</version>

<!-- ====================================================== -->
<!-- POM 类型 -->
<!-- ====================================================== -->
<packaging>pom</packaging>

<!-- ====================================================== -->
<!-- 项目名和组织 -->
<!-- ====================================================== -->
<name>mojian</name>
<description>魔剑 - 业务开发工具集</description>
<url>https://github.com/llnancy/mojian.git</url>

<!-- ====================================================== -->
<!-- SCM 信息 -->
<!-- ====================================================== -->
<scm>
<url>https://github.com/llnancy/mojian</url>
<connection>scm:git:https://github.com/llnancy/mojian.git</connection>
<developerConnection>scm:git:https://github.com/llnancy/mojian.git</developerConnection>
</scm>

<!-- ====================================================== -->
<!-- 开发者介绍 -->
<!-- ====================================================== -->
<developers>
<developer>
<name>llnancy</name>
<email>admin@lilu.org.cn</email>
<url>https://lilu.org.cn</url>
</developer>
</developers>

<!-- ====================================================== -->
<!-- 开源许可证 -->
<!-- ====================================================== -->
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>

<!-- ====================================================== -->
<!-- 多模块 -->
<!-- ====================================================== -->
<modules>
<module>mojian-all</module>
<module>mojian-base</module>
<module>mojian-bom</module>
<module>mojian-log</module>
<module>mojian-web</module>
</modules>

<!-- ====================================================== -->
<!-- 组件依赖管理 -->
<!-- ====================================================== -->
<properties>
<java.version>8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<revision>0.0.1</revision>
<mojian.version>${revision}</mojian.version>
<springboot.version>2.7.6</springboot.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

<distributionManagement>
<!-- 快照版本 -->
<snapshotRepository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<!-- 正式版本 -->
<repository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>

<!-- ====================================================== -->
<!-- 项目构建(包名/默认目标/插件等) -->
<!-- ====================================================== -->
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 源码包插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- javadoc 插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- GPG 签名插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<!-- id 与 setting.xml 的 gpg 配置中的 id 对应 -->
<id>ossrh</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Nexus 部署插件 -->
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.13</version>
<extensions>true</extensions>
<configuration>
<!-- 与 setting.xml 的 server 配置中的 ossrh 对应-->
<serverId>ossrh</serverId>
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
<autoReleaseAfterClose>true</autoReleaseAfterClose>
</configuration>
</plugin>
<!-- 单元测试插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<skipTests>true</skipTests> <!-- 跳过单元测试 -->
</configuration>
</plugin>
<!-- revision 版本号统一插件 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.2.7</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

发布

1
mvn clean deploy

发布成功后,可以登录 https://s01.oss.sonatype.org 根据 Group Id 搜索到对应 jar 包。账号密码是 sonatype 系统的账号密码。

然后回到 https://issues.sonatype.org/projects/OSSRH/issues 对应的问题页,发布一条评论告知已发布第一个版本的 jar 包,等待机器人回复后自动激活中央仓库的同步。之后我们就可以在 https://repo1.maven.org/maven2 中找到我们的 jar 包,国内仓库会陆续进行同步。