☰
Maven命令行发布Release版本到私有仓库的完整指南
2026/10/19 9:12:24 网站建设 项目流程

最近在配合一个小组整理组件发布流程时,发现大家在“制品发布”这件事上各自习惯差很多。有人喜欢在 IDE 里点 Deploy,有人写脚本调 mvn 命令,还有人把整个发布动作完全丢给 CI。我自己的偏好一直比较明确:用阿里云私有 Maven 仓库承接制品,用 Maven 命令行来发 Release 版本。不是命令行比 CI 高级,而是命令行更接近问题现场,出故障时一眼就能分辨是认证问题、版本号问题还是仓库策略问题。

这篇文章就沿着这个思路展开。先讲清楚 Release 版本和 SNAPSHOT 版本在私有仓库里到底有什么区别,再讲阿里云私有 Maven 仓库怎么创建,然后讲本地 settings.xml 和 pom.xml 怎么配,最后用 mvn 命令把 Release 版本发上去,并列出发布时容易翻车的几个场景。如果你之前只用 IDE 的 Maven 面板点过部署,看完这篇之后,应该能把发布链路里每一步的含义都理清楚。

1. 私有仓库里 Release 与 SNAPSHOT 的分工差异

1.1 两个版本后缀背后的 Maven 行为差异

Maven 的版本号里只要有-SNAPSHOT后缀,就代表这个版本还处于开发态。它的特点是:每次构建时,Maven 默认都会去远程仓库检查是否有新的快照版本,一旦远程仓库里的同名快照比本地新,它就会重新下载。所以同一个 SNAPSHOT 版本可以反复覆盖,今天传一个上去,明天再传一个上去,依赖方的下一次构建拿到的可能就不是同一个内容。

这一点既是快照的优势,也是它不适合当正式制品的原因。快照版本的制品是不可追溯的,你没法跟别人说“当前测试环境跑的是 1.0.0-SNAPSHOT 的第几次构建”,因为同一时刻它可能有多个来源,比如本地构建、CI 构建、某个同事手动上传,谁都说不清最后覆盖的那个包对应哪个代码提交。

Release 版本则完全相反。一旦版本号不带-SNAPSHOT后缀,比如1.0.0,Maven 就把它当作一个稳定版本,远程仓库收到之后就不会再允许同名版本覆盖。依赖方只要拿到一次,之后本地缓存里会一直认为这个版本是固定不变的。正式环境部署时,依赖1.0.0得到的永远是同一份内容,不会半夜突然被一个同名快照污染掉。这是 Release 版本在整个 Maven 依赖体系里最核心的价值。

1.2 阿里云私有仓库对两类制品的处理策略

阿里云的私有 Maven 制品仓库,通常也会分成两个入口来管理这两类内容。创建制品仓库时,平台会自动帮你准备好 release 和 snapshot 两个仓库地址,或者至少会给你一个可以统一管理两类制的仓库入口。

实际使用中,release 仓库默认不允许同一个版本重复发布,snapshot 仓库允许同名覆盖。这个策略跟 Maven 本身的约定是吻合的,所以配置distributionManagement时,我强烈建议把两者分开指向,不要全部丢到一个地址里。否则你发布的 Release 版本和 SNAPSHOT 版本混在一起,哪天手滑把同一个 release 版本传两次,或者依赖方解析仓库索引时出现错乱,排查起来会很痛苦。

另外一个值得注意的点是:私有仓库的下载和上传策略也是分开控制的。创建仓库时,你可以把“仓库可见范围”设置为私有,只允许加了白名单的账号访问。对公司内部的基础库来说,默认私有是更安全的选择,等真正要对外开放时再改为公开。这个设置在后续发布过程中不直接参与,但它决定了你配置server里的账号到底有没有权限上传。

2. 在阿里云控制台创建 Maven 私有仓库的完整操作

2.1 从创建制品库到拿到仓库地址

登录阿里云控制台之后,找到制品仓库相关服务,创建一个新的制品库。创建时通常会让你选择仓库类型,这里必须选 Maven,不要选成 npm 或者 Docker。别名和仓库名称我一般建议用项目名,比如common-lib这样的格式,不要用中文名,避免后面在命令行里复制地址时还要绕一圈转码。

