本文将详细介绍如何将一个托管在 Gitee 上的开源 Java 项目发布到 Maven Central 中央仓库,涵盖账号注册、GPG 密钥配置、POM 文件配置以及部署脚本等完整流程。
目录
- 前置准备
- [注册 Sonatype 账号](#注册 Sonatype 账号)
- 验证命名空间
- [GPG 密钥安装与配置(Mac版)](#GPG 密钥安装与配置(Mac版))
- [Maven settings.xml 配置](#Maven settings.xml 配置)
- [POM 文件配置](#POM 文件配置)
- 执行部署
- 常见问题与解决方案
1. 前置准备
在开始之前,请确保你已具备以下条件:
- Java 开发环境 :JDK 8+ 已安装并配置好
JAVA_HOME - Maven 环境 :Maven 3.6+ 已安装并配置好
M2_HOME - Gitee 账号:已有开源项目托管在 Gitee 上
- 有效的邮箱:用于注册 Sonatype 账号和 GPG 密钥
2. 注册 Sonatype 账号
2.1 访问 Sonatype Central Portal
访问 https://central.sonatype.com/ 并注册账号。
注意:2024年起,Sonatype 启用了新的 Central Portal,取代了原有的 OSSRH (oss.sonatype.org)。
2.2 登录方式
支持以下登录方式:
- GitHub 账号登录
- Google 账号登录
- 邮箱注册登录
推荐使用 GitHub 登录,便于后续验证命名空间。
3. 验证命名空间
3.1 什么是命名空间?
命名空间即你的 groupId,例如 io.gitee.ziqistudio。Maven Central 要求你必须证明拥有该命名空间的所有权。
3.2 Gitee 命名空间验证
对于 Gitee 项目,命名空间格式为:io.gitee.{用户名或组织名}
验证步骤:
- 登录 Sonatype Central Portal
- 进入 Namespaces 页面
- 点击 Add Namespace
- 输入命名空间,如
io.gitee.ziqistudio - 系统会生成一个验证码(Verification Key)
- 在 Gitee 上创建一个与验证码同名的公开仓库
- 回到 Sonatype 点击 Verify
示例:
验证码:abc123xyz
创建仓库:https://gitee.com/ziqistudio/abc123xyz
验证成功后,状态会变为 Verified。
4. GPG 密钥安装与配置(Mac版)
4.1 安装 GPG
使用 Homebrew 安装 GPG:
bash
# 安装 GPG
brew install gnupg
# 安装 pinentry-mac(用于密码输入)
brew install pinentry-mac
# 验证安装
gpg --version
4.2 生成 GPG 密钥对
bash
# 生成密钥(推荐使用 RSA 4096)
gpg --full-generate-key
按照提示操作:
请选择您要使用的密钥类型:
(1) RSA 和 RSA
(2) DSA 和 Elgamal
(3) DSA(仅用于签名)
(4) RSA(仅用于签名)
(9) ECC(签名和加密)
(10) ECC(仅用于签名)
您的选择是? 1
RSA 密钥的长度应在 1024 位与 4096 位之间。
您想要使用的密钥长度?(3072) 4096
请设定这个密钥的有效期限。
0 = 密钥永不过期
密钥的有效期限是?(0) 0
真实姓名: Your Name
电子邮件地址: your-email@example.com
注释: Maven Central Deploy Key
# 设置一个强密码(passphrase),后续部署时需要使用
4.3 查看密钥信息
bash
# 列出所有密钥
gpg --list-keys
# 列出私钥及 keygrip(用于免密配置)
gpg --list-secret-keys --keyid-format LONG
输出示例:
/Users/username/.gnupg/pubring.kbx
----------------------------------
pub rsa4096/14C5CF1B443259C5 2024-01-01 [SC]
14C5CF1B443259C5E6028F87492BAC9CB7397FFA
uid [ultimate] Your Name <your-email@example.com>
sub rsa4096/XXXXXXXXXXXXXXXX 2024-01-01 [E]
其中 14C5CF1B443259C5E6028F87492BAC9CB7397FFA 是完整的密钥 ID。
4.4 上传公钥到密钥服务器
Maven Central 要求公钥必须上传到公开的密钥服务器:
bash
# 上传到多个密钥服务器(确保可用性)
gpg --keyserver keyserver.ubuntu.com --send-keys 14C5CF1B443259C5E6028F87492BAC9CB7397FFA
gpg --keyserver keys.openpgp.org --send-keys 14C5CF1B443259C5E6028F87492BAC9CB7397FFA
gpg --keyserver pgp.mit.edu --send-keys 14C5CF1B443259C5E6028F87492BAC9CB7397FFA
4.5 配置 GPG Agent(解决 /dev/tty 问题)
在 macOS 上,非交互式环境(如 IDE 或 CI)中运行 Maven 时,可能遇到以下错误:
gpg: cannot open '/dev/tty': Device not configured
解决方案:
- 配置 gpg.conf
bash
# 创建或编辑 ~/.gnupg/gpg.conf
echo "use-agent" >> ~/.gnupg/gpg.conf
echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf
- 配置 gpg-agent.conf
bash
# 创建或编辑 ~/.gnupg/gpg-agent.conf
cat >> ~/.gnupg/gpg-agent.conf << 'EOF'
allow-loopback-pinentry
default-cache-ttl 600
max-cache-ttl 7200
EOF
- 重启 GPG Agent
bash
gpg-connect-agent reloadagent /bye
- 预缓存密码(可选)
bash
# 使用此命令预缓存密码,避免每次都输入
echo "test" | gpg --batch --yes --pinentry-mode loopback --passphrase "YOUR_PASSPHRASE" --sign > /dev/null 2>&1
4.6 导出密钥(备份)
bash
# 导出公钥
gpg --armor --export your-email@example.com > public-key.asc
# 导出私钥(妥善保管!)
gpg --armor --export-secret-keys your-email@example.com > private-key.asc
5. Maven settings.xml 配置
5.1 生成 Sonatype Token
- 登录 Sonatype Central Portal
- 点击右上角用户头像 → View Account
- 进入 Generate User Token
- 记录生成的
username和password(Token)
5.2 配置 settings.xml
编辑 ~/.m2/settings.xml:
xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0
https://maven.apache.org/xsd/settings-1.2.0.xsd">
<servers>
<!-- Sonatype Central Portal 认证 -->
<server>
<id>sonatype</id>
<!-- 使用 Token 认证(推荐) -->
<username>生成的Token用户名</username>
<password>生成的Token密码</password>
</server>
</servers>
<profiles>
<profile>
<id>gpg</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<!-- GPG 可执行文件路径(Mac 通常为 gpg) -->
<gpg.executable>gpg</gpg.executable>
<!-- GPG 密钥 ID -->
<gpg.keyname>14C5CF1B443259C5E6028F87492BAC9CB7397FFA</gpg.keyname>
<!-- 可选:配置密码(不推荐明文存储) -->
<!-- <gpg.passphrase>YOUR_PASSPHRASE</gpg.passphrase> -->
</properties>
</profile>
</profiles>
<activeProfiles>
<activeProfile>gpg</activeProfile>
</activeProfiles>
</settings>
5.3 密码加密(推荐)
使用 Maven 密码加密功能保护密码:
bash
# 创建主密码
mvn --encrypt-master-password
# 将输出保存到 ~/.m2/settings-security.xml
cat > ~/.m2/settings-security.xml << 'EOF'
<settingsSecurity>
<master>{加密后的主密码}</master>
</settingsSecurity>
EOF
# 加密服务器密码
mvn --encrypt-password
6. POM 文件配置
6.1 必需的元数据
Maven Central 对 POM 文件有严格要求,必须包含以下信息:
xml
<?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>
<!-- 必需:GAV 坐标 -->
<groupId>io.gitee.ziqistudio</groupId>
<artifactId>spring-boot-starter-exception-i18n</artifactId>
<version>3.0.1</version>
<packaging>jar</packaging>
<!-- 必需:项目名称和描述 -->
<name>Spring Boot Starter - Exception I18N</name>
<description>Spring Boot Starter for exception internationalization</description>
<!-- 必需:项目 URL -->
<url>https://gitee.com/ziqistudio/spring-exception-i18n</url>
<!-- 必需:许可证信息 -->
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<!-- 必需:开发者信息 -->
<developers>
<developer>
<name>ziqi studio</name>
<email>304246984@qq.com</email>
<organization>ziqi studio</organization>
<organizationUrl>https://gitee.com/ziqistudio</organizationUrl>
</developer>
</developers>
<!-- 必需:SCM 信息 -->
<scm>
<url>https://gitee.com/ziqistudio/spring-exception-i18n</url>
<connection>scm:git:https://gitee.com/ziqistudio/spring-exception-i18n.git</connection>
<developerConnection>scm:git:https://gitee.com/ziqistudio/spring-exception-i18n.git</developerConnection>
<tag>v${project.version}</tag>
</scm>
<!-- ... 其他配置 ... -->
</project>
6.2 构建插件配置
xml
<build>
<plugins>
<!-- Maven Source Plugin - 生成源码包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Maven Javadoc Plugin - 生成文档包 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>17</source>
<encoding>UTF-8</encoding>
<charset>UTF-8</charset>
<docencoding>UTF-8</docencoding>
<detectJavaApiLink>false</detectJavaApiLink>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<additionalOptions>-Xdoclint:none</additionalOptions>
</configuration>
</execution>
</executions>
</plugin>
<!-- Maven GPG Plugin - GPG 签名 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</execution>
</executions>
</plugin>
<!-- Central Publishing Maven Plugin - 发布到 Maven Central -->
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.9.0</version>
<extensions>true</extensions>
<configuration>
<!-- 与 settings.xml 中的 server id 保持一致 -->
<publishingServerId>sonatype</publishingServerId>
<checksums>required</checksums>
</configuration>
</plugin>
</plugins>
</build>
6.3 发布仓库配置
xml
<!-- 发布配置 -->
<distributionManagement>
<!-- SNAPSHOT 仓库 -->
<snapshotRepository>
<id>sonatype</id>
<url>https://central.sonatype.com/api/v1/publisher/snapshots</url>
</snapshotRepository>
<!-- 正式版仓库 -->
<repository>
<id>sonatype</id>
<url>https://central.sonatype.com/api/v1/publisher/releases</url>
</repository>
</distributionManagement>
7. 执行部署
7.1 部署命令
bash
# 清理并部署(推荐)
mvn clean deploy -pl spring-boot-starter-exception-i18n
# 如果配置了 release profile
mvn clean deploy -Prelease -pl spring-boot-starter-exception-i18n
# 传入 GPG 密码(非交互式)
mvn clean deploy -Dgpg.passphrase=YOUR_PASSPHRASE -pl spring-boot-starter-exception-i18n
7.2 部署脚本示例
创建 deploy.sh:
bash
#!/bin/bash
# Maven Central 部署脚本
# 用法: ./deploy.sh [module-name]
set -e
MODULE=${1:-spring-boot-starter-exception-i18n}
echo "=========================================="
echo " Maven Central 部署脚本"
echo "=========================================="
# 检查 GPG 是否可用
echo "检查 GPG 配置..."
gpg --list-secret-keys --keyid-format LONG
# 重新加载 GPG Agent
echo "重新加载 GPG Agent..."
gpg-connect-agent reloadagent /bye
# 执行部署
echo "开始部署模块: $MODULE"
mvn clean deploy -pl $MODULE -Dmaven.test.skip=true
echo "=========================================="
echo " 部署完成!请前往 Sonatype 确认发布"
echo " https://central.sonatype.com/publishing"
echo "=========================================="
7.3 手动发布
部署成功后,需要在 Sonatype Central Portal 手动发布:
- 访问 https://central.sonatype.com/publishing
- 找到刚刚上传的 Deployment
- 等待验证通过(状态变为 Validated)
- 点击 Publish 发布到 Maven Central
注意 :发布后约 10-30 分钟可在 Maven Central 搜索到。
8. 常见问题与解决方案
8.1 GPG 签名失败:cannot open '/dev/tty'
问题:
gpg: cannot open '/dev/tty': Device not configured
解决方案:
-
配置
~/.gnupg/gpg.conf:use-agent pinentry-mode loopback -
配置
~/.gnupg/gpg-agent.conf:allow-loopback-pinentry -
在 Maven GPG 插件中添加:
xml<gpgArguments> <arg>--pinentry-mode</arg> <arg>loopback</arg> </gpgArguments>
8.2 部署验证失败:Failed to get coordinates from pom file
问题:
Failed to get coordinates from pom file xxx.pom
解决方案 :
确保 POM 文件中明确包含 groupId、artifactId 和 version:
xml
<groupId>io.gitee.ziqistudio</groupId>
<artifactId>spring-boot-starter-exception-i18n</artifactId>
<version>3.0.1</version>
即使子模块继承父 POM,也建议显式声明这些坐标。
8.3 缺少必需的元数据
问题:
Missing: name, description, url, scm, licenses, developers
解决方案 :
确保 POM 文件包含所有必需元素(见第 6.1 节)。
8.4 Javadoc 生成失败
问题:
Javadoc generation failed due to lint errors
解决方案 :
添加 -Xdoclint:none 参数忽略 Javadoc 警告:
xml
<configuration>
<additionalOptions>-Xdoclint:none</additionalOptions>
</configuration>
8.5 401 Unauthorized
问题:
Return code is: 401, ReasonPhrase: Unauthorized
解决方案:
- 检查
settings.xml中的<server>配置 - 确保
<id>与 POM 中的publishingServerId一致 - 重新生成 Sonatype Token
8.6 密钥未上传到服务器
问题:
Key not found on key server
解决方案:
bash
# 上传到多个密钥服务器
gpg --keyserver keyserver.ubuntu.com --send-keys YOUR_KEY_ID
gpg --keyserver keys.openpgp.org --send-keys YOUR_KEY_ID
附录:完整项目结构示例
spring-exception-i18n/
├── pom.xml # 父 POM(packaging=pom)
├── spring-boot-starter-exception-i18n/
│ ├── pom.xml # 子模块 POM(可发布)
│ └── src/
├── spring-exception-i18n-demo/
│ ├── pom.xml # 示例模块(不发布)
│ └── src/
├── deploy.sh # 部署脚本
└── README.md
参考链接
作者 :ziqi studio
项目地址 :https://gitee.com/ziqistudio/spring-exception-i18n
许可证:Apache License 2.0