每次发版都是一场小型的杂技表演。我接手的一个 Java 项目,从“代码合并完”到“用户能下载到新版本”,中间要经历改版本号、打 tag、生成变更日志、编译打包、算校验和、签 GPG、推到 GitHub Releases、再上传到 Maven 仓库这一整套流程。最夸张的一次,我的发布脚本膨胀到了 200 多行 shell,结果发布到一半发现忘了生成校验和文件,只能灰溜溜地删掉 Release 重来。后来我把这套流程整体切换到了 JReleaser,情况才真正好转——它不是又一个“帮你发个版”的玩具脚本,而是一个把发布流程标准化、可复核、可自动化的开源发布工具。这篇博客就以我迁移到一个 JReleaser 项目的完整过程为线索,讲清楚它的核心机制、最小配置、常用参数,以及我在真实发布中踩过的五个坑和完整排查链路。如果你还在用手工步骤或者维护一坨发布脚本,这篇文章应该能帮你省下不少时间。
JReleaser 的官方定位是“release automation tool for Java projects”,但它实际能做的事情比名字看起来更宽。它不绑定 Maven 或 Gradle,你完全可以把它当成一个独立的命令行工具,跑在本地,也可以跑在 CI 里。它能把发布这件事拆成几个清晰的阶段:assemble 负责打出可分发的归档或原生镜像,changelog 从 git 历史生成变更日志,release 把产物和日志一起推到 GitHub、GitLab、Gitea 等平台,announce 负责发通知,deploy 处理上传到 Maven 中央仓库这类包源。下面我从实际使用的角度,把这套工具怎么用、怎么配、坑在哪里,一次性讲透。
1. JReleaser 到底帮你省掉了哪些发布环节
1.1 手动发布到底有多折腾
先算一笔账。一次标准的 Java 项目发版,至少包含下面这些动作:更新项目版本号、修改 changelog 草稿、创建 git tag、推 tag、执行构建命令产出 jar 包、计算 SHA-256 校验和、用 GPG 给 jar 签名、到 GitHub 网页上创建 Release、关联 tag、粘贴 changelog、上传多个附件文件、如果还有 Docker 镜像或 Homebrew 包,又是一轮额外操作。这一套做下来,熟练的人也要花掉三十分钟到一小时,而且每一步都依赖“人记得住”。
更麻烦的是,这些步骤之间往往有隐含依赖。比如你忘了先推 tag,GitHub Release 就关联不上正确的 commit;你换了台机器发布,GPG key 没导出,签名步骤直接失败;你在本地构建的 jar 和 CI 上构建的 jar 内容不一致,用户下载回去发现和源码对不上。我见过最痛的一次,是同事手动发版时漏掉了校验和,用户在下载页看到 jar 和 asc 文件,唯独没有 sha256,只能干等维护者补传。这些问题本质上不是“某个命令不会写”,而是流程没有结构化。
1.2 JReleaser 覆盖的发布链路
JReleaser 做的事情,就是把上述这些零散动作收敛成一个声明式的发布流水线。它本身是一个 Java CLI 工具,下载解压后就能跑,不需要改动你现有的构建脚本。它会读取一个jreleaser.yml配置文件,按照里面声明的内容,依次执行各个阶段。
它的核心价值在于几个设计选择。第一,所有发布参数都写在配置文件里,代码评审可以看到“这次发版到底会做什么”;第二,它提供完善的 dryrun 模式,可以先在本地模拟一次完整发布,把将要执行的 git 命令、HTTP 请求、文件上传清单全部打印出来,确认无误后再真正执行;第三,它不锁定平台,GitHub、GitLab、Gitea、Codeberg 都支持,同一个配置换一下 host 段落就能用在不同代码托管平台;第四,它和构建工具解耦,无论项目用 Maven、Gradle 还是纯手动打包,JReleaser 只关心你产出的文件在哪里。
这里要特别强调一下 dryrun 的价值。我以前用脚本发布,最怕的就是“脚本本身有 bug,但发布前发现不了”。JReleaser 的 dryrun 输出非常细,连“将要 POST 到哪个 API 地址、携带哪些参数”都会打出来,相当于每次发版前先做一次完整的发布会演练。我现在的习惯是:任何一次发布,先跑jreleaser release --dryrun,逐行看输出,再跑正式命令。
1.3 和常规发布脚本、CI 内嵌 action 的对比
很多团队现在的做法是:在 GitHub Actions 里写一个 release workflow,用softprops/action-gh-release这类 action 直接创建 Release 并上传文件。这种方式对“只发布到 GitHub 一个平台”的小项目来说够用,但一旦涉及多平台、多产物、签名、上传到额外包源,配置文件就开始失控。
我整理过一个对比表格,方便你判断自己到底该用哪种方式:
| 发布方式 | 能覆盖的环节 | 主要短板 |
|---|---|---|
| 手工 + curl 脚本 | 创建 Release、上传附件 | 没有 changelog 生成、签名、校验和逻辑,步骤脆弱,依赖人工记忆 |
| CI 内嵌专用 action | 发布到单一平台 | 换 Git 托管平台要重写;多产物、多平台支持弱;流程逻辑散落在 workflow 里 |
| JReleaser | assemble、changelog、release、announce、deploy 全链路 | 需要学习配置结构,初次上手有成本,但学完后一套配置到处用 |
我并不是说所有项目都该立刻迁移到 JReleaser。个人玩具项目用 action 完全没问题。但如果你维护的项目需要定期发版、有多个产物文件、或者要同步发布到 GitHub 和 Maven 中央仓库,那 JReleaser 的投资回报率非常高。一套jreleaser.yml写好后,本地能跑,CI 也能跑,换机器、换人、换平台都不影响。
2. 五分钟跑通第一次发布:最小配置实测
2.1 安装与版本验证
JReleaser 的安装不复杂。先去它的 GitHub Releases 页面下载对应系统的分发包,解压到本地目录(比如~/opt/jreleaser),然后把bin目录加入PATH。它要求本机有 JDK,老版本在 JDK 8 上能跑,但我建议直接用 JDK 11 或 17,新版本对现代 JDK 的支持更完整。
jreleaser --version看到版本号输出就算装好了。这里要注意,JReleaser 是一个独立的 CLI,不是 Maven 插件也不是 Gradle 插件,所以它不会侵入你的pom.xml或build.gradle。这一点我很喜欢,因为很多项目不愿意为了一个发布工具去改构建脚本,JReleaser 可以完全独立存在。
2.2 最小可用的 jreleaser.yml
假设你的项目用 Gradle 构建,产出物在build/libs/demo-app-1.0.0.jar,你希望发布到 GitHub。那么一个最小可用的配置长这样:
project: name: demo-app version: 1.0.0 description: A demo application released with JReleaser website: https://example.com authors: - Alice license: Apache-2.0 java: groupId: com.example artifactId: demo-app release: github: owner: your-github-account name: demo-app tagName: v{{projectVersion}} overwrite: false files: artifacts: - path: build/libs/demo-app-{{projectVersion}}.jar逐个字段解释一下。project段定义了项目元数据,其中name和version是整个配置的核心,后面很多地方都会用{{projectName}}、{{projectVersion}}这样的模板变量来引用它们。release.github段声明了发布目标是 GitHub,owner是你的 GitHub 用户名或组织名,name是仓库名,tagName是发布时使用的 git tag 格式。这里我特意写了v{{projectVersion}},因为很多项目的 tag 习惯带v前缀,而project.version本身通常不带。files.artifacts声明了要作为 Release 附件上传的文件路径,路径里的{{projectVersion}}会自动替换成1.0.0。
这个配置跑起来的前提是:你的项目已经构建出 jar,并且代码已经推送到 GitHub 仓库。
2.3 先用 dryrun 把流程完整演练一遍
第一次发布,我强烈建议先用 dryrun 模式演练。在项目根目录执行:
jreleaser release --dryrunJReleaser 会读取jreleaser.yml,解析配置,然后模拟整个发布过程。你会看到它打印出将要执行的 git 操作、将要访问的 GitHub API 地址、将要上传的文件清单、changelog 的生成结果。注意观察几点:tagName 是否正确解析成v1.0.0;文件路径build/libs/demo-app-1.0.0.jar是否能找到;changelog 里有没有内容。dryrun 模式下所有 HTTP 请求都是假的,不会真的在 GitHub 上创建 Release,所以随便跑,跑不坏任何东西。
我见过不少人在这一步直接跳过,结果正式发布时才发现路径写错,或者 tag 名不符合预期。dryrun 输出的信息密度非常高,值得逐行读一遍,尤其是文件解析路径和 API 请求摘要这两段。
2.4 正式发布与验证
dryrun 确认没问题后,接着做正式发布。首先需要准备一个 GitHub Personal Access Token,权限至少要包含repo范围;如果你用 fine-grained token,需要勾选Contents: Read and write权限。然后把 token 设置到环境变量里:
export JRELEASER_GITHUB_TOKEN=ghp_xxx再执行:
jreleaser release发布完成后,去 GitHub 仓库的 Releases 页面验证三件事:版本号是否正确;jar 附件是否上传成功;changelog 是否正常展示。另外去 git tag 列表里确认v1.0.0这个 tag 已经存在。
3. 核心配置逐项拆解:版本、tag、变更日志与产物上传
3.1 版本号与 git tag 的联动逻辑
用 JReleaser 项目最需要先理解的一点,是project.version和 git tag 之间的关系。JReleaser 默认情况下会使用配置里的project.version来计算默认 tag,但具体行为和你指定的tagName模板有关。很多项目第一次跑发布失败,就是因为 tag 和版本号不匹配。
我建议从一开始就把 tag 规则固定下来。比如统一用v{{projectVersion}}作为 tag 格式,那么版本号1.0.0对应的 tag 就是v1.0.0。在发布前手动打 tag:
git tag v1.0.0 git push origin v1.0.0然后执行 JReleaser 发布,它会用这个已存在的 tag 创建 Release。有一点值得注意:JReleaser不会自动帮你打 tag,如果当前仓库没有匹配的 tag,它会在命令执行时直接报错退出。这个设计本身是稳健的——它避免了把一次没有 tag 的提交发布成正式版本。但也意味着“打 tag、推 tag”这两个动作必须是你发布检查单里的固定步骤。
如果你只是想临时验证某个阶段的配置,可以手动指定 tag:
jreleaser release --tag-name v1.0.0 --dryrun这个参数会覆盖配置里的tagName规则,很适合在调试阶段使用。
3.2 changelog 生成规则与格式化技巧
JReleaser 的 changelog 是从 git 历史生成的,而不是让你手工维护一个独立的 CHANGELOG.md。它默认读取当前 tag 之前的所有 commit,按你指定的格式渲染成 Markdown。配置示例:
changelog: formatted: ALWAYS preset: conventional-commits format: '- {{commitShortHash}} {{commitTitle}} ({{commitAuthor}})' categories: - title: '新功能' labels: - enhancement - feature - title: 'Bug 修复' labels: - bug - fix - title: '其他' labels: - '**'这里的逻辑是:如果 commit message 采用 conventional commits 规范(比如feat: 添加登录功能、fix: 修复空指针),JReleaser 就能按preset: conventional-commits把 commit 自动分类。categories定义了分类名称和匹配规则,labels对应 commit 里的类型关键词,**作为兜底匹配所有未归类的 commit。
如果你的团队没有强制 conventional commits,commit message 写得比较随意,那preset反倒会帮倒忙——所有 commit 都会被扔到“其他”分类里。这种情况下,不如去掉preset,直接用format定义单条 commit 的渲染格式,再用categories里的**兜底。另外一个实用技巧是用excludeLabels把chore、ci这类不影响用户的提交从 changelog 里过滤掉,让发布的变更日志真正聚焦在用户可见的变化上。
3.3 files 区段的四类产物与路径模板
发布到 GitHub Release 的附件都配置在files段下。这个段我拆成四类来看:
files: artifacts: - path: build/libs/demo-app-{{projectVersion}}.jar signatures: - path: build/libs/demo-app-{{projectVersion}}.jar.asc checksums: - path: build/libs/demo-app-{{projectVersion}}.jar.sha256 extraProperties: key: valueartifacts是主产物,比如 jar、zip、tarball。signatures是 GPG 签名文件;如果你配置了signing段(后面会讲),签名文件会自动生成,这里可以不用手工声明。checksums是校验和文件;同样,如果你配置了checksum段并指定算法,JReleaser 会自动计算并生成,不需要自己先算好。extraProperties则用来附加一些自定义信息,供后续模板或脚本引用。
路径里的模板变量非常关键。除了{{projectVersion}},还支持{{projectName}}、{{projectEffectiveVersion}}等。这些变量在 dryrun 时会一并解析,所以发布前盯着 dryrun 输出里的文件清单,就能确认路径解析是否正确。如果文件路径写错,JReleaser 在发布阶段会报“文件不存在”的错误,但这个问题本可以在 dryrun 阶段就发现。
自动生成 checksum 的配置示例:
checksum: active: ALWAYS algorithms: - SHA-256这条配置会让 JReleaser 为所有 artifacts 生成.sha256文件,并和 jar 一起上传。对发布开源项目来说,这一步能显著提升用户对产物的信任度,成本却几乎为零。
3.4 多平台 host 配置与 token 管理
JReleaser 支持 GitHub、GitLab、Gitea、Codeberg 等多个平台。切换平台时,只需把release段下的github换成对应平台名。比如发布到 Gitea:
release: gitea: owner: your-gitea-account name: demo-app host: https://git.example.com username: your-name token: ${GITEA_TOKEN}注意token字段可以直接写成环境变量占位符${GITEA_TOKEN},这样 token 不会出现在配置文件里,避免误提交到仓库。JReleaser 也支持从环境变量读取标准命名的 token,比如JRELEASER_GITHUB_TOKEN对应 GitHub,JRELEASER_GITEA_TOKEN对应 Gitea。如果你同时配置了多个 host,JReleaser 会按照release段里声明的顺序逐个发布,这在“一个项目同时发布到 GitHub 和 Gitea”的场景下非常实用。
关于 token 权限,不同平台的要求不完全一样。GitHub 的 fine-grained token 需要勾选Contents: Read and write才能创建 Release;Gitea 的 token 需要具备repository相关的写权限。这块细节容易踩坑,我在下一章单独展开说。
4. 正式发布中踩过的五个坑与完整排查链路
4.1 坑一:token 权限不足,发布在 API 调用阶段失败
现象:dryrun 全程正常,正式发布时日志在Creating GitHub release这一行停住,紧接着报出 HTTP 403 或 401,错误信息里常见Resource not accessible by integration或Bad credentials。
排查链路:
- 先看日志里实际请求的 API endpoint 和 token 前缀。JReleaser 日志会打印请求的 HTTP 状态码,403 多数是权限范围问题,401 更可能是 token 本身失效。
- 如果你在 GitHub Actions 里用
${{ secrets.GITHUB_TOKEN }},检查 workflow 有没有开启写入权限。需要在仓库的Settings > Actions > General > Workflow permissions里勾选Read and write permissions,同时 workflow 文件里也要声明permissions: contents: write。 - 如果你是本地用个人 token,检查 fine-grained token 的权限设置,确认
Contents的权限级别是Read and write,而不只是Read-only。 - 用 curl 快速验证 token 是否有效:
curl -H "Authorization: Bearer $JRELEASER_GITHUB_TOKEN" https://api.github.com/user能返回你的用户名,说明 token 本身没问题;问题大概率出在权限范围。
经验总结:这类问题最迷惑人的地方在于 dryrun 一切正常,因为 dryrun 根本不发真实请求。所以千万别因为 dryrun 没报错就跳过 token 检查。第一次接 JReleaser 时,我建议先用一个有完整repo权限的 token 跑通全流程,之后再按最小权限原则收敛。
4.2 坑二:git tag 和 project.version 不一致,发布被中途拦截
现象:执行jreleaser release时,日志在解析配置后直接报错,提示当前 git 仓库没有匹配的 tag,或者 tag 名称与配置不一致。
排查链路:
- 先确认当前 HEAD 上有没有 tag:
git tag --points-at HEAD如果输出为空,说明当前 commit 没有任何 tag。
对比配置文件里的 version 和实际 tag。比如
project.version是1.0.0,但 tag 打成了1.0.0而不是v1.0.0,就会和tagName: v{{projectVersion}}不匹配。JReleaser 在发布前会校验 tag,确保 Release 关联的 commit 是可控的。两个方向的解决办法:要么改 tag,重新打一个符合规则的 tag;要么在配置里显式指定
tagName,让 tag 规则和你的实际习惯一致。如果是快速验证配置,用
--tag-name参数临时指定:
jreleaser release --tag-name v1.0.0 --dryrun经验总结:每个项目都应该明确自己的 tag 规则,比如“正式版一律v开头”。JReleaser 不会帮你创建 tag,它只负责在你已经打好的 tag 上创建 Release;所以发布前先git tag再git push origin <tag>是必须坚持的步骤。我在项目 README 里专门写了一段发布检查单,把打 tag、推 tag、跑 dryrun、正式发布四步列清楚,团队其他人照着执行再也没有出现过 tag 不匹配的问题。
4.3 坑三:dryrun 一片正常,正式发布后 release 却没有附件
现象:GitHub Release 创建成功,页面标题和 changelog 都在,但 Release 附件里找不到 jar 文件,或者上传的文件列表和预期不符。
排查链路:
- 检查
files.artifacts[].path是不是相对路径。JReleaser 的工作目录可能和你jar所在的构建目录不一致,路径解析出来没有文件,上传阶段自然就跳过了。 - 发布前手工确认产物文件存在:
ls -l build/libs/demo-app-1.0.0.jardryrun 输出里找到文件解析的汇总信息,看它打印的最终路径是不是你预期的那一个。JReleaser dryrun 会详细打印每个文件的解析结果,这一步的眼睛不要快进。
如果本地路径是
build/libs/demo-app-1.0.0.jar,但你希望上传到 Release 后显示成demo-app-1.0.0.jar,用transform字段控制上传后的文件名:
files: artifacts: - path: build/libs/demo-app-{{projectVersion}}.jar transform: demo-app-{{projectVersion}}.jar经验总结:JReleaser 的 dryrun 对文件解析的输出非常透明,问题在于很多人不会逐行去看。我第一次迁移时,dryrun 输出里其实已经把“文件不存在”或者“路径被跳过”的信息打出来了,但我没注意,直到正式发布后才发现 Release 是空的。现在我的习惯是:dryrun 跑完,先搜输出里有没有Uploading或file相关的行,核对路径和文件名。
4.4 坑四:changelog 内容为空或密集成一团
现象:Release 页面的 changelog 是空的,或者所有 commit 标题被挤成一个没有分段的长列表,看起来非常糟。
排查链路:
- 单独跑一次 changelog 生成,确认 JReleaser 从 git 历史里读到了什么:
jreleaser changelog --dryrun检查 commit message 是否规范。如果配置了
preset: conventional-commits,但 commit 都是“更新代码”“修改 bug”这类自由文本,preset 匹配不到任何分类,内容就可能落入空分组或全部堆在“其他”里。如果团队没有统一的 commit 规范,先去掉
preset,直接用format自定义渲染格式,并用categories里的**兜底,保证所有 commit 都至少出现在“其他”分类下。使用
excludeLabels排除噪音提交:
changelog: formatted: ALWAYS format: '- {{commitShortHash}} {{commitTitle}} ({{commitAuthor}})' excludeLabels: - chore - ci - docs经验总结:changelog 好不好看,根源在 commit 规范,不在工具。JReleaser 只是把你 git log 里的内容结构化。想发布出让人眼前一亮的变更日志,先让团队统一用 conventional commits 写 message,再在 JReleaser 里配置对应的分类规则。如果项目历史已经一团乱,那就先不加 preset,用一个简单的 format 保证基本可读性。
4.5 坑五:重复发布同一版本,GitHub 返回 422
现象:第一次发布成功后,因为某个附件没传对,你想重新发布同一个版本,结果 JReleaser 报 HTTP 422 Unprocessable Entity。
排查链路:
- GitHub 不允许两个 Release 使用同一个 tag。如果配置里
overwrite: false(默认),重复发布同版本会被 API 拒绝。 - 如果你只是想覆盖同名 Release,设置
overwrite: true。但注意,overwrite只能覆盖 Release,不能覆盖已经存在的 tag。如果同名 tag 已经指向了其他 commit,你需要先删除本地和远程的 tag:
git tag -d v1.0.0 git push origin :refs/tags/v1.0.0然后重新打 tag、推 tag,再执行发布。
某些平台(比如 Gitea)对重复 tag 的处理有细微差别,但“先清理旧 tag,再发新版”是通用规则。
经验总结:我建议正式发布流程中保持overwrite: false,宁可让它失败,也要让人停下来看看“为什么重复发布同一个版本”。覆盖发布偶尔会在 CI 误触发时产生意想不到的后果。如果实在需要覆盖,在本地手工执行一次带--dryrun的命令确认清楚,再真正覆盖。
5. 把 JReleaser 接进日常工程:签名、中央仓库与 CI 一体化
5.1 用 GPG 给产物签名并生成校验和
开源项目发布时,GPG 签名是一个增强可信度的重要手段。JReleaser 的signing段配置好之后,会自动为所有 artifacts 生成.asc签名文件,并作为附件上传:
signing: active: ALWAYS armored: true使用前需要先准备好 GPG key。如果本机没有,可以用gpg --gen-key生成一个。JReleaser 在签名时通常需要私钥的 passphrase,可以通过环境变量GPG_PASSPHRASE提供,避免把密码写在配置里。签名文件生成后,files.signatures段可以不再手工声明,JReleaser 会自动把它们放进 release 附件。需要注意,JReleaser 要求能够读取到你的 GPG key,你需要在本地导入私钥,或者把私钥导出后放到 CI 的 secrets 里。
配合自动校验和:
checksum: active: ALWAYS algorithms: - SHA-256发布后,用户可以在 Release 页面同时看到 jar、jar.asc、jar.sha256 三个文件。这样一套组合拳打下来,发布产物的可信度比裸传一个 jar 高出一大截。
5.2 发布到 Maven 中央仓库的配置思路
JReleaser 的deploy阶段支持将产物发布到 Maven 中央仓库。对于 Java 库项目来说,这一步是刚需。典型配置:
deploy: maven: central: sonatype: active: ALWAYS url: https://central.sonatype.com/api/v1/publisher stagingRepositories: - target/nexus-staging使用思路是:先用./gradlew publish或mvn deploy把产物部署到本地的 staging 目录,再由 JReleaser 执行 deploy 阶段把 staging 里的文件上传到 Sonatype 完成正式发布。需要设置JRELEASER_SONATYPE_USERNAME和JRELEASER_SONATYPE_PASSWORD环境变量。
这里要提醒一点:发布到中央仓库有很多前置条件,比如 groupId 需要通过验证、所有产物必须有 GPG 签名、POM 文件必须完整。JReleaser 只是在最后一步帮你去掉“手工上传到 Nexus”的麻烦,前面那些质量门禁,还是得在你的构建配置里做好。
5.3 在 GitHub Actions 里一键全量发布
把 JReleaser 接进 GitHub Actions 是让发布流程自动化的最后一步。我现在的做法是:推 tag 触发 workflow,workflow 自动构建产物,再运行 JReleaser 完成发布。
一个完整可用的 workflow 示例:
name: Release on: push: tags: - 'v*' jobs: release: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-java@v4 with: distribution: temurin java-version: 17 - name: Build run: ./gradlew clean build - name: Run JReleaser uses: jreleaser/release-action@v2 with: arguments: full-release env: JRELEASER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}这个 workflow 里有几个细节直接决定成败。第一,checkout必须设置fetch-depth: 0,否则 JReleaser 生成 changelog 时看不到完整 git 历史,变更日志会缺一大截。第二,permissions: contents: write必须显式声明,否则GITHUB_TOKEN默认只有只读权限,Release 创建会失败。第三,arguments: full-release会让 JReleaser 先执行 assemble 阶段,再执行 release 阶段;如果你已经在前面单独跑了构建步骤,也可以只跑release。第四,JRELEASER_GITHUB_TOKEN使用内置的GITHUB_TOKEN就够了,不需要单独创建个人 token,只要 workflow 权限设置正确。
我在实际使用中,把jreleaser/release-action@v2这个 action 固定在仓库里,团队其他成员发版时只需要推一个v*格式的 tag,后面全部自动化。这比我以前维护一整个 shell 发布脚本爽太多了。
5.4 进阶扩展:多模块项目、announce 环节与我的总结
如果项目是多模块结构,JReleaser 也支持通过命令行参数覆盖版本号进行全量发布。比如:
jreleaser full-release -PJRELEASER_PROJECT_VERSION=2.0.0这个参数会把配置里的版本号临时覆盖成2.0.0,适合在 CI 里根据 tag 动态发版的场景。
announce阶段可以在发布完成后自动发通知到 Twitter、Discord、邮件等渠道。如果团队有发布公告的需求,可以额外配置,属于锦上添花的功能,我在这里不展开。
最后分享一点个人体会。我在团队里推广 JReleaser 时,很多人问的第一句话是“这不就是 GitHub Actions 里加一个 step 的事吗”。但真正用了两个版本周期之后,大家的感受是:JReleaser 最大的价值不是省掉了某个点击动作,而是把发布流程从“每个人的私有记忆”变成了“一份所有人都能读懂的公开配置”。以前发版依赖某个核心维护者在场,现在任何一个人推一个 tag,流水线就会按同样的标准跑完所有环节。如果你也想把发布这件小事固定下来,我的建议是从最小配置开始,本地先 dryrun 看输出,再找一个小项目全量跑通,最后接进 CI。这套流程走一遍之后,你再也不想回到手工发版的日子。