创建完成之后,进入仓库详情页,能看到这个制品库对应的仓库地址。通常会有两个地址:Release 仓库和一个 Snapshot 仓库。有些版本的控制台会直接把地址展示成https://packages.aliyun.com/maven/repository/你的仓库名-release/这样的结构。如果你看到的地址不是这个格式,也没关系,以控制台实际展示的地址为准,关键是区分清楚哪一个用于 Release,哪一个用于 Snapshot。

仓库创建本身并不复杂,容易忽略的反而是仓库之外的两项准备工作。一是仓库的访问权限,二是上传制品的认证信息。权限如果你漏配了,后面命令行发布时大概率会收到 403。

2.2 访问凭证和账号体系整理

阿里云私有 Maven 仓库支持用账号密码认证,也支持用访问令牌。我个人的建议是优先使用访问令牌,而不是主账号密码。主账号密码权限范围太大,一旦在settings.xml里明文保存,任何能看到配置文件的人都能直接操作你整个账号下的所有资源。访问令牌可以限定到某个制品仓库,出问题后撤销也很方便。

拿到认证信息后,建议先整理成一份自己的记录,至少要包含三样东西:

  • 仓库地址,分为 release 和 snapshot 两个 URL;
  • 认证用户名,一般对应你的账号 ID 或一个指定的用户名;
  • 认证密码或访问令牌。

这三样东西后面都会用上。我不太建议把这些信息直接写在 pom.xml 里,因为 pom.xml 大概率会近进代码库,哪怕你用的是私有 git 仓库,也尽量不要把敏感凭证提交进去。正确做法是把它们放在本地的~/.m2/settings.xml里,让 Maven 在运行时读取,而不是把密码作为项目的一部分。

3. 本地 Maven 对接私有仓库:三份配置一起改

3.1 settings.xml 的 servers 配置

Maven 的settings.xml是本地构建环境的全局配置,它的位置在~/.m2/settings.xml。如果这个文件不存在,就手动创建。你需要在这个文件里新增<servers>节点,把阿里云仓库的认证信息配进去。

<settings> <servers> <server> <id>aliyun-release</id> <username>你的用户名</username> <password>你的访问令牌</password> </server> </servers> </settings>

这里最容易踩坑的地方是<id>必须和后面 pom.xml 里<distributionManagement>中的<repository><id>完全一致。Maven 做认证匹配的时候不看仓库地址,只看 id,所以哪怕你地址写对了,id 不一致,它也会认为这是一个没有配认证的仓库,然后报 401。

如果使用同一个账号同时上传 release 和 snapshot,那你只需要一个<server>就够了,比如上面的aliyun-release。如果你希望两个仓库用两套账号隔离,也可以再加一个 id 为aliyun-snapshot的 server。注意,Maven 对 password 的处理是明文读取,没有加密机制,所以保管好settings.xml的权限很重要。

3.2 pom.xml 的 distributionManagement 配置

pom.xml要告诉 Maven:我这个项目的正式版本应该发布到哪里,快照版本又发布到哪里。这个描述写在<distributionManagement>节点里。

<distributionManagement> <repository> <id>aliyun-release</id> <url>https://packages.aliyun.com/maven/repository/common-lib-release/</url> </repository> <snapshotRepository> <id>aliyun-snapshot</id> <url>https://packages.aliyun.com/maven/repository/common-lib-snapshot/</url> </snapshotRepository> </distributionManagement>

需要注意,这里的两个<id>要分别对应 settings.xml 里的 server id。比如你在 settings.xml 里只配了一个 id 为aliyun-release的 server,那上面 snapshotRepository 的 id 就不能写aliyun-snapshot,否则 Maven 在传 snapshot 时会找不到对应凭据,报认证失败。稳妥的做法是 settings.xml 里把两个 id 都配上,哪怕用户名和密码一样,也省得 Maven 做无效匹配。

还有一个细节是 URL 末尾的斜杠。Maven 在上传时会直接把 artifact 的路径拼接在 URL 后面,如果 URL 末尾没有斜杠,可能出现路径拼接错乱。建议从控制台复制地址之后,确认它以/结尾。

3.3 发布插件:maven-release-plugin 与 maven-deploy-plugin 的版本选择

