Renovate 与 OpenRewrite:从依赖升级到代码自动迁移的工程化实践

欢迎你来读这篇博客,这篇博客主要是关于 Renovate + OpenRewrite 的整合实践。

如果只用 Renovate,我们可以自动发现依赖新版本,并创建升级 MR;如果只用 OpenRewrite,我们可以自动重构代码、迁移 API、替换配置。但真正的企业项目里,最麻烦的往往不是“有没有新版本”,而是:

依赖升级之后,代码、配置、测试、构建脚本也要跟着改。

所以这篇文章要讲的是一个更完整的工程化方案:

Renovate 负责发现依赖升级机会并创建 MR,OpenRewrite 负责把项目代码自动迁移到适配新版本,CI 负责验证,人工 Review 负责最后兜底。

序言

在 Java 后端项目中,依赖升级是一件长期被低估的事情。

很多团队的 pom.xml 一开始看起来还挺清爽:

1
2
3
4
<spring-boot.version>2.7.18</spring-boot.version>
<spring-cloud.version>2021.0.9</spring-cloud.version>
<mybatis-plus.version>3.5.5</mybatis-plus.version>
<fastjson.version>1.2.83</fastjson.version>

几年之后,它可能就变成了“版本考古现场”:

1
2
3
4
5
6
7
Spring Boot 还是 2.x;
JDK 还是 8 或 11;
javax 还没迁到 jakarta;
JUnit 4 和 JUnit 5 混着用;
某些老依赖带着漏洞;
Maven 插件版本多年没动;
Dockerfile 里的基础镜像也很久没升级。

这时如果只靠人工升级,就会非常痛苦。因为依赖升级不是简单改一个版本号,它经常会牵出一串连锁反应:

1
2
3
4
5
6
7
依赖版本变了
-> API 变了
-> import 变了
-> 配置项变了
-> 测试框架变了
-> 构建插件也要变
-> CI 开始爆红

于是我们需要两类工具:

工具 核心职责
Renovate 发现依赖新版本,自动创建升级分支和 MR/PR
OpenRewrite 自动修改源码、配置、构建文件,让代码适配新依赖

这两者不是替代关系,而是互补关系。

一句话概括:

Renovate 管“依赖要不要升”,OpenRewrite 管“升完之后代码怎么改”。

正文

一、Renovate 和 OpenRewrite 分别解决什么问题

1.1 Renovate 的定位

Renovate 是一个依赖自动升级机器人。它会扫描仓库里的依赖声明文件,例如:

