Renovate renovate-config 管理器详解:自动更新 Preset 版本与工具约束
【免费下载链接】renovateHome of the Renovate CLI: Cross-platform Dependency Automation by Mend.io项目地址: https://gitcode.com/GitHub_Trending/re/renovate
Renovate 的renovate-config管理器是少数把“Renovate 自己的配置文件”当成依赖来管理的模块:它负责解析renovate.json等配置文件中的extendsPreset 引用与constraints工具版本约束,把已固定版本的 Preset 仓库 Tag 和 Containerbase 支持的工具版本提取为可更新的依赖。读完本文,你将理解该管理器扫描哪些文件、哪些 Preset 来源受支持、为什么未固定版本的 Preset 不会被自动固定,以及constraints中的工具版本是如何从配置项一路解析到 Containerbase 安装命令的。
管理器定位:它扫描哪些文件、支持哪些数据源
renovate-config管理器是 管理器目录 中注册的一个特殊 Manager:其他管理器面向package.json、go.mod等依赖清单,而它面向的是Renovate 自身的配置文件。
从 index.ts 的源码结构看,它有两个关键定义:
- 扫描文件范围:
defaultConfig.managerFilePatterns取自getConfigFileNames()(定义于 app-strings.ts),并显式过滤掉package.json——这正对应官方文档中“package.jsonfile config 已弃用”的说明。 - 支持的数据源:
supportedDatasources由三部分去重合并而成——github-tags、gitlab-tags、gitea-tags(用于 Preset 仓库的版本发现),以及 containerbase.ts 中allToolConfig里每个工具声明的 datasource(用于工具约束的版本发现,如github-releases、npm、pypi等)。
管理器入口为extractPackageFile(content, packageFile)(extract.ts),输入是配置文件内容,输出是PackageFileContent(即一组PackageDependency)。若文件无法解析(如空文件、非 JSON 对象)则返回null;若解析出的依赖数组为空(例如文件里既没有可提取的 Preset 也没有constraints),同样返回null。测试用例(extract.spec.ts)验证了这两点:extractPackageFile('this-is-not-json-object', 'renovate.json')返回null,仅含"draftPR": true的文件也返回null且不产生任何日志。
配置文件的结构由 schema.ts 中的 Zod 模式约束:
extends:可选的字符串数组;constraints:可选的“键-字符串”记录;packageRules[].constraints:同样只接受字符串值。
值得注意的是解析入口使用的是Json5封装(来自 schema-utils),因此renovate.json5中允许注释和尾随逗号,这在 测试用例 “supports JSON5” 中有明确验证。
Preset 更新规则:只更新已固定版本的引用
官方行为说明
根据管理器 README(readme.md)与 Shareable Config Presets 文档,核心规则是:
Preset 的版本仅在已经固定版本号时才会被更新。例如
github>user/renovate-config#1.2.3会在1.2.4可用时更新为github>user/renovate-config#1.2.4,但github>user/renovate-config(未带#tag)不会被自动固定。
典型配置如下(继承自官方文档的完整示例):
{ "extends": [ "github>user/renovate-config#1.2.3", "github>user/renovate-config:group" ] }第一条引用了user/renovate-config仓库并固定到 Tag1.2.3,Renovate 会为其创建 Preset 仓库的 Tag 更新;第二条引用了预设名group但没有#tag,Renovate 不会为它生成任何更新。
源码印证:Preset 的解析与跳过策略
extractPackageFile对extends的逐项处理逻辑在 extract.ts,可归纳为一条决策链:
模板化 Preset 直接跳过:包含
{{的条目(如local>{{ env.PRESET_REPO }}:python-312)只在运行时可解析,静态提取阶段直接continue,不产生依赖、不产生日志(对应测试 “ignores templated presets”,extract.spec.ts)。解析 Preset 语法:调用 parsePreset。该函数先识别来源前缀——
github>、gitlab>、gitea>、forgejo>、local>、npm>、相对路径(./、../、/开头)、HTTP URL,其余情况按 npm 命名空间(renovate-config-*)或内部预设(config:、schedule:等前缀,以及:name简写)归类。随后按仓库正则拆出repo、presetName与tag。解析失败(PRESET_INVALID)的条目被记录为skipReason: 'invalid-value',例如./foo#1.2.3(相对引用不允许带#tag,参见 parse.ts 的校验)。来源映射到数据源:supportedPresetSources 仅登记了三种来源:
Preset 来源前缀 映射的 Datasource github>github-tagsgitlab>gitlab-tagsgitea>gitea-tags不在该映射中的来源(
local>、npm>、HTTP URL、相对路径等)会以skipReason: 'unsupported-datasource'记录后跳过;内部预设(presetSource === 'internal')则静默忽略,因为内置 Preset 随 Renovate 版本演进,无需单独更新。从源码结构看,parsePreset虽然也识别forgejo>前缀,但当前supportedPresetSources并未登记 forgejo 对应的 Tags 数据源,因此forgejo>xxx形式的 Preset 在本管理器中会被归入unsupported-datasource处理。未固定版本则跳过:若
parsedPreset.tag为空(如github>abc/foo、gitlab>abc/bar:xyz),依赖以skipReason: 'unspecified-version'记录——这正是 README 中“不会为未固定版本的 Preset 创建固定”的源码实现,对应测试 extract.spec.ts。已固定版本则提取为依赖:生成
{ depName: repo, datasource, currentValue: tag },后续由对应的 tags 数据源发现新 Tag 并发起更新。
github>abc/bar:xyz#1.2.3、github>cde/foo//path/xyz#1.2.3、github>cde/bar:xyz/sub#1.2.3等“预设名 / 子目录 / 嵌套预设名”写法均能被正确解析出仓库名与 Tag,extract.spec.ts 分别对 GitHub、GitLab、Gitea 三种来源给出了完整断言。
不受支持的 Preset 形式
官方文档列出的 Unsupported Config(readme.md)与源码行为一一对应:
| 不受支持的形式 | 说明 | 源码中的处理结果 |
|---|---|---|
| Local Presets | local>path引用本地文件 | skipReason: 'unsupported-datasource' |
| HTTP URL Presets | 从任意 HTTP 服务器拉取 | skipReason: 'unsupported-datasource' |
package.json中的 Renovate 配置 | 已弃用;managerFilePatterns显式排除package.json | 文件本身不被本管理器扫描 |
| npm 托管 Presets | 已弃用 | skipReason: 'unsupported-datasource' |
子对象内的extends(如packageRules里) | 无法静态定位版本 | schema 不提取、无版本可固定 |
相对路径 Preset(./x、../x、/x) | 没有独立仓库可跟踪 | skipReason: 'unsupported-datasource',测试见 extract.spec.ts |
需要强调:skipReason条目只是让诊断信息更完整(便于在日志/仪表盘中看到“为何某 Preset 不被更新”),并不会触发任何更新动作。
Tool constraints:把工具版本也当成依赖
官方行为说明
README 的另一半内容是Tool constraints:Renovate 会提取并建议更新constraints配置中的包管理器/工具版本。“对 Containerbase 支持的工具设置的约束会触发 Renovate 更新;其他约束则不会。”
一个综合示例(取自 测试用例,可直接作为实战参考):
{ "extends": [ "github>abc/foo#1.2.3" ], "constraints": { "bazelisk": ">= 1.2.3", "maven": "< 4.0.0" } }constraints支持精确版本("golang": "1.20.5")与范围版本(">= 1.2.3"、"< 4.0.0"),并且可以下沉到packageRules做按文件分组约束:
{ "constraints": { "golang": "1.20.5" }, "packageRules": [ { "matchFileNames": ["go.mod"], "constraints": { "golang": "1.26.0", "gomodMod": "1.2.0" } } ] }源码印证:工具名的识别与依赖生成
约束的提取逻辑在 extract.ts:对constraints(以及每条packageRules[].constraints)的每个键值对,先用isToolName判断键是否为受支持工具名:
- 是工具名:从 allToolConfig 取出该工具的
datasource/packageName/versioning(extractVersion可选),生成依赖时附带depType: 'tool-constraint'与commitMessageTopic: '{{{depName}}} tool constraint',从而让 PR 标题使用“xxx tool constraint”这一措辞,与依赖更新区分开。例如测试断言了"bazelisk": "1.2.3"会解析为datasource: 'github-releases'、packageName: 'bazelbuild/bazelisk'、versioning: 'semver'(extract.spec.ts)。 - 不是工具名(如
gomodMod、go):以skipReason: 'unsupported'与depType: 'constraint'记录,不会产生更新——这正对应 README 的“Other constraints will not be updated by Renovate”。
受支持的工具名清单是编译期常量toolDefinitions(types.ts),当前包含:apm、bazelisk、bun、bundler、cocoapods、composer、conan、copier、corepack、deno、devbox、dotnet、erlang、elixir、flux、gh、gleam、golang、gradle、hashin、helm、helmfile、java、java-maven、jb、kustomize、maven、mise、nix、node、npm、pdm、php、pip-tools、pipenv、pnpm、pixi、poetry、python、ruby、rust、uv、yarn、yarn-slim、dart、flutter、vendir。
每个工具在allToolConfig中声明了版本发现渠道,可举几例:
| 工具 | datasource | packageName | versioning |
|---|---|---|---|
bazelisk | github-releases | bazelbuild/bazelisk | semver |
maven | github-releases | containerbase/maven-prebuild | maven |
golang | github-releases | containerbase/golang-prebuild | npm |
poetry | pypi | poetry | pep440 |
node | github-releases | containerbase/node-prebuild | node |
除工具约束外,types.ts还定义了additionalConstraintDefinitions(types.ts):如ghActionsLock(github-actions 管理器的锁文件扩展)、gomodMod(gomod 管理器的marwan-at-work/mod版本)、jenkins、platform、rubygems、vscode、dotnet-sdk、perl、%goMod等。这些约束供各自管理器内部消费,不属于renovate-config管理器会发起更新的工具,在提取阶段统一标记unsupported。
从配置到安装命令:约束的下游消费
提取出的工具约束最终服务于“动态安装”:运行时通过 resolveConstraint 把约束(精确或范围)解析成具体版本——先用对应 versioning 校验约束合法性,再用数据源返回的 releases 中匹配约束的最新稳定版本兜底,逐级降级(稳定匹配 → 不稳定匹配 → 最新稳定 → 最高版本并告警);随后 generateInstallCommands 生成install-tool <name> <version>命令交给 Containerbase 执行。
需要说明适用前提:isDynamicInstall 表明动态安装仅在binarySource: 'install'且运行于 Containerbase 环境(检测到CONTAINERBASE环境变量)时生效,否则回退到binarySource=global。换句话说,constraints的版本更新 PR 在任何部署形态下都会产生,但“解析约束→安装指定版本”的完整链路主要在有 Containerbase 参与的场景中发挥作用。
端到端验证:一次提取的完整决策过程
以 extract.spec.ts 的综合用例 为例,extractPackageFile对如下输入:
{ "extends": [ "github>abc/foo#1.2.3", "github>abc/bar:xyz#1.2.3", "github>cde/foo//path/xyz#1.2.3", "github>cde/bar:xyz/sub#1.2.3" ], "constraints": { "bazelisk": ">= 1.2.3", "maven": "< 4.0.0" } }会产出 6 条依赖:4 条github-tags依赖(depName分别为abc/foo、abc/bar、cde/foo、cde/bar,currentValue均为1.2.3,注意预设名与子目录段都被剥离)和 2 条tool-constraint依赖(bazelisk→bazelbuild/bazelisk、maven→containerbase/maven-prebuild)。若文件中只有draftPR: true之类无关字段,则返回null,整个管理器对该文件“无感”。
实践要点小结
- 想让 Preset 版本被 Renovate 维护:必须使用
github>/gitlab>/gitea>前缀且带#tag固定版本;local>、HTTP URL、npm 托管与相对路径 Preset 一律不会更新。 - 模板化 Preset(含
{{ ... }})在静态提取阶段被静默跳过,属预期行为。 constraints只更新工具约束:键名必须在toolDefinitions清单内才会生成tool-constraint依赖;gomodMod、go等附加约束属于各管理器内部机制,不会被本管理器更新。- 诊断信息可查证:各类
skipReason(invalid-value、unsupported-datasource、unspecified-version、unsupported)均可在 skip-reason 类型定义 中找到,配合日志即可定位“某条 Preset/约束为何没有更新 PR”。 - JSON5 友好:
renovate.json5支持注释与尾随逗号,与标准 JSON 语义一致。
核心源码入口一览:管理器注册 index.ts、提取逻辑 extract.ts、配置模式 schema.ts、Preset 语法解析 parse.ts、工具清单与解析 containerbase.ts 与 types.ts、行为测试 extract.spec.ts。
【免费下载链接】renovateHome of the Renovate CLI: Cross-platform Dependency Automation by Mend.io项目地址: https://gitcode.com/GitHub_Trending/re/renovate
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考