命令行发布 Release 版本时,最常用的插件是maven-release-plugin,而实际把文件传到远程仓库的是maven-deploy-plugin。这两个插件都有内置默认版本,但默认版本往往偏老,在高版本 JDK 下容易出问题,我建议在 pom.xml 的<build><plugins>里显式指定版本。

<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> <version>3.0.1</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-deploy-plugin</artifactId> <version>3.1.1</version> </plugin> </plugins> </build>

老版本的maven-deploy-plugin在处理仓库返回 400 或 403 时,报错信息非常模糊,经常只会提示Failed to deploy artifacts,你根本不知道是认证失败还是版本冲突。新版本对错误信息的记录更完整,排查问题会轻松很多。

4. 命令行发布 Release 的完整过程与底层逻辑

4.1 发布前一定要做的自检

很多人习惯直接跑mvn release:prepare,结果报错之后才发现版本号没改对,或者工作区还有未提交的代码。我建议把发布前的自检固定成一套清单:

  • 确认当前分支是你想发布的分支;
  • 运行git status,确保工作区干净;
  • 确认项目版本号是1.0.0-SNAPSHOT这种形式,而不是已经变成1.0.0;
  • 运行mvn clean verify,确保单元测试和打包流程都能通过。

这个自检过程看起来简单,却能省掉后面大量时间。release:prepare是严格要求工作区干净的,如果你有未提交的 pom.xml 改动,它直接会中断,并提示你有本地修改。与其让它中断再回去处理,不如一开始就把状态检查好。

还有一个容易忽略的点:如果项目是多模块结构,父子模块之间的版本号必须保持一致。发布插件在修改版本号时,会遍历所有模块,如果某个子模块的版本号跟父模块不一致,它会报错。遇到这种情况,先执行mvn versions:set -DnewVersion=1.0.0-SNAPSHOT把所有模块版本统一,再走发布流程。

4.2 release:prepare 命令具体做了什么

mvn release:prepare是发布链路的第一个关键命令。它不会直接上传制品,而是做一系列准备工作,核心流程大致如下。

它会检查当前项目的工作区状态和 SCM 配置。如果你没有在 pom.xml 里配置<scm>节点,这一步会直接失败。所以你需要先在 pom.xml 中加入类似这样的配置:

<scm> <connection>scm:git:https://your-git-server/xxx/project.git</connection> <developerConnection>scm:git:https://your-git-server/xxx/project.git</developerConnection> <url>https://your-git-server/xxx/project</url> <tag>v1.0.0</tag> </scm>

配置好之后,release:prepare会依次完成以下事情:

  • 把 pom.xml 里的版本号从1.0.0-SNAPSHOT改成1.0.0;
  • 执行一次构建,验证这个版本确实能正常编译;
  • 将修改后的 pom.xml 提交到 git;
  • 在 git 上创建一个标签,默认标签名通常是project-1.0.0,你可以在命令里用-Dtag=v1.0.0指定;
  • 再把 pom.xml 里的版本号改成下一个开发版本,比如1.0.1-SNAPSHOT;
  • 再次提交这个版本号变更。

所以执行完release:prepare之后,你的工作区会停在下一个开发版本号上,当前的 git HEAD 指向的是开发版本号的提交,而你刚发布的1.0.0被记录在 git tag 上。这个机制设计得很巧妙,它保证了发布版本号和开发版本号不会混在一起。

实际执行命令时,我一般这样写:

mvn release:prepare -Dtag=v1.0.0 -Dresume=false

-Dresume=false的意思是不要从上次中断的位置恢复,强制从头执行。如果上一次 prepare 执行到一半失败,Maven 会留下一些 release.properties 临时文件,再次执行时默认尝试恢复,有时反而会把事情搞复杂。

4.3 release:perform 命令与仓库部署

prepare完成之后,接着执行mvn release:perform。这个命令做的事情是从刚才创建的 git tag 检出代码,然后以 Release 版本的 pom.xml 执行构建和部署,最后把制品上传到你在distributionManagement里配置的仓库地址。

因为 perform 是从 tag 上重新拉的代码,所以它能保证发布出去的制品和 git 上记录的那个版本完全对应,不会出现“本地改了代码但发布的是旧包”的情况。这也是 release 插件比手动mvn deploy更可靠的地方。