1
2
3
4
5
6
7
pom.xml
build.gradle
package.json
Dockerfile
docker-compose.yml
.gitlab-ci.yml
.github/workflows/*.yml

然后发现新版本,创建升级分支,并提交 MR/PR。

在 Maven 项目里,它可以识别:

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.4.0</version>
</dependency>

发现新版本后,自动提交类似这样的 MR:

1
2
3
chore(deps): update spring boot to 3.5.3
chore(deps): update dependency com.alibaba:fastjson to v2
chore(deps): update docker image eclipse-temurin to 21-jre

Renovate 重点解决的是:

1
2
3
4
5
6
1. 哪些依赖有新版本?
2. 哪些依赖有安全更新?
3. 哪些依赖可以分组升级?
4. 哪些依赖可以自动合并?
5. 哪些依赖需要人工审批?
6. 如何把升级变成可 review 的 MR/PR?

但是 Renovate 通常不负责复杂代码迁移。它可以改版本号,但不会天然理解你的业务代码该如何适配新 API。

比如它可以把 Spring Boot 从 2.7 升到 3.5,但升级之后下面的代码会出问题:

1
import javax.servlet.http.HttpServletRequest;

Spring Boot 3 基于 Jakarta EE,很多 javax.* API 要迁移到 jakarta.*

1
import jakarta.servlet.http.HttpServletRequest;

这个时候就轮到 OpenRewrite 上场了。

1.2 OpenRewrite 的定位

OpenRewrite 是一个自动化代码重构和迁移工具。它通过预定义的 Recipe 规则,对源码、构建文件、配置文件做自动修改。

它能做的事情包括:

1
2
3
4
5
6
7
8
9
10
Spring Boot 2.x -> Spring Boot 3.x
Java 8/11/17 -> Java 21
javax.* -> jakarta.*
JUnit 4 -> JUnit 5
老 API -> 新 API
废弃配置项 -> 新配置项
Maven 插件版本升级
依赖坐标迁移
代码风格清理
静态分析修复

例如:

1
import javax.annotation.PostConstruct;

迁移后:

1
import jakarta.annotation.PostConstruct;

再比如 JUnit 4:

1
2
3
4
5
6
7
8
9
import org.junit.Test;
import static org.junit.Assert.assertEquals;

public class UserServiceTest {
@Test
public void testCreate() {
assertEquals(1, 1);
}
}

迁移到 JUnit 5:

1
2
3
4
5
6
7
8
9
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

class UserServiceTest {
@Test
void testCreate() {
assertEquals(1, 1);
}
}

这类机械迁移,如果靠人工改,很容易改漏;如果靠 OpenRewrite,就可以批量处理。

1.3 两者的边界

对比项 Renovate OpenRewrite
核心目标 依赖升级自动化 代码迁移自动化
主要输入 依赖文件、镜像版本、CI 配置 源码、POM、Gradle、YAML、Properties
输出结果 升级分支、MR/PR 代码 diff
擅长场景 小版本升级、安全升级、版本治理 大版本迁移、API 替换、框架升级
典型问题 这个依赖有没有新版本? 升级之后代码怎么改?
企业落地方式 GitLab/GitHub Bot、定时 Runner Maven/Gradle 插件、CI Job、迁移分支

更形象一点:

1
2
3
Renovate 像情报员:告诉你哪里有新版本,并把升级申请递上来。
OpenRewrite 像施工队:根据迁移规则,把代码和配置真正改好。
CI 像验收员:编译、测试、扫描,全过了才允许合并。

单独使用 Renovate,容易出现“版本升了,项目炸了”。

单独使用 OpenRewrite,又缺少持续发现依赖升级机会的能力。

所以最佳实践是把它们整合起来。

二、整体架构设计

2.1 推荐流水线

flowchart LR
    A[Renovate 定时运行] --> B[扫描依赖文件]
    B --> C[发现可升级依赖]
    C --> D[创建升级分支]
    D --> E[创建 MR/PR]
    E --> F[CI 触发]
    F --> G[OpenRewrite 自动迁移]
    G --> H[提交迁移代码到同一分支]
    H --> I[编译与测试]
    I --> J[人工 Review]
    J --> K[合并]

这个流程的核心目标是:

1
依赖升级不是孤立动作,而是一个完整的变更流水线。

也就是说,MR 里不应该只有一个版本号变化:

1
2
- <version>2.7.18</version>
+ <version>3.5.3</version>

更理想的 MR 是:

1
2
3
4
1. Renovate 修改依赖版本;
2. OpenRewrite 自动修改不兼容代码;
3. CI 运行编译和测试;
4. 开发者只需要 Review 剩余不可自动化的部分。

这才是真正能落地的升级自动化。

2.2 三种集成模式

Renovate 和 OpenRewrite 整合,一般有三种模式。

模式一:半自动模式

流程:

1
2
3
4
5
Renovate 创建 MR
开发者 checkout 这个分支
本地运行 OpenRewrite
开发者手工 commit 到同一个 MR
CI 验证

适合:

1
2
3
4
1. 刚开始试点;
2. 团队还不敢让 CI 自动改代码;
3. 大版本升级风险较高;
4. 老项目测试覆盖不够。

优点:安全、可控。
缺点:人工参与多。

模式二:CI 自动补丁模式

流程:

1
2
3
4
5
Renovate 创建 MR
CI 识别这是 Renovate 分支
CI 自动运行 OpenRewrite
如果有代码变化,CI 自动 commit 回 Renovate 分支
CI 再次编译测试

适合:

1
2
3
4
1. 团队有较好的 CI;
2. 测试覆盖较完整;
3. 想减少重复迁移劳动;
4. 主要处理框架升级、JUnit 迁移、Java 版本迁移。

优点:自动化程度高。
缺点:权限、循环触发、误改风险要控制好。

模式三:专项迁移分支模式

流程:

1
2
3
4
5
手工创建 migration/spring-boot-3.5 分支
OpenRewrite 先批量迁移代码
Renovate 后续持续维护依赖小版本
CI 验证
人工分批合并

适合:

1
2
3
4
5
1. Spring Boot 2 -> 3 这种大迁移;
2. Java 8/11 -> 17/21;
3. javax -> jakarta;
4. 多模块项目;
5. 改动范围很大,不适合由 Renovate 单次 MR 承担。

优点:迁移边界清晰。
缺点:周期更长,需要迁移计划。

三、基础准备

本文以一个企业 GitLab + Maven + Spring Boot 项目为例。

假设仓库是:

1
background/finance

项目结构类似:

1
2
3
4
5
6
7
8
9
finance/
├── renovate.json
├── pom.xml
├── com.amugua.finance.impl/
│ └── pom.xml
├── com.amugua.finance.api/
│ └── pom.xml
├── Dockerfile
└── .gitlab-ci.yml

本地环境:

1
2
3
4
5
6
JDK 17 或 JDK 21
Maven 3.8+
Node.js 18+
Git
GitLab Token
Nexus / Maven 私服账号,可选

版本参考:

组件 示例版本
Renovate 43.x
OpenRewrite Maven Plugin 6.41.0
OpenRewrite Gradle Plugin 7.34.0
rewrite-spring 6.32.1
rewrite-testing-frameworks 3.37.0
rewrite-migrate-java 3.36.0

版本会持续变化,生产中建议在文档和 CI 变量里显式固定版本,不要所有地方都写 latest.releaseRELEASE。试验阶段可以用 RELEASE,生产流水线最好固定版本,别让迁移工具自己“偷偷升级自己”。

四、Renovate 实战配置

4.1 仓库内配置:renovate.json

先在仓库根目录创建 renovate.json

如果你只想让 Renovate 管 Maven,并且只扫描 com.amugua.finance.impl/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
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"enabledManagers": [
"maven"
],
"includePaths": [
"com.amugua.finance.impl/pom.xml"
],
"dependencyDashboard": true,
"packageRules": [
{
"description": "Only manage Amugua Maven coordinates in the impl project.",
"matchManagers": [
"maven"
],
"matchPackageNames": [
"!/^com\\.amugua(\\.|:)/"
],
"enabled": false
}
]
}

这个配置的含义是:

1
2
3
4
5
1. 只启用 Maven manager;
2. 只扫描 com.amugua.finance.impl/pom.xml;
3. 只管理 com.amugua 开头的 Maven 坐标;
4. 非 com.amugua 依赖全部禁用;
5. 开启 Dependency Dashboard,方便集中查看更新情况。

注意:

1
2
includePaths 是相对仓库根目录的路径。
所以运行 Renovate 时要从仓库根目录看路径,不是从子模块目录看路径。

如果你发现 Renovate 仍然扫描了 Dockerfile,说明这个 renovate.json 很可能没有生效。常见原因是:

1
2
3
4
5
1. renovate.json 没有 push 到默认分支;
2. Renovate 读取的是 master,但你把配置提交到了 develop;
3. 你用了远程模式,但本地文件还没提交;
4. 运行配置里覆盖了仓库配置;
5. 配置文件不在 Renovate 支持的位置。

可以用:

1
2
git fetch origin master
git show origin/master:renovate.json

确认远程默认分支上到底有没有这份配置。

4.2 Spring Boot 升级专用配置

如果你的目标是让 Renovate 专门处理 Spring Boot / Spring Cloud 升级,可以这样写:

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
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"enabledManagers": [
"maven"
],
"dependencyDashboard": true,
"prConcurrentLimit": 5,
"prHourlyLimit": 2,
"packageRules": [
{
"description": "Spring Boot framework upgrade group.",
"matchManagers": [
"maven"
],
"matchPackageNames": [
"org.springframework.boot:spring-boot-starter-parent",
"org.springframework.boot:spring-boot-dependencies",
"org.springframework.boot:spring-boot-maven-plugin",
"/^org\\.springframework\\.boot:/"
],
"groupName": "spring boot framework"
},
{
"description": "Major upgrades require dashboard approval.",
"matchUpdateTypes": [
"major"
],
"dependencyDashboardApproval": true
},
{
"description": "Patch upgrades can be automerged after CI passes.",
"matchUpdateTypes": [
"patch"
],
"automerge": true
}
]
}

这个配置适合持续治理:

1
2
3
4
小版本升级可以自动化;
大版本升级必须先人工审批;
Spring Boot 相关依赖集中到一个 MR;
避免一堆零散 PR 把团队淹没。

4.3 自托管运行配置:config.js

仓库里的 renovate.json 只放仓库级配置,不要放 token、endpoint、repositories 这类运行级配置。

本地或 CI 运行 Renovate 时,可以单独创建一个 config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
platform: 'gitlab',
endpoint: 'https://git.dianplus.cn/api/v4',
token: process.env.RENOVATE_TOKEN,

repositories: [
'background/finance'
],

gitAuthor: 'Renovate Bot <renovate-bot@example.com>',

onboarding: false,
requireConfig: 'optional'
};

运行:

1
LOG_LEVEL=debug npx --yes --package renovate -- renovate --config-file ./config.js --dry-run=full

确认无误后正式运行:

1
LOG_LEVEL=info npx --yes --package renovate -- renovate --config-file ./config.js

4.4 私服 Maven 仓库配置

如果公司依赖来自 Nexus / Artifactory,建议在 config.js 里配置 hostRulesregistryUrls

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
module.exports = {
platform: 'gitlab',
endpoint: 'https://git.dianplus.cn/api/v4',
token: process.env.RENOVATE_TOKEN,

repositories: [
'background/finance'
],

gitAuthor: 'Renovate Bot <renovate-bot@example.com>',

hostRules: [
{
hostType: 'maven',
matchHost: 'https://nexus.example.com/repository/maven-public/',
username: process.env.MAVEN_REPO_USERNAME,
password: process.env.MAVEN_REPO_PASSWORD
}
],

packageRules: [
{
matchDatasources: [
'maven'
],
registryUrls: [
'https://nexus.example.com/repository/maven-public/'
]
}
]
};

运行前设置环境变量:

1
2
3
export RENOVATE_TOKEN="glpat_xxxxxxxxxxxxxxxxxxxx"
export MAVEN_REPO_USERNAME="renovate"
export MAVEN_REPO_PASSWORD="your-password"

注意:不要把私服密码写进 renovate.json。仓库配置会被所有人看到,密码一旦提交,故事就从“依赖治理”变成“事故复盘”了。

4.5 本地验证 Renovate 配置

校验配置文件

1
2
cd /your/project/root
npx --yes --package renovate -- renovate-config-validator

本地只扫描当前目录

1
2
3
4
5
6
unset RENOVATE_CONFIG
unset RENOVATE_REPOSITORIES
unset RENOVATE_ENDPOINT

cd /your/project/root
LOG_LEVEL=debug npx --yes --package renovate -- renovate --platform=local --dry-run=extract 2>&1 | tee renovate-local.log

注意:

1
platform=local 时,不支持 repositories。

如果你看到:

1
Invalid configuration: repositories list not supported when platform=local

说明你把本地扫描模式和远程仓库模式混用了。

正确理解:

1
2
platform=local:只扫描当前本地目录,不创建分支,不创建 MR。
platform=gitlab:连接 GitLab 仓库,可以创建分支和 MR。

远程 dry-run

1
2
3
4
5
6
7
8
9
10
11
12
export RENOVATE_TOKEN="glpat_xxxxxxxxxxxxxxxxxxxx"

export RENOVATE_CONFIG='{
"platform": "gitlab",
"endpoint": "https://git.dianplus.cn/api/v4",
"repositories": [
"background/finance"
],
"gitAuthor": "Renovate Bot <renovate-bot@example.com>"
}'

LOG_LEVEL=debug npx --yes --package renovate -- renovate --dry-run=full 2>&1 | tee renovate-remote.log

如果日志里出现:

1
DRY-RUN: Would commit files to branch renovate/xxx

说明 Renovate 已经发现可升级依赖,并且如果去掉 dry-run,就会创建分支和 MR。

五、OpenRewrite 实战配置

5.1 Maven 插件方式

在根 pom.xml 中配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<build>
<plugins>
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>6.41.0</version>
<configuration>
<exportDatatables>true</exportDatatables>
<activeRecipes>
<recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<version>6.32.1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>

执行 dry-run:

1
mvn rewrite:dryRun

真正修改代码:

1
mvn rewrite:run

查看改动:

1
git diff

5.2 不改 pom 的命令行方式

如果不想把 OpenRewrite 插件长期写进业务项目,可以直接用命令行:

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:6.32.1 \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 \
-Drewrite.exportDatatables=true

这种方式适合:

1
2
3
4
1. 临时迁移;
2. CI 自动运行;
3. 不希望业务 pom 增加迁移工具插件;
4. 多项目统一迁移时由脚本控制版本。

5.3 Spring Boot 3.5 迁移

OpenRewrite 的 Spring Boot 3.5 迁移 Recipe 是:

1
org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5

运行:

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:6.32.1 \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 \
-Drewrite.exportDatatables=true

它会尝试处理:

1
2
3
4
5
6
7
Spring Boot 版本升级;
Spring Boot Maven Plugin 升级;
Spring Boot parent/BOM 升级;
Spring Cloud 相关迁移;
Spring Security 相关迁移;
Spring Boot 配置项迁移;
部分 deprecated API 替换。

但是注意:OpenRewrite 不是魔法棒。下面这类问题仍然可能需要人工处理:

1
2
3
4
5
6
7
1. 公司自研框架适配 Spring Boot 3;
2. 老版本第三方库不支持 jakarta;
3. 运行时行为变化;
4. 配置中心中的远程配置;
5. XML 配置或反射字符串;
6. 动态 SQL、脚本、模板中的类名;
7. 测试覆盖不足导致的问题。

5.4 Java 21 迁移

Java 21 迁移 Recipe:

1
org.openrewrite.java.migrate.UpgradeToJava21

运行:

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:3.36.0 \
-Drewrite.activeRecipes=org.openrewrite.java.migrate.UpgradeToJava21 \
-Drewrite.exportDatatables=true

它会尝试处理:

1
2
3
4
5
source/target/release 调整到 21;
构建插件升级;
部分废弃 API 替换;
部分语法现代化;
Java 17 到 Java 21 的常见迁移动作。

5.5 JUnit 4 迁移 JUnit 5

JUnit 4 到 JUnit 5 的 Recipe:

1
org.openrewrite.java.testing.junit5.JUnit4to5Migration

运行:

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-testing-frameworks:3.37.0 \
-Drewrite.activeRecipes=org.openrewrite.java.testing.junit5.JUnit4to5Migration \
-Drewrite.exportDatatables=true

如果是 Spring Boot 2.x 项目,Spring 相关测试迁移也可以考虑:

1
org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration

它对 Spring Test Runner、Spring Extension、@RunWith 等场景更友好。

六、整合方案一:半自动模式

半自动模式最适合刚落地时使用。它不会让 CI 自动提交代码,风险最低。

6.1 流程

sequenceDiagram
    participant R as Renovate
    participant G as GitLab
    participant D as Developer
    participant O as OpenRewrite
    participant C as CI

    R->>G: 创建依赖升级 MR
    D->>G: checkout Renovate 分支
    D->>O: 本地运行 OpenRewrite
    O->>D: 修改源码、pom、配置
    D->>G: commit 到同一 MR
    G->>C: 触发 CI
    C->>G: 返回编译/测试结果

6.2 操作示例

Renovate 创建了一个 MR,分支名类似:

1
renovate/spring-boot-framework

本地拉取:

1
2
git fetch origin renovate/spring-boot-framework
git checkout -b renovate/spring-boot-framework origin/renovate/spring-boot-framework

运行 OpenRewrite:

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:6.32.1 \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 \
-Drewrite.exportDatatables=true

查看 diff:

1
2
git diff --stat
git diff

跑测试:

1
mvn clean test

提交到同一个分支:

1
2
3
git add .
git commit -m "chore: apply OpenRewrite Spring Boot migration"
git push origin renovate/spring-boot-framework

这时 MR 中就包含两类改动:

1
2
1. Renovate 的依赖版本升级;
2. OpenRewrite 的代码和配置迁移。

6.3 适用建议

首次在老项目里使用时,建议先半自动。

原因很简单:你需要先观察 OpenRewrite 对你们公司项目的“改代码风格”。工具是好工具,但第一次上来就让它自动 commit,多少有点像刚认识就把家门钥匙给出去,浪漫是浪漫,风险也是真风险。

七、整合方案二:CI 自动补丁模式

CI 自动补丁模式是更高级的玩法。

核心目标:

Renovate 创建 MR 后,CI 自动运行 OpenRewrite,并把代码迁移结果 commit 回 Renovate 分支。

7.1 GitLab CI 示例

.gitlab-ci.yml

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
stages:
- rewrite
- test

variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
REWRITE_MAVEN_PLUGIN_VERSION: "6.41.0"
REWRITE_SPRING_VERSION: "6.32.1"

cache:
key: "${CI_PROJECT_NAME}-maven"
paths:
- .m2/repository

openrewrite:spring-boot:
stage: rewrite
image: maven:3.9.9-eclipse-temurin-21
rules:
- if: '$CI_COMMIT_BRANCH =~ /^renovate\//'
when: manual
- when: never
before_script:
- git config user.name "Renovate Bot"
- git config user.email "renovate-bot@example.com"
script:
- |
mvn -U org.openrewrite.maven:rewrite-maven-plugin:${REWRITE_MAVEN_PLUGIN_VERSION}:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:${REWRITE_SPRING_VERSION} \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 \
-Drewrite.exportDatatables=true
- |
if ! git diff --quiet; then
git add .
git commit -m "chore: apply OpenRewrite migration [skip ci]"
git push "https://oauth2:${RENOVATE_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" "HEAD:${CI_COMMIT_REF_NAME}"
else
echo "No OpenRewrite changes detected."
fi

maven:test:
stage: test
image: maven:3.9.9-eclipse-temurin-21
script:
- mvn clean test

这里我把 openrewrite:spring-boot 设置成了 manual,原因是:

1
不是所有 Renovate 分支都应该自动跑 Spring Boot 迁移。

比如 fastjson 升级、commons-lang3 升级、小版本 patch 升级,通常不需要跑 Spring Boot 大迁移 Recipe。

如果你确认只对特定分支运行,可以进一步限制规则:

1
2
3
4
rules:
- if: '$CI_COMMIT_BRANCH =~ /^renovate\/spring-boot/'
when: on_success
- when: never

7.2 防止 CI 无限循环

CI 自动 commit 回分支时,容易触发新一轮 CI。为了避免循环,可以在 commit message 中加:

1
[skip ci]

例如:

1
git commit -m "chore: apply OpenRewrite migration [skip ci]"

也可以用更精细的规则:

1
2
3
rules:
- if: '$CI_COMMIT_BRANCH =~ /^renovate\// && $CI_COMMIT_MESSAGE !~ /apply OpenRewrite migration/'
when: on_success

7.3 Token 权限

RENOVATE_PUSH_TOKEN 至少需要能 push 当前 MR 分支。

GitLab 中常见选择:

1
2
3
Project Access Token
Group Access Token
专门的 Bot 用户 Personal Access Token

权限建议:

1
2
3
read_repository
write_repository
api,可选,如果还要操作 MR

如果分支受保护,要确认 Bot 用户有权限 push。别让工具跑到最后一步才发现没有权限,那感觉就像外卖到了小区门口,但骑手进不来。

7.4 自动补丁模式的风险控制

建议做这些限制:

1
2
3
4
5
6
7
1. 默认 manual,稳定后再自动;
2. 只对 renovate/ 特定分支运行;
3. 只对特定依赖组运行,例如 spring boot framework;
4. commit message 加 [skip ci] 防止循环;
5. OpenRewrite job 之后必须跑 mvn clean test;
6. 合并前必须人工 review;
7. 老项目第一次不要开 automerge。

八、整合方案三:专项迁移分支模式

对于 Spring Boot 2 到 3、Java 11 到 21 这种大迁移,我更推荐专项迁移分支。

8.1 创建迁移分支

1
2
3
git checkout master
git pull
git checkout -b migration/spring-boot-3.5-java-21

8.2 先迁移 Java 21

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:3.36.0 \
-Drewrite.activeRecipes=org.openrewrite.java.migrate.UpgradeToJava21 \
-Drewrite.exportDatatables=true

提交:

1
2
git add .
git commit -m "chore: migrate build to Java 21 with OpenRewrite"

8.3 再迁移 Spring Boot 3.5

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:6.32.1 \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 \
-Drewrite.exportDatatables=true

提交:

1
2
git add .
git commit -m "chore: migrate to Spring Boot 3.5 with OpenRewrite"

8.4 再迁移 JUnit 5

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-testing-frameworks:3.37.0 \
-Drewrite.activeRecipes=org.openrewrite.java.testing.junit5.JUnit4to5Migration \
-Drewrite.exportDatatables=true

提交:

1
2
git add .
git commit -m "test: migrate JUnit 4 tests to JUnit 5 with OpenRewrite"

8.5 最后运行验证

1
2
mvn clean test
mvn clean package -DskipTests

如果项目比较大,可以分模块跑:

1
mvn -pl com.amugua.finance.impl -am clean test

8.6 迁移顺序建议

推荐顺序:

1
2
3
4
5
6
1. 先升级 JDK 构建配置;
2. 再升级 Spring Boot;
3. 再处理 javax -> jakarta;
4. 再迁移测试框架;
5. 再清理废弃 API;
6. 最后跑全量测试和人工验收。

不要一口气把所有 Recipe 全扔进去。那样 diff 会非常大,Review 会变成灾难片。

九、完整实战案例:GitLab + Renovate + OpenRewrite + Maven

下面给一套比较完整的企业落地方案。

9.1 目标

我们希望实现:

1
2
3
4
5
6
7
1. Renovate 定时扫描 background/finance 仓库;
2. 只管理 Maven 依赖;
3. Spring Boot 相关升级聚合成一个 MR;
4. 大版本升级需要人工审批;
5. Spring Boot 升级 MR 可触发 OpenRewrite;
6. OpenRewrite 自动提交迁移代码到同一个分支;
7. CI 编译测试通过后人工合并。

9.2 仓库 renovate.json

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
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"enabledManagers": [
"maven"
],
"dependencyDashboard": true,
"labels": [
"dependencies",
"renovate"
],
"prConcurrentLimit": 5,
"prHourlyLimit": 2,
"packageRules": [
{
"description": "Group Spring Boot framework dependencies.",
"matchManagers": [
"maven"
],
"matchPackageNames": [
"org.springframework.boot:spring-boot-starter-parent",
"org.springframework.boot:spring-boot-dependencies",
"org.springframework.boot:spring-boot-maven-plugin",
"/^org\\.springframework\\.boot:/"
],
"groupName": "spring boot framework",
"branchTopic": "spring-boot-framework"
},
{
"description": "Major updates require manual dashboard approval.",
"matchUpdateTypes": [
"major"
],
"dependencyDashboardApproval": true
},
{
"description": "Patch updates are low risk and can be automerged after CI.",
"matchUpdateTypes": [
"patch"
],
"automerge": false
}
]
}

这里我把 automerge 设成了 false。老项目刚接入时,不建议一上来就自动合并。等 CI、测试覆盖、回滚策略都稳定后,再逐步打开 patch 自动合并。

9.3 Renovate Runner 配置

本地或 CI 中创建 config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module.exports = {
platform: 'gitlab',
endpoint: 'https://git.dianplus.cn/api/v4',
token: process.env.RENOVATE_TOKEN,

repositories: [
'background/finance'
],

gitAuthor: 'Renovate Bot <renovate-bot@example.com>',
onboarding: false,
requireConfig: 'required',

hostRules: [
{
hostType: 'maven',
matchHost: 'https://nexus.example.com/repository/maven-public/',
username: process.env.MAVEN_REPO_USERNAME,
password: process.env.MAVEN_REPO_PASSWORD
}
]
};

运行 dry-run:

1
LOG_LEVEL=debug npx --yes --package renovate -- renovate --config-file ./config.js --dry-run=full

正式运行:

1
LOG_LEVEL=info npx --yes --package renovate -- renovate --config-file ./config.js

9.4 GitLab 定时任务

如果你把 Renovate 放到单独的 renovate-runner 项目,可以在 .gitlab-ci.yml 中这样写:

1
2
3
4
5
6
7
8
9
10
stages:
- renovate

renovate:
stage: renovate
image: renovate/renovate:43
script:
- renovate --config-file config.js
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'

然后在 GitLab 里配置 Pipeline Schedule,例如:

1
每周一早上 08:00 跑一次

不要每几分钟跑一次。依赖升级不是刷短视频,不需要这么频繁。

9.5 业务仓库 OpenRewrite Job

background/finance 仓库 .gitlab-ci.yml 里增加:

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
stages:
- rewrite
- build
- test

variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
REWRITE_MAVEN_PLUGIN_VERSION: "6.41.0"
REWRITE_SPRING_VERSION: "6.32.1"

cache:
key: "${CI_PROJECT_NAME}-maven"
paths:
- .m2/repository

openrewrite:spring-boot:
stage: rewrite
image: maven:3.9.9-eclipse-temurin-21
rules:
- if: '$CI_COMMIT_BRANCH =~ /^renovate\/spring-boot-framework/'
when: manual
- when: never
before_script:
- git config user.name "Renovate Bot"
- git config user.email "renovate-bot@example.com"
script:
- |
mvn -U org.openrewrite.maven:rewrite-maven-plugin:${REWRITE_MAVEN_PLUGIN_VERSION}:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:${REWRITE_SPRING_VERSION} \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 \
-Drewrite.exportDatatables=true
- |
if ! git diff --quiet; then
git add .
git commit -m "chore: apply OpenRewrite Spring Boot migration [skip ci]"
git push "https://oauth2:${RENOVATE_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" "HEAD:${CI_COMMIT_REF_NAME}"
else
echo "No OpenRewrite changes detected."
fi

maven:compile:
stage: build
image: maven:3.9.9-eclipse-temurin-21
script:
- mvn -U clean compile

maven:test:
stage: test
image: maven:3.9.9-eclipse-temurin-21
script:
- mvn test

这套流程运行后,会出现这样的效果:

1
2
3
4
Renovate MR:升级 Spring Boot 版本
OpenRewrite Job:点击后自动修改代码
CI:继续编译测试
开发者:Review MR

9.6 分支命名与规则匹配

Renovate 的分支名可能会因为配置不同而变化。

如果你想稳定匹配,可以在 packageRules 里配置:

1
2
3
4
5
6
7
{
"matchPackageNames": [
"/^org\\.springframework\\.boot:/"
],
"groupName": "spring boot framework",
"branchTopic": "spring-boot-framework"
}

这样分支更容易被 CI 规则识别:

1
2
rules:
- if: '$CI_COMMIT_BRANCH =~ /^renovate\/spring-boot-framework/'

十、实战案例二:只升级公司内部 com.amugua 依赖

你可能有这样的场景:

1
2
3
4
公司内部有很多 com.amugua.* 依赖;
这些依赖经常发布新版本;
但不希望 Renovate 管所有第三方依赖;
只想先让它管理某个 impl 模块里的公司内部包。

可以这样配置:

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
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"enabledManagers": [
"maven"
],
"includePaths": [
"com.amugua.finance.impl/pom.xml"
],
"dependencyDashboard": true,
"packageRules": [
{
"description": "Only manage Amugua Maven coordinates in the impl project.",
"matchManagers": [
"maven"
],
"matchPackageNames": [
"!/^com\\.amugua(\\.|:)/"
],
"enabled": false
},
{
"description": "Group all Amugua internal dependencies.",
"matchManagers": [
"maven"
],
"matchPackageNames": [
"/^com\\.amugua(\\.|:)/"
],
"groupName": "amugua internal dependencies",
"branchTopic": "amugua-internal-dependencies"
}
]
}

如果跑出来仍然看到:

1
2
dockerfile: fileCount 1
maven: fileCount 2

说明配置没生效。正确情况下应该接近:

1
maven: fileCount 1

排查顺序:

1
2
3
4
5
6
7
8
9
# 1. 看默认分支上有没有配置
git fetch origin master
git show origin/master:renovate.json

# 2. 打印最终配置
LOG_LEVEL=debug npx --yes --package renovate -- renovate --dry-run=full --print-config 2>&1 | tee renovate-debug.log

# 3. 检查关键字段
grep -n "enabledManagers\|includePaths\|packageRules\|dockerfile" renovate-debug.log

如果你只是本地测试,不要带 repositories:

1
2
3
4
5
unset RENOVATE_CONFIG
unset RENOVATE_REPOSITORIES
unset RENOVATE_ENDPOINT

LOG_LEVEL=debug npx --yes --package renovate -- renovate --platform=local --dry-run=extract

如果要远程创建 MR,不要用 platform=local

1
2
3
4
5
6
7
8
9
10
export RENOVATE_CONFIG='{
"platform": "gitlab",
"endpoint": "https://git.dianplus.cn/api/v4",
"repositories": [
"background/finance"
],
"gitAuthor": "Renovate Bot <renovate-bot@example.com>"
}'

LOG_LEVEL=debug npx --yes --package renovate -- renovate --dry-run=full

十一、实战案例三:Spring Boot 2.7 升级到 3.5

这是 Renovate + OpenRewrite 最典型的组合场景。

11.1 初始项目

pom.xml

1
2
3
4
5
6
7
8
9
10
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>

<properties>
<java.version>11</java.version>
</properties>

代码中存在:

1
2
3
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.annotation.PostConstruct;

测试中存在:

1
2
3
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;

11.2 Renovate 先创建升级 MR

Renovate 将 Spring Boot 相关依赖升级到 3.5.x。

MR 中可能出现:

1
2
- <version>2.7.18</version>
+ <version>3.5.3</version>

这一步只是依赖版本升级。此时项目很可能编译失败。

11.3 OpenRewrite 迁移 Spring Boot

运行:

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:6.32.1 \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 \
-Drewrite.exportDatatables=true

可能产生的变化:

1
2
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
1
2
-import javax.validation.Valid;
+import jakarta.validation.Valid;
1
2
-import javax.annotation.PostConstruct;
+import jakarta.annotation.PostConstruct;

11.4 OpenRewrite 迁移 JUnit 5

运行:

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-testing-frameworks:3.37.0 \
-Drewrite.activeRecipes=org.openrewrite.java.testing.junit5.JUnit4to5Migration \
-Drewrite.exportDatatables=true

可能产生的变化:

1
2
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
1
2
-import org.junit.Assert.assertEquals;
+import org.junit.jupiter.api.Assertions.assertEquals;

11.5 编译验证

1
mvn clean test

如果报错,常见原因有:

1
2
3
4
5
6
1. 第三方依赖不兼容 Spring Boot 3;
2. 公司自研 starter 没支持 jakarta;
3. 旧版本 MyBatis、Dubbo、ShardingSphere 等依赖不兼容;
4. Spring Security 配置需要人工处理;
5. 反射字符串中的 javax 没被替换;
6. 配置中心中的配置项没有同步迁移。

11.6 Review 重点

这种 MR Review 时,不要只看能不能编译,还要看:

1
2
3
4
5
6
7
1. Spring Boot 版本是否符合公司统一基线;
2. Spring Cloud 版本是否匹配;
3. Jakarta 迁移是否完整;
4. starter 是否有兼容版本;
5. 测试是否只是“能跑”,还是确实覆盖关键业务;
6. 配置中心、部署脚本、镜像基础版本是否同步调整;
7. 日志、监控、APM、网关、鉴权是否受影响。

十二、实战案例四:Java 11 升级 Java 21

Java 21 是当前很多企业后端项目的目标版本。配合 Spring Boot 3.x,更适合做长期技术基线。

12.1 Renovate 负责依赖基线更新

Renovate 可以持续升级:

1
2
3
4
5
maven-compiler-plugin
maven-surefire-plugin
maven-failsafe-plugin
maven-dependency-plugin
spring-boot-maven-plugin

但 Java 源码和构建配置的迁移,更适合 OpenRewrite。

12.2 OpenRewrite 执行 Java 21 迁移

1
2
3
4
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:3.36.0 \
-Drewrite.activeRecipes=org.openrewrite.java.migrate.UpgradeToJava21 \
-Drewrite.exportDatatables=true

可能改动:

1
2
3
<properties>
<java.version>21</java.version>
</properties>

或者:

1
<maven.compiler.release>21</maven.compiler.release>

12.3 CI 镜像同步升级

.gitlab-ci.yml

1
image: maven:3.9.9-eclipse-temurin-21

Dockerfile:

1
FROM eclipse-temurin:21-jre

注意:JDK 升级不是只改 pom.xml。你还要同步检查:

1
2
3
4
5
6
7
1. CI 镜像;
2. Docker 基础镜像;
3. 生产环境 JRE;
4. APM Agent 是否支持 Java 21;
5. Lombok、MapStruct、Maven 插件是否兼容;
6. IDE 编译版本;
7. 构建机 Maven/JDK 版本。

十三、企业落地规范

13.1 MR 类型分级

建议把依赖升级分成三类:

类型 示例 处理策略
Patch 3.5.1 -> 3.5.2 CI 通过后可考虑自动合并
Minor 3.4.x -> 3.5.x 需要 Review,必要时跑 OpenRewrite
Major 2.x -> 3.x 必须专项迁移,禁止自动合并

13.2 推荐分支策略

1
2
3
4
renovate/*:Renovate 自动升级分支;
migration/*:专项迁移分支;
hotfix/*:紧急安全修复;
release/*:发布分支。

13.3 推荐流水线门禁

每个依赖升级 MR 至少跑:

1
2
3
4
5
6
7
1. mvn clean compile;
2. mvn test;
3. 单元测试覆盖率检查;
4. 静态扫描;
5. 依赖漏洞扫描;
6. Docker 镜像构建;
7. 必要时跑集成测试。

Spring Boot / JDK 大迁移还应增加:

1
2
3
4
5
6
1. 启动测试;
2. 配置加载测试;
3. 数据源连接测试;
4. Redis/MQ/Nacos/Dubbo 注册测试;
5. 核心接口冒烟测试;
6. 回滚方案检查。

13.4 建议的团队流程

1
2
3
4
5
6
第一阶段:只开启 Renovate dry-run,观察扫描结果。
第二阶段:开启 Renovate MR,但禁止 automerge。
第三阶段:对低风险 patch 开启自动合并。
第四阶段:引入 OpenRewrite 半自动迁移。
第五阶段:对稳定 Recipe 开启 CI 自动补丁。
第六阶段:沉淀公司内部 Recipe 和版本基线。

不要一上来就:

1
全仓库扫描 + 全依赖自动升级 + OpenRewrite 自动 commit + automerge

这不是自动化,这是把方向盘交给工具,然后希望它懂你们业务。工具懂语法,不懂 KPI。

十四、常见问题排查

14.1 Renovate 只创建 onboarding PR

日志:

1
DRY-RUN: Would create onboarding PR

说明 Renovate 认为仓库还没有完成初始化。

处理方式:

1
2
3
4
1. 确认 renovate.json 已经提交到默认分支;
2. 如果想跳过 onboarding,在运行配置中设置 onboarding=false;
3. 如果要求必须有配置,设置 requireConfig=required;
4. 不要把 self-hosted 配置写进 renovate.json。

14.2 Repository is not found

通常是 repositories 写错。

GitLab 中应该写:

1
background/finance

不要写:

1
https://git.dianplus.cn/background/finance.git

也不要带 .git

验证:

1
git remote get-url origin

提取 repository slug:

1
2
git remote get-url origin \
| sed -E 's#https?://[^/]+/##; s#git@[^:]+:##; s#\.git$##'

14.3 platform=local 与 repositories 冲突

错误:

1
Invalid configuration: repositories list not supported when platform=local

原因:

1
platform=local 是本地目录扫描,不允许 repositories。

解决:

1
2
3
4
5
unset RENOVATE_CONFIG
unset RENOVATE_REPOSITORIES
unset RENOVATE_ENDPOINT

LOG_LEVEL=debug npx --yes --package renovate -- renovate --platform=local --dry-run=extract

或者改用 GitLab 远程模式:

1
LOG_LEVEL=debug npx --yes --package renovate -- renovate --dry-run=full

14.4 Renovate 没有按 includePaths 生效

先看远程默认分支配置:

1
2
git fetch origin master
git show origin/master:renovate.json

再打印最终配置:

1
LOG_LEVEL=debug npx --yes --package renovate -- renovate --dry-run=full --print-config 2>&1 | tee renovate-debug.log

检查:

1
grep -n "enabledManagers\|includePaths\|packageRules" renovate-debug.log

14.5 OpenRewrite 找不到 Recipe

报错可能类似:

1
Recipe(s) not found

常见原因:

1
2
3
4
5
1. 没有配置 recipeArtifactCoordinates;
2. recipe 包版本不对;
3. recipe 名称写错;
4. 私服无法访问 Maven Central;
5. 公司 Maven settings.xml 没有正确配置 mirror。

解决示例:

1
2
mvn -U org.openrewrite.maven:rewrite-maven-plugin:6.41.0:discover \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:6.32.1

14.6 OpenRewrite 改动太大

处理建议:

1
2
3
4
5
6
1. 拆分 Recipe;
2. 每次只跑一个迁移主题;
3. 先 dryRun;
4. 小步 commit;
5. 每一步都跑测试;
6. 不要把 Java 21、Spring Boot 3、JUnit 5、代码格式化全部塞进一个 commit。

十五、推荐项目结构

可以在企业内部建一个专门的治理仓库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dependency-governance/
├── renovate/
│ ├── config.js
│ ├── presets/
│ │ ├── java-maven.json
│ │ ├── spring-boot.json
│ │ └── internal-amugua.json
│ └── README.md
├── openrewrite/
│ ├── rewrite-spring-boot-35.sh
│ ├── rewrite-java-21.sh
│ ├── rewrite-junit5.sh
│ └── rewrite.yml
├── ci/
│ ├── gitlab-renovate.yml
│ └── gitlab-openrewrite.yml
└── docs/
├── upgrade-policy.md
└── rollback-policy.md

这样做的好处:

1
2
3
4
5
1. 依赖治理配置集中管理;
2. OpenRewrite 命令统一维护;
3. CI 模板可以复用;
4. 各业务项目只引用配置,不重复造轮子;
5. 后续可以沉淀公司内部 Recipe。

十六、推荐 Shell 脚本

16.1 run-renovate-local.sh

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env bash
set -euo pipefail

unset RENOVATE_CONFIG || true
unset RENOVATE_REPOSITORIES || true
unset RENOVATE_ENDPOINT || true

LOG_LEVEL=debug npx --yes --package renovate -- renovate \
--platform=local \
--dry-run=extract \
2>&1 | tee renovate-local.log

16.2 run-renovate-gitlab-dryrun.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env bash
set -euo pipefail

: "${RENOVATE_TOKEN:?RENOVATE_TOKEN is required}"

export RENOVATE_CONFIG='{
"platform": "gitlab",
"endpoint": "https://git.dianplus.cn/api/v4",
"repositories": [
"background/finance"
],
"gitAuthor": "Renovate Bot <renovate-bot@example.com>"
}'

LOG_LEVEL=debug npx --yes --package renovate -- renovate \
--dry-run=full \
2>&1 | tee renovate-remote.log

16.3 run-openrewrite-spring-boot-35.sh

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env bash
set -euo pipefail

REWRITE_MAVEN_PLUGIN_VERSION="6.41.0"
REWRITE_SPRING_VERSION="6.32.1"

mvn -U org.openrewrite.maven:rewrite-maven-plugin:${REWRITE_MAVEN_PLUGIN_VERSION}:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:${REWRITE_SPRING_VERSION} \
-Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_5 \
-Drewrite.exportDatatables=true

16.4 run-openrewrite-java-21.sh

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env bash
set -euo pipefail

REWRITE_MAVEN_PLUGIN_VERSION="6.41.0"
REWRITE_MIGRATE_JAVA_VERSION="3.36.0"

mvn -U org.openrewrite.maven:rewrite-maven-plugin:${REWRITE_MAVEN_PLUGIN_VERSION}:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:${REWRITE_MIGRATE_JAVA_VERSION} \
-Drewrite.activeRecipes=org.openrewrite.java.migrate.UpgradeToJava21 \
-Drewrite.exportDatatables=true

16.5 run-openrewrite-junit5.sh

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env bash
set -euo pipefail

REWRITE_MAVEN_PLUGIN_VERSION="6.41.0"
REWRITE_TESTING_VERSION="3.37.0"

mvn -U org.openrewrite.maven:rewrite-maven-plugin:${REWRITE_MAVEN_PLUGIN_VERSION}:run \
-Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-testing-frameworks:${REWRITE_TESTING_VERSION} \
-Drewrite.activeRecipes=org.openrewrite.java.testing.junit5.JUnit4to5Migration \
-Drewrite.exportDatatables=true

十七、最终建议

Renovate 和 OpenRewrite 的整合,不要理解成“两个工具简单串起来”。

更准确地说,它是一套技术债治理机制:

1
2
3
4
5
Renovate 负责持续发现;
OpenRewrite 负责自动修复;
CI 负责质量验证;
Review 负责业务判断;
版本基线负责长期约束。

如果团队没有版本基线,Renovate 会产生很多升级 MR,最后变成噪音。

如果团队没有测试,OpenRewrite 改完代码,你也不知道对不对。

如果团队没有 Review 规范,自动化工具越强,事故传播越快。

所以真正的落地顺序应该是:

1
2
3
4
5
先观测,再限制;
先 dry-run,再创建 MR;
先人工运行 OpenRewrite,再 CI 自动补丁;
先小版本治理,再大版本迁移;
先单仓试点,再多仓推广。

这套组合用好了,依赖升级就不再是“每年一次的大型恐怖片”,而是持续、可控、可回滚的日常工程能力。

参考资料

  1. OpenRewrite 官方文档:https://docs.openrewrite.org/
  2. OpenRewrite Maven Plugin 配置:https://docs.openrewrite.org/reference/rewrite-maven-plugin
  3. OpenRewrite 最新模块版本:https://docs.openrewrite.org/reference/latest-versions-of-every-openrewrite-module
  4. OpenRewrite Spring Boot 3.5 迁移 Recipe:https://docs.openrewrite.org/recipes/java/spring/boot3/upgradespringboot_3_5-community-edition
  5. OpenRewrite Java 21 迁移 Recipe:https://docs.openrewrite.org/recipes/java/migrate/upgradetojava21
  6. OpenRewrite JUnit 4 到 JUnit 5 迁移 Recipe:https://docs.openrewrite.org/recipes/java/testing/junit5/junit4to5migration
  7. Renovate 官方文档:https://docs.renovatebot.com/
  8. Renovate Configuration Options:https://docs.renovatebot.com/configuration-options/
  9. Renovate Self-Hosted Configuration:https://docs.renovatebot.com/self-hosted-configuration/
  10. Renovate GitLab Platform:https://docs.renovatebot.com/modules/platform/gitlab/
  11. Renovate Local Platform:https://docs.renovatebot.com/modules/platform/local/

启示录

依赖升级不是改版本号,而是一次小型系统迁移。

工具能替你搬砖,但不能替你判断业务风险。

自动化的最高境界,不是无人看管,而是让人只看真正值得看的地方。


Renovate 与 OpenRewrite:从依赖升级到代码自动迁移的工程化实践
https://allendericdalexander.github.io/2026/06/12/scm/renovate-openrewrite-integration-guide/
作者
AtLuoFu
发布于
2026年6月12日
许可协议