news 2026/9/13 11:06:44

Renovate 自定义数据源(Custom Datasource)完全指南:用 HTTP(S) 通用端点驱动任意依赖的版本升级

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Renovate 自定义数据源(Custom Datasource)完全指南:用 HTTP(S) 通用端点驱动任意依赖的版本升级

Renovate 自定义数据源(Custom Datasource)完全指南:用 HTTP(S) 通用端点驱动任意依赖的版本升级

【免费下载链接】renovateHome of the Renovate CLI: Cross-platform Dependency Automation by Mend.io项目地址: https://gitcode.com/GitHub_Trending/re/renovate

导读

本文聚焦 Renovate 的custom数据源(datasource),讲解如何通过customDatasources配置项,从任意 HTTP(S) 通用端点(或本地file://文件)获取版本数据,从而让 Renovate 为那些没有现成数据源支持的软件自动升级依赖。你将掌握defaultRegistryUrlTemplateformattransformTemplates三大核心参数的用法,五种响应格式(json/plain/yaml/toml/html)的转换规则,以及 K3s、Hashicorp、Grafana Dashboard、nginx 目录列表等真实场景的完整配置方案,最终能独立为任意软件编写自定义数据源。

一、认识 Custom Datasource:Renovate 的"万能数据源"

Renovate 内置了大量数据源(如 npm、maven、docker 等),但在真实项目中,总有一些依赖的版本信息来自"非标准"位置:某个自建 API、一个简单的目录列表页面、甚至一个纯文本文件。custom数据源就是为这类场景设计的通用方案。

在 lib/modules/datasource/custom/index.ts 中,CustomDatasource被定义为static readonly id = 'custom',同时设置了customRegistrySupport = true,意味着它允许在依赖提取时通过registryUrl覆盖默认的 registry 地址。它的工作模式非常简单:

  1. 从配置中取出该数据源的defaultRegistryUrlTemplateformattransformTemplates
  2. format选择对应的 fetcher(html/json/plain/toml/yaml)发起 HTTP 请求或读取本地文件;
  3. 依次执行每条transformTemplates中的 JSONata 表达式,前一条的输出作为后一条的输入;
  4. 用 Zod schema 校验最终结果,通过后返回标准的ReleaseResult结构。

整个流程在getReleases方法(index.ts)中清晰可见。配置解析逻辑则在 utils.ts 的getCustomConfig/massageCustomDatasourceConfig中:datasource: "custom.xxx"中的custom.前缀会被剥离,剩下部分作为customDatasources对象中的键名去查找对应配置。

二、核心配置项与模板变量

2.1 配置项速查表

customDatasources是一个"数据源名 -> 配置对象"的记录(record),其中每个数据源配置支持以下选项:

option默认值说明
defaultRegistryUrlTemplate""当查找新版本时未提供registryUrl时使用的 URL 模板
format"json"API 响应格式。可选值:htmljsonplaintomlyaml
transformTemplates[]用于转换 API 输出的 JSONata 规则 列表,每条规则依次执行,结果作为下一条的输入

从 lib/config/types.ts 的CustomDatasourceConfig定义可以确认,format的合法取值被限定为htmljsonplaintomlyaml五种,且三个字段均为可选。

2.2 模板变量

defaultRegistryUrlTemplatetransformTemplates中都可以使用 Handlebars 风格模板,可用变量为:

  • packageName—— 当前依赖的包名;
  • currentValue—— 当前依赖版本值。

模板编译发生在 utils.ts:template.compile(registryUrlTemplate, templateInput)会在每次查找版本时将{ packageName, currentValue }注入模板。这意味着同一个自定义数据源可以通过 URL 模板为不同包服务,例如 Hashicorp 示例中的https://api.releases.hashicorp.com/v1/releases/{{packageName}}?license_class=oss

提示:JSONata 表达式本身也可以用模板变量,例如transformTemplates: ['{{packageName}}'],用于从多层嵌套的 JSON 中取出与包名对应的节点(测试用例见 index.spec.ts)。建议使用 JSONata Exerciser 在线调试你的规则。

三、五种响应格式详解

format决定 fetcher 如何解析 HTTP 响应体。fetcher 的注册表位于 formats/index.ts,五种格式都实现了统一的fetch(http, registryURL)readFile(registryURL)接口(见 formats/types.ts),因此它们既可以请求远程 URL,也可以读取本地文件。

3.1 JSON(默认)

formatjson时,响应体被直接解析为 JSON 并交给转换规则处理(实现见 formats/json.ts)。最理想的情况是 API 本身就返回 Renovate 的ReleaseResult结构,此时无需任何转换即可直接使用(测试用例见 index.spec.ts)。

3.2 Plain(纯文本)

formatplain时,Renovate 会以Accept: text/plain头调用 HTTP 端点(实现见 formats/plain.ts),响应体被当作纯文本,按换行分割、每行 trim 后作为一条版本

1.0.0 2.0.0 3.0.0

会被转换为:

{ "releases": [ { "version": "1.0.0" }, { "version": "2.0.0" }, { "version": "3.0.0" } ] }

从源码看,convertLinesToVersions对每行执行line.trim(),因此带空白的行也能被正确解析(对应测试见 index.spec.ts)。转换完成后,transformTemplates中的 JSONata 规则会照常继续处理。

3.3 YAML

formatyaml时,响应体通过parseSingleYaml解析并转换为 JSON 供后续处理(实现见 formats/yaml.ts)。例如:

releases: - version: 1.0.0 - version: 2.0.0 - version: 3.0.0

会被转换为:

{ "releases": [ { "version": "1.0.0" }, { "version": "2.0.0" }, { "version": "3.0.0" } ] }

之后同样应用transformTemplates中的 JSONata 规则。

3.4 TOML

formattoml时,响应体按 TOML 解析(实现见 formats/toml.ts)。下面的 TOML 文档:

[[releases]] version = "1.0.0" [[releases]] version = "2.0.0" [[releases]] version = "3.0.0"

会被转换为:

{ "releases": [ { "version": "1.0.0" }, { "version": "2.0.0" }, { "version": "3.0.0" } ] }

随后应用transformTemplates中的 JSONata 规则。

3.5 HTML

formathtml时,Renovate 以Accept: text/html头调用端点,将响应体作为 HTML 文档解析,提取页面中所有<a>链接的href属性作为版本(实现见 formats/html.ts)。例如:

<html> <body> <a href="package-1.0.tar.gz">package-1.0.tar.gz</a> <a href="package-2.0.tar.gz">package-2.0.tar.gz</a> </body> </html>

会生成:

{ "releases": [ { "version": "package-1.0.tar.gz" }, { "version": "package-1.0.tar.gz" } ] }

注意:HtmlFetcherextractLinks会特殊处理<pre>块——先解析<pre>内部的文本,再提取其中的链接。这是为了兼容 nginx 等 Web 服务器把目录列表包裹在<pre>标签中的情况(源码注释与测试用例均验证了这一点,见 formats/html.ts 与 index.spec.ts)。测试还覆盖了畸形 HTML 与不完整 HTML(如<a>标签未闭合),解析器均能容错处理。

转换完成后,JSONata 规则照常应用。由于提取到的是完整文件名(如package-1.0.tar.gz)而非纯净的版本号,通常需要用extractVersion或 JSONata 规则进一步提取版本号。

四、结果数据结构:Renovate 期望的输出格式

无论采用哪种格式、经过多少层转换,最终结果都必须符合 Renovate 的ReleaseResult结构,由 schema.ts 中的ReleaseResultZod校验。

最小可用结构:

{ "releases": [ { "version": "v1.1.0" }, { "version": "v1.2.0" } ] }

全部可选字段:

{ "releases": [ { "version": "v1.0.0", "isDeprecated": true, "releaseTimestamp": "2022-12-24T18:21Z", "changelogUrl": "https://github.com/demo-org/demo/blob/main/CHANGELOG.md#v0710", "sourceUrl": "https://github.com/demo-org/demo", "sourceDirectory": "monorepo/folder", "digest": "c667f758f9e46e1d8111698e8d3a181c0b10f430", "isStable": true } ], "sourceUrl": "https://github.com/demo-org/demo", "sourceDirectory": "monorepo/folder", "changelogUrl": "https://github.com/demo-org/demo/blob/main/CHANGELOG.md", "homepage": "https://demo.org" }

各字段说明(依据 schema.ts 的 Zod 定义):

字段层级说明
versionreleases[] 必填版本号字符串,必填
isDeprecatedreleases[] 可选该版本是否已废弃
releaseTimestampreleases[] 可选发布时间戳,需符合MaybeTimestamp校验
changelogUrlreleases[] 可选该版本对应的变更日志 URL
sourceUrlreleases[] 可选源码仓库 URL
sourceDirectoryreleases[] 可选源码仓库内子目录(适用于 monorepo)
digestreleases[] 可选摘要值,会被重命名为newDigest供后续使用
isStablereleases[] 可选是否稳定版
tags顶层可选标签到版本的映射,如{"latest": "v1.0.0"}
sourceUrl/sourceDirectory/changelogUrl/homepage顶层可选整体仓库级元数据

值得注意的细节:schema 在解析时会执行transform,把digest字段重命名为newDigest(schema.ts),因此数据源可以直接在releases中提供digest来支持 digest 型更新;而CustomDatasource.getDigest方法默认返回null,正是为了"让 digest 由 getReleases 提供"(见 index.ts 及对应测试 index.spec.ts)。

另外,schema 解析成功后会执行structuredClone(parsed)返回结果;若校验失败,则记录 debug 日志与 trace 日志并返回null,不会抛出异常中断流程。

五、端到端示例:customDatasources + regex 自定义管理器

custom数据源通常与 regex 自定义管理器 配合使用:regex manager 负责从文件中提取依赖声明,custom datasource 负责查询版本。下面以更新k3s.version文件为例:

{ "customManagers": [ { "customType": "regex", "managerFilePatterns": ["/k3s.version/"], "matchStrings": ["(?<currentValue>\\S+)"], "depNameTemplate": "k3s", "versioningTemplate": "semver-coerced", "datasourceTemplate": "custom.k3s" } ], "customDatasources": { "k3s": { "defaultRegistryUrlTemplate": "https://update.k3s.io/v1-release/channels", "transformTemplates": [ "{\"releases\":[{\"version\": $$.(data[id = 'stable'].latest),\"sourceUrl\":\"https://github.com/k3s-io/k3s\",\"changelogUrl\":$join([\"https://github.com/k3s-io/k3s/releases/tag/\",data[id = 'stable'].latest])}],\"sourceUrl\": \"https://github.com/k3s-io/k3s\",\"homepage\": \"https://k3s.io/\"}" ] } } }

整个依赖查找链路为:regex manager 从k3s.version文件匹配出currentValuedatasourceTemplate: "custom.k3s"指定数据源 →getCustomConfig剥离custom.前缀找到k3s配置 → 以json格式请求 K3s 频道 API → JSONata 从返回数据中取出stable频道的latest版本,并拼接出changelogUrl→ 校验后返回releases数组。

六、真实场景实战案例

6.1 跟踪 K3s 最新稳定版

单独使用customDatasources即可查询 K3s 最新稳定版(与上节配合 regex manager 使用效果更佳):

{ "customDatasources": { "k3s": { "defaultRegistryUrlTemplate": "https://update.k3s.io/v1-release/channels", "transformTemplates": [ "{\"releases\":[{\"version\": $$.(data[id = 'stable'].latest),\"sourceUrl\":\"https://github.com/k3s-io/k3s\",\"changelogUrl\":$join([\"https://github.com/k3s-io/k3s/releases/tag/\",data[id = 'stable'].latest])}],\"sourceUrl\": \"https://github.com/k3s-io/k3s\",\"homepage\": \"https://k3s.io/\"}" ] } } }

6.2 追踪 Hashicorp 产品线(Nomad、Vault、Terraform 等)

Hashicorp 为所有产品提供了统一的 release API,配合{{packageName}}模板变量即可用一份配置覆盖所有产品:

{ "customManagers": [ { "customType": "regex", "managerFilePatterns": ["/\\.yml$/"], "datasourceTemplate": "custom.hashicorp", "matchStrings": [ "#\\s*renovate:\\s*(datasource=(?<datasource>.*?) )?depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s*\\w*:\\s*(?<currentValue>.*)\\s" ], "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}" } ], "customDatasources": { "hashicorp": { "defaultRegistryUrlTemplate": "https://api.releases.hashicorp.com/v1/releases/{{packageName}}?license_class=oss", "transformTemplates": [ "{ \"releases\": $map($, function($v) { { \"version\": $v.version, \"releaseTimestamp\": $v.timestamp_created, \"changelogUrl\": $v.url_changelog, \"sourceUrl\": $v.url_source_repository } }), \"homepage\": $[0].url_project_website, \"sourceUrl\": $[0].url_source_repository }" ] } } }

要让 Ansible 变量文件中的 Nomad 版本保持最新,在上述配置之外,只需在 YAML 中加入 renvoate 注释指令:

# renovate: depName=nomad nomad_version: 1.6.0

这里的{{packageName}}会被替换为nomad,从而请求 Nomad 的 release API。

6.3 升级 Grafana Helm chart 中的 Dashboard

Grafana Dashboard 的版本号存于 Helm chart 的values.yaml(形如gnetId+revision),可通过自定义数据源查询 Grafana API:

{ "customManagers": [ { "customType": "regex", "managerFilePatterns": ["/\\.yml$/"], "matchStrings": [ "#\\s+renovate:\\s+depName=\"(?<depName>.*)\"\\n\\s+gnetId:\\s+(?<packageName>.*?)\\n\\s+revision:\\s+(?<currentValue>.*)" ], "versioningTemplate": "regex:^(?<major>\\d+)$", "datasourceTemplate": "custom.grafana-dashboards" } ], "customDatasources": { "grafana-dashboards": { "defaultRegistryUrlTemplate": "https://grafana.com/api/dashboards/{{packageName}}", "format": "json", "transformTemplates": [ "{\"releases\":[{\"version\": $string(revision)}]}" ] } } }

Grafana Helm chartvalues.yaml中的对应片段:

dashboards: default: 1860-node-exporter-full: # renovate: depName="Node Exporter Full" gnetId: 1860 revision: 31 datasource: Prometheus 15760-kubernetes-views-pods: # renovate: depName="Kubernetes / Views / Pods" gnetId: 15760 revision: 20 datasource: Prometheus

这里的gnetId被提取为packageName用于构造请求 URL,revision被提取为currentValue,JSONata 将 API 返回的revision转换为字符串版本号。

6.4 无 API 时的替代方案:自定义离线依赖文件

有时依赖的版本来源根本没有可用 API。变通做法是自建"版本追踪文件"(dependency files),通过 HTTP(S) 暴露给 Renovate。例如为软件something准备versiontracker.json

[ { "version": "77" }, { "version": "76" } ]

再编写如下自定义数据源(示例以 Nexus 作为 Web 服务器):

{ "customDatasources": { "nexus_generic": { "defaultRegistryUrlTemplate": "https://nexus.example.com/repository/versiontrackers/{{packageName}}/versiontracker.json", "transformTemplates": [ "{ \"releases\": $map($, function($v) { { \"version\": $v.version, \"sourceUrl\": $v.filelink } }) }" ] } } }

配合自定义管理器即可更新 Ansible YAML 中的版本号:

# renovate: datasource=custom.nexus_generic depName=something versioning=loose something_version: '77'

对应的 regex 自定义管理器:

{ "customManagers": [ { "customType": "regex", "managerFilePatterns": ["/\\.yml$/"], "datasourceTemplate": "custom.nexus_generic", "matchStrings": [ "#\\s*renovate:\\s*(datasource=(?<datasource>.*?)\\s*)?depName=(?<depName>.*?)(\\s*versioning=(?<versioning>.*?))?\\s*\\w*:\\s*[\"']?(?<currentValue>.+?)[\"']?\\s" ], "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}" } ] }

如果不想搭建 HTTP 服务,也可以把版本追踪文件放在本地仓库中,用file://前缀指向相对路径:

{ "customDatasources": { "local_generic": { "defaultRegistryUrlTemplate": "file://dependencies/{{packageName}}/versiontracker.json", "transformTemplates": [ "{ \"releases\": $map($, function($v) { { \"version\": $v.version, \"sourceUrl\": $v.filelink } }) }" ] } } }

此时 Renovate 会从当前工作目录解析该文件。源码层面,isLocalRegistry的判断逻辑是defaultRegistryUrlTemplate.startsWith('file://'),命中后调用fetcher.readFile(...)读取本地文件(见 index.ts);五种格式均实现了readFile,因此 json/plain/yaml/toml/html 全部支持本地文件模式(对应测试见 index.spec.ts、index.spec.ts、index.spec.ts 与 index.spec.ts)。

6.5 解析 nginx 目录列表

当只有"一个装满文件的目录 + 能生成目录列表的 HTTP 服务器"时,html格式可以派上用场。以下配置跟踪 nginx 官方下载目录:

{ "customDatasources": { "nginx": { "defaultRegistryUrlTemplate": "https://nginx.org/download", "format": "html" } }, "packageRules": [ { "matchDatasources": ["custom.nginx"], "extractVersion": "^nginx-(?<version>.+)\\.tar\\.gz$" } ] }

由于html格式提取到的是完整文件名,必须用extractVersion正则从中提取纯版本号。

6.6 解析普通 HTML "Downloads" 页面

同理,html格式也可以用于典型的下载页面,例如 curl 官网:

{ "customDatasources": { "curl": { "defaultRegistryUrlTemplate": "https://curl.se/download.html", "format": "html" } }, "packageRules": [ { "matchDatasources": ["custom.curl"], "extractVersion": "/curl-(?<version>.+)\\.tar\\.gz$" } ] }

注意extractVersion既支持字符串正则,也支持/.../包裹的正则字面量写法。

七、故障排查与日志调试

7.1 内置的追踪日志

Renovate 在转换之前会写入 trace 级日志(Custom datasource API fetcher '${format}' received data. Starting transformation.),如果发现意外的数据格式,还会在转换之后额外写一条 trace 日志。这意味着LOG_LEVEL=trace时,你可以在日志中同时看到转换前后的原始数据与结果数据(见 index.ts 与 index.ts)。

调试经验:

  • JSONata 表达式编译失败时,会以logger.once.warn输出Invalid JSONata expression(如$[.name = "Alice" and这类语法错误,测试见 index.spec.ts);
  • JSONata 表达式执行抛错时,会输出Error while evaluating JSONata expression(测试见 index.spec.ts);
  • 结果未通过 schema 校验时,会输出 debug 级Response has failed validation与 trace 级的数据快照,然后返回null
  • 未找到对应自定义数据源、未提供 registry URL、HTTP 请求失败等场景均返回null并伴随对应日志(测试覆盖见 index.spec.ts)。

7.2 在 Mend 托管 App 上获取 trace 日志

如果使用 Mend Renovate 托管应用,可通过logLevelRemap配置项把特定消息提升到 info 级别输出:

{ "logLevelRemap": [ { "matchMessage": "/^Custom datasource/", "newLogLevel": "info" } ] }

7.3 自托管时获取 trace 日志

自托管 Renovate 时,按以下步骤开启 trace 日志:

  1. 设置环境变量LOG_FILE_LEVELtrace
  2. dryRun模式运行 Renovate,避免实际创建 PR,安全观察数据源返回的版本数据。

八、最佳实践小结

  1. 优先复用内置数据源custom数据源适用于内置数据源无法覆盖的"通用 HTTP(S) 端点"场景,不要用它重复造轮子。
  2. 模板变量让一份配置服务多个包:善用{{packageName}}{{currentValue}}构造 URL 和转换规则,如 Hashicorp、Grafana 示例。
  3. JSONata 转换保持"链式、幂等":多条transformTemplates依次执行、层层递进,建议先在 JSONata Exerciser 中验证表达式再写入配置。
  4. 让 API 直接输出 Renovate 格式:若 API 归你控制,直接返回{"releases": [...]}结构可省去全部转换逻辑(测试验证了"直接暴露 Renovate 格式"的场景,见 index.spec.ts)。
  5. 结合extractVersion清洗版本html/plain等格式提取到的常是文件名或整行文本,用extractVersion或 JSONata 提取纯净版本号。
  6. 注意 schema 校验的严格性versionreleases中唯一必填字段,其余均为可选;非法数据不会导致任务失败,而是静默返回null并记录日志,调试时务必开启 trace 级日志。

九、延伸阅读

  • Custom Datasource 源码 与 配置解析
  • 结果结构校验 schema 与 五种格式 fetcher
  • 单元测试(含全部格式与异常路径)
  • regex 自定义管理器
  • extractVersion配置说明
  • logLevelRemap配置说明
  • 自托管配置中的dryRun

【免费下载链接】renovateHome of the Renovate CLI: Cross-platform Dependency Automation by Mend.io项目地址: https://gitcode.com/GitHub_Trending/re/renovate

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 11:06:41

论文降重与文本改写:如何避开不靠谱服务的坑

一、为什么降重服务频频翻车&#xff1f; 每到毕业季&#xff0c;论文降重和文本改写就成了很多同学绕不开的环节。为了赶进度&#xff0c;不少人会选择付费服务来帮忙处理&#xff0c;但结果往往不尽如人意——要么改完的句子读不通&#xff0c;要么核心术语被改得面目全非&a…

作者头像 李华
网站建设 2026/9/13 11:06:19

STM32步进电机速度闭环实战:定时器脉冲、编码器反馈与PID调参

简介&#xff1a;面向基于STM32的步进电机控制开发者&#xff0c;资源以Emm V4.2驱动器为对象&#xff0c;完整演示了步进闭环控制与速度控制的实现方法&#xff0c;适合正在调试电机定位精度、动态响应或负载波动问题的嵌入式工程师。压缩包总大小约6.88MB&#xff0c;共163个…

作者头像 李华
网站建设 2026/9/13 11:05:58

Mellanox Onyx交换机实战:从CLI登录到常用配置与排障

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 11:01:47

Java Service层设计:从贫血模型到充血模型的重构实践

1. Service层的本质与常见误区 在Java企业级开发中&#xff0c;Service层作为业务逻辑的核心载体&#xff0c;其设计质量直接影响系统的可维护性和扩展性。但许多开发者对Service层的理解仍停留在"数据库操作代理"层面&#xff0c;导致出现典型的贫血模型问题。我曾参…

作者头像 李华
网站建设 2026/9/13 10:59:11

LangChain4j提示词工程:Java AI应用开发实战

1. LangChain4j提示词工程实战概述在Java生态中构建AI应用时&#xff0c;LangChain4j正迅速成为开发者的首选工具包。最新发布的0.35.0版本带来了更强大的提示词工程支持&#xff0c;让开发者能够更精细地控制AI模型的输出。提示词工程(Prompt Engineering)作为连接人类意图与A…

作者头像 李华