执行命令:

mvn release:perform

如果一切顺利,控制台会看到 BUILD SUCCESS,然后你登录阿里云制品仓库控制台,就能看到刚才上传的1.0.0版本制品。重点检查一下它是不是进入了 release 仓库,而不是 snapshot 仓库。

4.4 不使用 release 插件的手动发布方案

有人会问:我理解这套流程了,但项目里没有配置 scm,也不想用 release 插件,能不能只用命令行上传 Release 版本?

可以。最简单的方式是手动改版本号,然后执行 deploy。

mvn versions:set -DnewVersion=1.0.0 mvn deploy -DskipTests mvn versions:set -DnewVersion=1.0.1-SNAPSHOT

这个过程本质上把 release:prepare 的部分工作拆成了手动操作。缺点是需要你自己记得提交版本号修改、自己打 git tag,发布结果和代码提交之间的关联需要人工保障。优点是流程透明,每一步你都看得见。

还有一种临时部署的方式,不需要改 pom.xml 的 distributionManagement,直接用-DaltDeploymentRepository指定目标仓库:

mvn deploy -DaltDeploymentRepository=aliyun-release::default::https://packages.aliyun.com/maven/repository/common-lib-release/

这个命令适合临时把某个构建传上去验证,但不适合作为长期方案。因为参数太过冗长,而且稍不注意就容易把写错的仓库地址也带进部署流程。

5. 实际发布中更容易翻车的四个场景

5.1 401/403 认证失败:几乎都是 id 和凭据不匹配

发布时最常见的报错是 401 Unauthorized 或 403 Forbidden。出现这类问题,第一反应不要去看密码是不是输错,而是检查 Maven 有没有找到正确的 server 配置。

排查链路我建议这样走:

  1. 确认 pom.xml 里 repository 的 id 到底叫什么;
  2. 打开 settings.xml,看 servers 节点里有没有完全一致的 id;
  3. 确认这个 server 下的用户名和密码是阿里云控制台里生成的那一套,不是你自己随便设的;
  4. 如果用的是访问令牌,确认令牌状态是启用中,且没有被删掉。

很多时候,问题出在 pom.xml 里的仓库 id 写的是private-releases,但 settings.xml 里配的是aliyun-release,Maven 找不到匹配项就直接匿名访问,然后被阿里云拒绝。

5.2 同一个 Release 版本重复部署被拒

Release 仓库不允许同名覆盖,这是很多人第一次会遇到的问题。第一次部署1.0.0成功,修了一个 bug 之后想再传一次1.0.0,结果发现抛错,说版本已存在。

出现这个问题的根源往往不是提交错了版本号,而是发布习惯没统一。建议记住一个原则:Release 版本一旦发布,就永远不要试图覆盖。如果你想修复已经发布过的版本,正确做法是发布一个新的修订号,比如1.0.1。如果你是在测试环境反复验证,那就用 SNAPSHOT 版本,不要动 Release 版本。

如果确实需要清理仓库里某个错误发布的版本,只能在阿里云控制台手动删除这个制品,然后再重新上传。删除操作在团队协作中要格外谨慎,因为一旦有其他项目已经依赖了这个版本,删除之后他们的本地缓存还留得住,但新环境可能就直接解析失败了。

5.3 release:prepare 因为工作区不干净而中断

准备发布的项目里,常常会有一些未提交的文件,比如 IDE 的配置、生成的文档、最近改过还没提交的代码。release:prepare对此非常敏感,它会检查当前 git 工作区的状态,只要发现有未提交的改动,就直接中断。

处理方式有两种。第一种是把未提交内容 commit 或者 stash,让工作区恢复干净。第二种是确认这些文件不需要入库,然后加入.gitignore。但注意,Maven 的 release 插件在检查工作区状态时,会同时检查所有被插件认为需要管理的文件,不仅仅是 pom.xml。如果某个文件虽然被 ignore,但插件仍然认为它是项目的一部分,也可能触发错误。

这些年在实际项目里最常见的情况,其实是本地 pom.xml 里的版本号被人手动改成了1.0.0,但又没提交,所以工作区看起来很脏,release:prepare 完全无法推进。这时候先git checkout pom.xml把版本号恢复成1.0.0-SNAPSHOT,再重新走流程就行了。

