Halo 的 Gradle 依赖升级实践:版本目录、Versions 插件与项目级约束
本文以 Halo 仓库内置的 gradle-dependency-updates 技能文档为主体,系统讲解该项目"检查过期依赖 → 判断可升级范围 → 制定升级计划 → 应用并验证"的完整工作流。读完本文,你将掌握如何基于 Gradle 版本目录(gradle/libs.versions.toml)与 Spring Boot BOM 双轨管理后端依赖、如何用 ben-manes Versions 插件安全地发现过期项,以及 Halo 中几条不可逾越的项目级约束(thymeleaf 平铺 jar、lombok 对齐、插件版本锁定)背后的原因与源码依据。
1. 依赖管理体系:版本目录 + Spring Boot BOM
Halo 的后端依赖由两套机制协同管理:
- Gradle 版本目录:gradle/libs.versions.toml,集中声明
[versions]、[libraries]、[bundles]、[plugins]四类条目。从当前仓库可以看到,Lucene、Therapi Javadoc、Resilience4j 采用"版本号与库分离"的写法(如lucene = '10.5.0'配version.ref = 'lucene'),而 jsoup、guava、pf4j 等则直接使用内联坐标(如org.jsoup:jsoup:1.23.1);[bundles]将同一族的库打包引用(如lucenebundle 聚合了 core、queryparser、highlighter 等 5 个模块)。 - Spring Boot BOM:由 platform/application/build.gradle 这个
java-platform工程承载,其中api platform(SpringBootPlugin.BOM_COORDINATES)引入了 Spring Boot 官方 BOM,随后用constraints块把版本目录中的 bundle 和第三方库以约束形式注入。:api与:application模块通过api platform(project(':platform:application'))消费该平台,因此 Spring Framework、r2dbc 驱动、postgresql、byte-buddy、jspecify、junit-platform-launcher 等大量库的版本不由项目直接控制,而是随 BOM 统一到达。
另外,ben-manes Versions 插件已经应用于 :api 与 :application 两个模块,分别见 api/build.gradle 和 application/build.gradle 中的 alias(libs.plugins.versions),插件版本固定为 0.54.0(见 gradle/libs.versions.toml)。多模块结构由 settings.gradle 定义:api、application、platform:application、platform:plugin、ui。
注意范围边界:该升级流程仅针对后端(api / application / platform)。前端
ui/目录走 pnpm,不在此文档范围内。
2. 第一步:找出过期的依赖
核心命令:
./gradlew dependencyUpdates -DoutputFormatter=plain,json --console=plain
执行后需阅读两份报告并合并去重(两个模块各自输出一份):
api/build/dependencyUpdates/report.{txt,json}application/build/dependencyUpdates/report.{txt,json}
一个容易误解的点:插件默认 revision 包含稳定版(stable),所以报告里提示的"later milestone versions"并不等于预发布版——只有带 alpha / beta / rc 限定符的版本才算 pre-release(例如 tika-core 的 4.0.0-beta-1 就应跳过,除非用户明确要求)。
3. 第二步:判断哪些可以升级
拿到过期清单后,按以下四类分别处理:
| 类别 | 声明位置 | 处理方式 |
|---|---|---|
| Catalog library | gradle/libs.versions.toml 的 [libraries] |
可直接在目录中升级 |
| Gradle plugin | [plugins] 段 |
可直接在目录中升级(受约束 3.3 限制) |
| BOM-managed | 由 platform/application 中 Spring Boot BOM 锁定 |
永远不要单独升级,随下一次 BOM 整体升级 |
| Gradle wrapper | gradle/wrapper/gradle-wrapper.properties | 单独管理,当前为 gradle-9.7.0-bin.zip |
从源码结构看,BOM-managed 的判断依据是:api/build.gradle 中这些依赖只写坐标不写版本号(如 api 'org.jspecify:jspecify'),版本完全来自 platform(project(':platform:application')) 引入的 BOM;而 platform/application/build.gradle 的 constraints 块才是项目真正能改动的地方。
4. 项目级约束(不可逾越的红线)
技能文档列出了五条硬约束,每一条都有仓库内的具体佐证:
-
thymeleaf 平铺 jar 不得改动。application/build.gradle 中存在如下声明:
// Build from https://github.com/halo-dev/thymeleaf/commit/d23498ea297059deff04ba8c3578de59c73ccf03 runtimeOnly ':thymeleaf:3.1.3.RELEASE' runtimeOnly ':thymeleaf-spring6:3.1.3.RELEASE'这是针对 halo-dev/halo#7289 的固定版本 workaround,jar 位于 application/libs/(flat-dir 方式引入),版本号还同时出现在 gradle.properties 中。升级依赖时绝不能触碰它们。
-
lombok 保持与
io.freefair.lombok插件对齐。当前 gradle/libs.versions.toml 声明lombok = 'io.freefair.lombok:9.5.0',不要额外添加lombok { version = ... }覆盖。 -
ben-manes Versions 插件停留在 0.54.0。0.59.0 应用时会因 classloader 错误失败,因此即使报告提示有新版也不升。
-
预发布版跳过(如 tika-core 4.0.0-beta-1),除非用户明确要求。
-
版本号只允许出现在三处:
gradle/libs.versions.toml、gradle.properties、ui/package.json,严禁在 build 脚本中硬编码(AGENTS.md 约定)。
5. 第三步:输出升级计划
批准执行之前,先按类别分组展示计划,每个条目需包含:当前版本 → 最新版本、semver 增量级别(major / minor / patch)、落点位置(catalog 的哪个条目、插件段还是 wrapper),并明确列出排除项及原因(例如 BOM-managed 项、锁定在 0.54.0 的 Versions 插件、thymeleaf 平铺 jar)。
6. 第四步:应用修改并验证
只有在用户批准计划之后,才编辑 gradle/libs.versions.toml,每次升级只改一行(一个条目一行),然后:
./gradlew build
构建通过后再跑一次 dependencyUpdates,确认:
- 已升级的条目不再出现在过期清单中;
- 剩余过期项应当恰好等于第 4 节所列的排除集合(BOM-managed、锁定的插件、平铺 jar、预发布版等),出现"意料之外的残留"说明计划执行不完整。
小结
Halo 的依赖升级并非"见新就升":它通过版本目录 + BOM 双轨制划清权限边界,借助 Versions 插件生成可信的过期报告,再用一组项目级约束(thymeleaf workaround、lombok 对齐、插件版本锁定、预发布过滤、版本号单一来源)防止升级动作破坏构建。按照"发现 → 分类 → 计划 → 批准 → 修改 → 构建 → 复验"这一闭环操作,可以把后端依赖升级控制在一个可审计、可回退的安全范围内。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00