
Renovate 的 Changelog 解析原理从 AngularJS 发布说明看 release-notes 模块的切片与抽取实现【免费下载链接】renovateHome of the Renovate CLI: Cross-platform Dependency Automation by Mend.io项目地址: https://gitcode.com/GitHub_Trending/re/renovateRenovate 在升级依赖时会在 Pull Request 正文中自动附带目标版本的变更日志changelog / release notes而支撑这一能力的是lib/workers/repository/update/pr/changelog模块。本文以该模块测试夹具 angular-js.md 为标本逐层拆解 Renovate 如何定位 CHANGELOG 文件、如何把多版本混排的长文档切分为独立版本段落、如何精确抽取单个版本的内容并生成可点击的锚点链接同时给出对应的源码与测试证据帮助读者理解、调试乃至为 Renovate 贡献 changelog 相关代码。angular-js.md一份真实的“多版本混排”Changelog 标本angular-js.md 是 AngularJSangular/angular.js仓库真实 CHANGELOG.md 的镜像全文约 1.5 万行覆盖 1.6.0 至 1.6.9 等多个版本。其结构非常典型每个版本段以a name版本号/a锚点标签开头紧随其后是一个# 版本号 代号 (yyyy-mm-dd)级别标题例如# 1.6.9 fiery-basilisk (2018-02-02)版本段内部按## Bug Fixes、## New Features、## Performance Improvements、## Security Related、## Breaking Changes等小节组织每条变更后面跟随提交短哈希与 GitHub 链接例如([5dc076](https://github.com/angular/angular.js/commit/...))以及被修复的 issue 编号[#16389](https://github.com/angular/angular.js/issues/16389)部分小节尤其Breaking Changes还包含大段的迁移指引代码示例与前后对比。这种“标题级别多、版本区间大、既有锚点又有链接引用”的文件恰好覆盖了 Renovate 解析器需要处理的主要复杂度因此被选为getReleaseNotesMd解析逻辑的回归测试样本。在 release-notes.spec.ts 中测试parses angular.js会模拟 GitHub API 返回该文件并断言抽取出的 body 以#### Bug Fixes开头、包含#### New Features、以第 15105 号 issue 链接结尾且相邻的 1.6.8 段落beneficial-tincture绝不泄漏进来——这是对切片边界正确性的核心验证。变更日志从哪里来CHANGELOG 文件与 Release 列表双通道在深入解析算法之前先看 Renovate 获取 changelog 的两条通道二者都在 release-notes.ts 中组织文件通道getReleaseNotesMdFileInner()按平台分发到bitbucket/bitbucket-server/forgejo/gitea/github/gitlab各自的实现去仓库里定位 changelog 文件并读取内容。以 GitHub 为例github/index.ts 的实现是先请求GET /repos/{repository}拿到默认分支再请求GET /repos/{repository}/git/trees/{branch}?recursive1获取文件树用changelog-filename-regex过滤出候选文件按compareChangelogFilePath排序后取第一个common.ts 中CHANGELOG.md优先级最高最后请求GET /repos/{repository}/git/blobs/{sha}并 Base64 解码正文。Release 通道getReleaseList()对 GitHub 使用 GraphQL 的queryReleases拉取 Releases API 列表对 GitLab/Gitea/Forgejo 等走各自的 REST 接口作为文件通道缺失时的兜底数据源。在addReleaseNotes()中每个目标版本按“先文件、后 Release”的顺序查找getReleaseNotesMd(...)命中则直接用文件解析结果未命中再退回getReleaseNotes(...)匹配 Release 列表。如果两者都没有则至少保留版本间的 compare URL 作为兜底展示见 release-notes.ts。值得一提的是shouldSkipChangelogMd()对facebook/react-native与react/react-native这类 changelog 文件巨大的仓库Renovate 会跳过文件通道强制走 Release 通道避免拉取超大 Markdownrelease-notes.ts。核心算法一sectionize 如何按标题层级切分长文档拿到整份 CHANGELOG.md 后解析的关键在于sectionize()release-notes.ts。它的输入是text和一个标题层级level输出该层级下的所有段落先用markdown.parse基于markdown-it且只启用heading、lheading、fence三个特性见 release-notes.ts把全文解析为 token 流遍历 token凡是heading_open且标题级别 level的位置都记为一个“边界”在边界序列末尾补上[-1, lines.length]作为文档结尾随后两两配对把[start, end)行区间切成独立段落。也就是说sectionize只提取标题级别恰好等于level的段落且段落以所有更低/同级标题为界自然截断——这正是测试中“1.6.8 段落不得泄漏进 1.6.9”能成立的根本原因一旦找到 1.6.9 的# 1.6.9一级标题边界其后的内容在遇到下一个一级标题# 1.6.8时立即截止。外层驱动是getReleaseNotesMd()中从 1 到 7 的逐级尝试release-notes.ts先用level1切分若得到至少 2 个段落changelogParsed.length 2就在这些段落里匹配版本都不匹配则升到level2重试依此类推。这种自适应的做法使同一套逻辑既能处理 AngularJS 这种一级标题分版本的文件也能处理 Keep-a-Changelog 风格## [1.30.1] - 2026-04-17二级标题分版本的文件。核心算法二在段落内精确匹配目标版本每个被切出的段落都要经过“标题匹配”与“正文匹配”两轮验证标题匹配把段落首行去括号replace(/[[\]()]/g, )、去掉#前缀后按空格拆词只要某个词includes(version)且不是 URL即命中release-notes.ts。例如目标版本1.6.9的段落标题1.6.9 fiery-basilisk (2018-02-02)拆词后1.6.9直接命中。正文匹配monorepo 场景当标题里找不到版本号、但段落标题包含(yyyy-mm-dd)日期时进一步扫描正文每一行——只要某一行同时包含packageName与version、不是 URL 且不是 Markdown 链接引用定义[1.2.3]: https://…/compare/...就判定命中release-notes.ts。链接引用定义的排除很关键Keep-a-Changelog 文件底部通常有一堆[1.2.3]: url形式的引用定义若不排除任何版本都会“命中”而导致误匹配。命中后正文通过linkifyBody()处理先经massageBody()清洗再调用linkify()把#123这类裸编号转成指向该仓库 issues 的真实链接。massageBody抽取正文之前的标准化清洗无论是文件通道还是 Release 通道取到的正文都要经过massageBody()release-notes.ts清洗规则包括统一换行符\r\n→\n去掉 semantic-release 风格的a name.../a锚点行AngularJS 文件的每一段都以这种锚点开头正属于此场景删掉## x.x.x这类重复的版本比较标题行删掉孤立成行的 compare 链接标题降级把正文中的#/##/####分别降为###/####/#####但用 code block 感知避免破坏代码块内的#注释——AngularJS 文件里 Breaking Changes 段落的 JS 迁移示例因此得以原样保留最终 trim 首尾空白。正是这步把## Bug Fixes变成了#### Bug Fixes所以测试中断言抽取出的 body 以#### Bug Fixes开头。Release 通道的正文还会额外经过massageName()当 Release 名称与版本号重复时如Release v1.0.0或直接就是1.0.1把版本前缀剥掉避免 PR 里出现冗余标题。锚点链接生成从 Markdown 标题到 URL fragment解析命中后Renovate 还需要生成“指向 changelog 文件里该版本段落”的链接由getReleaseNotesMdAnchorUrl()完成source.ts去掉标题中的方括号与圆括号替换为空格去掉开头的#与空白按空格拆词、过滤掉 URL 词用-连接并剔除所有非[A-Za-z0-9-]字符。以 1.6.9 段落为例标题# 1.6.9 fiery-basilisk (2018-02-02)最终变成169-fiery-basilisk-2018-02-02拼接在notesSourceUrl之后。测试 release-notes.spec.ts 证实了三种平台下生成结果的差异GitHubhttps://github.com/angular/angular.js/blob/HEAD/CHANGELOG.md#169-fiery-basilisk-2018-02-02Bitbuckethttps://bitbucket.org/some-org/some-repo/src/HEAD/CHANGELOG.md#169-fiery-basilisk-2018-02-02Bitbucket Serverhttps://bitbucket.domain.org/projects/some-org/repos/some-repo/browse/CHANGELOG.md?atHEAD#169-fiery-basilisk-2018-02-02。同一个夹具、同一套解析算法输出随平台不同而不同这正是 ChangeLogSource 抽象类设计的意义平台差异API 基地址、URL 规范被封装在各平台的source子类中核心解析逻辑保持平台无关。版本区间与 PR 正文预算解析之外的工程细节真正进入getReleaseNotesMd()的版本不是随意的而是由 source.ts 的getChangeLogJSON()预先筛选取当前版本currentVersion到新版本newVersion开区间内的所有合法版本versioningApi.isVersion过滤、排序、按equals去重并为每个版本计算前后两个 git ref拼出baseUrl/repository/compare/prevHead...nextHead形式的比较链接GitHub 平台实现见 github/source.ts。另外当fetchChangeLogs配置为pr时addReleaseNotes()会受平台 PR 正文长度上限platform.maxBodyLength()约束按新版本优先的顺序累计已抓取正文长度一旦达到上限就停止抓取更旧的版本release-notes.ts。对应测试覆盖了“超限后仅回退 compare URL”“无 compare URL 时直接跳过”等边界行为。而 Release 通道的查询结果本身也会按“发布距今时间”差异化缓存一周内 55 分钟、半年内约 1 天、更久约 10 天releaseNotesCacheMinutes见 release-notes.ts以平衡时效性与请求开销。源码地图快速定位 changelog 相关实现关注点位置changelog 总入口getChangeLogJSON、版本区间筛选、compare URLsource.ts文件/Release 双通道、sectionize、massageBody、版本匹配release-notes.ts解析逻辑的完整测试含parses angular.jsrelease-notes.spec.tsGitHub 侧 CHANGELOG 文件定位tree/blob APIgithub/index.ts平台差异GitHub token 校验、compare URL 格式github/source.tschangelog 文件名优先级与 URL slug 规则common.ts各平台 changelog 实现bitbucket、gitlab 等changelog/bitbucket、changelog/gitlab 等子目录解析测试夹具angular-js、jest、js-yaml、yargs 等changelog/fixtures小结通过 AngularJS 这份 1.5 万行的真实 changelog可以看到 Renovate 的 release-notes 模块具备三条核心能力多级标题自适应切分sectionize从 level 1 到 7 逐级尝试、标题与正文双通道精确匹配普通包看标题monorepo 包看带日期的正文行并排除链接引用定义、平台感知的锚点与清洗massageBody降级标题、getReleaseNotesMdAnchorUrl生成 URL fragment。任何仓库只要遵循“版本号出现在标题或带日期段落的正文中”这一惯例都能被这套算法稳定识别而 angular-js.md 正是保证这一能力在历次重构中不回归的守门员。【免费下载链接】renovateHome of the Renovate CLI: Cross-platform Dependency Automation by Mend.io项目地址: https://gitcode.com/GitHub_Trending/re/renovate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考