5.4 release:prepare 因为 SNAPSHOT 依赖被拦截

一个项目要发布 Release 版本,它的直接依赖里一般不应该出现 SNAPSHOT 版本。如果某个依赖方引用的是common-utils:1.0.0-SNAPSHOT,release 插件默认会认为这个发布链不稳定,直接报错。

这个限制是为了防止你发布出去的 Release 依赖一个随时会变化的 SNAPSHOT 包,导致最终运行环境无法复现。解决方式有两个:

  • 把依赖方升级到已经发布的 Release 版本,比如common-utils:1.0.0;
  • 如果内部确实有依赖还没发 Release,你需要在 release 插件里显式打开allowDependenciesSnapshot开关。
mvn release:prepare -DallowDependenciesSnapshot=true

但我建议这个开关只在确实需要时才用。项目里长期依赖 SNAPSHOT,会让 Release 版本的可复现性打折扣,发布出来的制品表面上叫稳定版,实际上内部还是随开发版本跳动的状态。

6. 长期维护仓库的版本管理心得

6.1 版本号的规划应从第一天开始

创建私有仓库的第一天,就要把版本号规范定下来。常见的格式是三位版本号主版本.次版本.修订号,开发中用1.0.0-SNAPSHOT作为下一个发布版的占位。主版本号大改接口时递增,次版本号增加功能时递增,修订号只修 bug 时递增。不要用1.0、1.01这种不规范的写法,也不要在 SNAPSHOT 阶段就随便改成 release 版本上传。

版本号规划看起来和仓库创建没有直接关系,但它在发布链路里影响面非常大。比如你发布1.1.0之前,版本号可能已经经历过很多次手工改动,如果中间哪一步没有把 pom.xml 的版本号恢复成规范格式,release:prepare 就会把错误版本号带上 tag,之后改起来很麻烦。

6.2 把发布动作固化成一个脚本

虽然这篇文章一直在讲命令行操作,但我不建议真到了每次发布都去手动敲那两行命令。更好的做法是把发布流程固化成一个 shell 脚本,比如release.sh,脚本里固定执行自检、prepare、perform 三步。

mvn clean verify mvn release:prepare -Dtag=v$1 -Dresume=false mvn release:perform

这样每次发布时只需要传入 tag 名称,脚本会自动跑完整流程。再往后,你可以把脚本挂到 CI 上,让 CI 在特定分支打 tag 时自动触发发布。第一步从命令行切入的原因是帮助你理解底层逻辑,理解透了再把步骤自动化,才不会一出问题就抓瞎。

6.3 发布后做一次依赖解析验证

发布完成并不意味着结束。下一步我建议在另一个干净环境里,写一个只依赖这个新版本的最小测试项目,执行mvn dependency:get -Dartifact=groupId:artifactId:1.0.0,强制从远程仓库拉取发布后的制品。如果这一步能成功,说明仓库地址、认证信息、仓库策略都正常。

如果拉取失败,优先检查依赖方项目的 repository 配置里有没有把阿里云私有仓库加进去。很多人只配了发布地址,忽略了下载地址,导致自己发的包自己都下不回来。这其实是私有仓库运维里最经典的盲点。

6.4 账号和令牌的治理

私有仓库的使用周期很长,账号治理不要等到出问题才做。如果某个成员离职,最好第一时间在阿里云控制台撤销他的访问令牌,避免仓库被非授权访问。团队内部可以把认证信息放到统一的密码管理工具里,而不是靠各人本地保存的 settings.xml 流传。

如果你用的是 CI 发布模式,建议为 CI 单独创建一个令牌,不要复用个人账号的令牌。这样你可以精确控制哪些流水线能发布,哪些只能在构建阶段下载依赖,不会因为某个人临时改了令牌导致整条 CI 链路崩溃。

这整套流程最值得记住的地方,不是某一条具体命令,而是命令背后的状态管理:SNAPSHOT 和 Release 的职责划分、prepare 对版本号和 tag 的一致性保证、perform 对发布内容的可追溯性保证。把这些想清楚,哪怕以后换了别的制品仓库,换成别的构建工具,你也能很快迁移过去。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询