使用 renovate(bot) 维护升级依赖版本
背景
在开发过程中,可能遇到这样的情况,在公司接触(维护)一些老旧项目时 —— 因为历史原因(比如a.早先的项目基建并不完善,b.单纯的在于开发人员不愿意付出时间成本/承担风险去定期维护升级依赖版本,无论是手动维护还是依赖自动化工具),导致一些依赖的版本过于老旧,隐患 bug 或我们需要的新特性只在高版本中得到了支持,而我们此时(可选择的解决手段之一就是)手动尝试升级依赖。
但同时,手动升级依赖却往往不能一帆风顺,多数情况下依赖版本升级后,启动开发环境时,出现各种花式报错(比如:其他关联依赖发生版本冲突,此时也需要匹配升级,后续产生一系列连锁行为),导致项目启动失败。
显然的,如果最初:项目本身有定期维护升级依赖版本,那么如上的问题可以得到有效避免,这也是本文接下来涉及主题:使用当下流行工具(比如 "renovate") 来自动化处理项目依赖版本的(周期性)维护升级需求。
前言
当前社区普遍选用的两款可用于对依赖版本维护升级的自动化工具有是: "Dependabot" 以及 "Renovate", 两款工具的特性对比见:docs.renovatebot.com
正题
吾辈从流行度(以及配置语法的便捷程度)选择使用了 renovate 方案,以下皆以 renovate(bot) 配置为例,主要作一些(期望具备什么自动化行为等)考量点的分享。
同时 renovate(bot) (引入项目的)初始化手段多样,更多细节可查看项目首页: github.com/renovatebot
下面主要分享配置 renovate(bot) 时可纳入考量的细节点:
- 版本细节 - 对于第三方依赖迭代的主版本,次要版本,以及补丁版本,是否分别约定不同的自动化行为;
- 周期更新 - 约定自动化工具的周期执行时间;
- 依赖固定 - 规避 renovate(bot) 不能处理升级问题;
- (...后续或待完善)
1. 版本细节
在当前前端项目的依赖版本(package.json#dependencies
中,主要使用语义化版本声明,即(MAJOR.MINOR.PATCH
) (eg: lib-name: ^3.2.1
)
Given a version number MAJOR.MINOR.PATCH, increment the:
MAJOR version when you make incompatible API changes; MINOR version when you add functionality in a backward compatible manner; PATCH version when you make backward compatible bug fixes.
吾辈的意见:对于 MAJOR 版本的升级,因为多是不兼容(破坏式)的 API 更新,对于这样的更新,我们需要保守求稳,配置 renovate 对每个依赖单独提出升级 PR (同时这也是 renovate(bot) 的静默配置),
当然我们也可以使用配置 :separateMultipleMajorReleases
作如下显式声明:
{
"extends": ["config:best-practices", ":separateMultipleMajorReleases"]
}
而对于如 MINOR 或者 PATCH 的版本更新,则可以一次性将多个依赖版本升级聚合到一个 PR 中(如此最大的好处即是不会短时间内触发过多 renovate-PR,因为某些第三方依赖往往有频繁的 MINOR 、PATCH 版本发布)
{
"packageRules": [
{
"groupName": "all minor patch dependencies",
"groupSlug": "all-minor-patch",
"matchPackagePatterns": ["*"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
}
]
}
另外,可以自行考虑是否要处理依赖的(或许频繁的)PATCH 即补丁版本更新,如果想要跳过 PATCH 版本,可如下声明:
{
"patch": {
"enabled": false
}
}
(更多关于语义化版本(Semantic Versioning)的细节补充可见:https://semver.org/)
2. 周期更新
接下来关注,renovate 的静默配置下,自动化执行的周期是不定且尽可能频繁的,而实际项目中,也许我们只需要以“周”或“月”为单位来定期更新,那么可如下声明配置:
{
"timezone": "Asia/Shanghai",
"schedule": ["after 10pm every weekend", "before 5am every weekend"]
}
(更多周期性配置语法见: docs.renovatebot.com)
3. 依赖固定
不同的包管理工具(npm / yarn / pnpm / ...) 都提供了各自的依赖版本锁定文件 (eg: pnpm-lock.yaml
),其最大作用是:对于项目版本在 package.json#dependencies
字段下的版本模糊声明(如:^3.2.1
),能由 lock 文件下声明,包管理工具去安装依赖在该版本范围下的指定版本,以确保项目在不同的开发环境下安装启动后都有相同的表现行为,
但是,这可能仍然不能满足我们的:依赖版本自动化维护需求。
回到 renovate 配置项中,官方提供了这样的配置 rangeStrategy
(可用于处理所有未明确固定版本的依赖(到固定版本) )
{
"packageRules": [
{
"matchPackagePatterns": ["*"],
"rangeStrategy": "pin"
}
]
}
吾辈也在 renovate.json
配置文件使用了此声明,原因主要有二:
- 更明确的(语义化)版本标明
- 比如
3.3.5
就比^3.2.1
来得更加直接,不用再到 lock 文件中确定项目明确安装的版本;
- 比如
- 与 renovate(bot) 的自动化行为相关,
- 这也是吾辈最开始配置使用 renovate(bot) 遇到的一个(非典型)问题,吾辈发现在 项目的 renovate-PR 描述中,虽然显示了本次 PR 会批量升级多个依赖到更高版本,但最终
package.json
文件改动只涉及其中部分依赖。 - 直观讲即是:项目中的某个依赖声明为
lib-name: ^3.2.1
,在 renovate(bot) 自动化触发 PR 中也明确描述了本次升级包含了:lib-name: 3.2.1
->lib-name: 3.3.0
,但是最终本次 PR 对package.json
改动中,lib-name: ^3.2.1
未作变动(即无版本变更) - 而 renovate(bot) 对于
package.json
下固定了版本的依赖(比如:lib-anema: 4.1.1
,则可以对其顺利升级
- 这也是吾辈最开始配置使用 renovate(bot) 遇到的一个(非典型)问题,吾辈发现在 项目的 renovate-PR 描述中,虽然显示了本次 PR 会批量升级多个依赖到更高版本,但最终
(👉🏻 推荐阅读 -> 关于固定依赖版本的必要性,可参考 Should you Pin your JavaScript Dependencies?)
4. 禁用更新
对于一些非核心(并且更新过于频繁的包,比如图标库 lucide-react 对图标的更新),则可以基于 docs.renovatebot.com#enabled,声明禁止查询该类包的版本更新,比如:
{
"packageRules": [
{
"matchPackagePatterns": ["lucide-react"],
"enabled": false
}
]
}
配置分享
结合上面提到的几点:
如下配置是本博客项目的 renovate 配置文件 (后续会保持跟随项目下 renovate.json
配置文件的更新)
使用 renovate(bot) 维护升级依赖版本
https://blog.ninoh.cc/loc-blog/20_use-renovate-2-manage-deps[Copy]转载或引用本文时请遵守“署名-非商业性使用-相同方式共享 4.0 国际”许可协议,注明出处、不得用于商业用途!分发衍生作品时必须采用相同的许可协议。