Chainlink release-changelog 工具详解:为 CCIP 版本生成发布变更日志与风险审计清单
2026/9/16 14:32:18 网站建设 项目流程

Chainlink release-changelog 工具详解:为 CCIP 版本生成发布变更日志与风险审计清单

【免费下载链接】chainlinknode of the decentralized oracle network, bridging on and off-chain computation项目地址: https://gitcode.com/GitHub_Trending/ch/chainlink

本篇基于 Chainlink 仓库中的 tools/release/release-changelog/README.md 及其配套源码,讲解release-changelog这个发布过程工具:它如何在两个 git ref(SHA、版本 tag 或发布分支)之间,为 CCIP 相关依赖生成一份可用作风险审计工件的 Markdown 变更报告,并可投递到 Slack 线程。读完本文,你将掌握该工具的完整命令行用法、CCIP 镜像 tag 到 git tag 的映射原理、RepoConfig配置模型的每个字段含义,以及 go.mod diff、commit 变更日志、DRIFT/ROLLBACK 等风险标记的产生机制和源码位置。

工具定位:发布流程中的风险审计工件

release-changelog是一个专注 CCIP(跨链互操作协议)发布审计的命令行工具。发布工程师在对比"当前线上镜像由哪个版本构建"和"新版本由哪个版本构建"时,需要回答的核心问题不是简单的"改了什么",而是"依赖 pin 有没有漂移、有没有回滚、有没有高危提交"。

对每一对 ref,工具生成四类内容(见 README):

  1. go.mod diff——CCIP 相关模块的版本变化:chainlink-ccip及其chains/*子模块、chainlink-evm及其子模块、chainlink-aptos/codecchainlink-sui/codecchainlink-ton
  2. chainlink-ccip 的 commit 变更日志——基于两个 ref 中固定的 commit 之间的提交列表;
  3. 各链仓库的 commit 变更日志——chainlink-aptos、chainlink-sui、chainlink-solana、chainlink-ton、chainlink-evm,外加核心仓库中限定在core/capabilities/ccip/路径下的提交;
  4. 风险标记(Risk flags)——plugins/plugins.public.yaml中 plugin gitRef 变化、plugin 与 go.mod 之间的漂移(TON、EVM)、回滚/分叉、模块或插件的新增/移除,以及关键词高亮(breakingreverthotfixsecurityfix!config)。

入口是 cmd/release-changelog/main.go,它对 internal/engine 中的产品无关引擎做了一层薄封装,将 CCIP 产品定义(internal/products/ccip)注入engine.Generate

快速上手:命令行用法

基本命令

从仓库根目录执行(需要完整的 git history checkout):

go run ./tools/release/release-changelog/cmd/release-changelog \ --old v2.55.0 --new release/2.56.0 \ [--out report.md] [--slack-thread https://<ws>.slack.com/archives/<channel>/p<ts>]

main.go中实际注册的完整参数集为:

参数默认值说明
--old必填旧 ref:构建当前线上镜像的 SHA、tag、分支或镜像 tag
--new必填新 ref:新发布镜像对应的 ref
--repo.chainlink 核心仓库的本地 checkout 路径
--out空(stdout)将完整 Markdown 报告写入该文件(权限 0600);未指定且未指定 Slack 时打印到 stdout
--slack-thread可选的 Slack 线程 URL,投递摘要并上传完整报告

--old--new都接受任意 ref:SHA、tag(v2.55.0v2.56.1-rc.3)、分支(release/2.57.2)。

ref 解析顺序(源码:refs.go)

internal/engine/refs.go 中的ResolveRef实现了三级解析:

  1. 本地 refgit rev-parse --verify <ref>^{commit}
  2. 远程跟踪分支origin/<ref>(发布分支往往只存在于远端);
  3. 按需 fetchgit fetch origin <ref>,只写入FETCH_HEAD——不创建任何本地分支或 tag。

如果所有途径都失败,suggestRefs会调用git ls-remote按版本号核心(剥离refs/tags/refs/heads/release/v前缀后)匹配相似远端 ref,最多给出 8 条建议。例如只存在release/2.56.1v2.56.1-rc.N时请求v2.56.1,错误信息会提示 "Did you mean: release/2.56.1, v2.56.1-rc.3? ..."。

直接传入 CCIP 镜像 tag 或镜像 URI

这是该工具最有实战价值的设计之一。发布工程师日常接触的是镜像版本号,而不是 git tag,因此--old/--new可以直接接受:

--old 2.56.1-ccip-rc.2 --old v2.56.1-ccip-rc.2 # 混合形式(v 前缀 + -ccip-)也接受 --old public.ecr.aws/chainlink/ccip:2.56.1-ccip-rc.2

映射规则在 internal/products/ccip/ccip.go 中实现。build-publish.yml从构建用的 git tag 派生镜像 tag(v2.56.1-rc.2→ 镜像2.56.1-ccip-rc.2),因此这个映射是可逆的——工具在本地反推,从不拉取或检查镜像。两条正则分别处理标准镜像 tag 和常见的"误加 v 前缀"混合形式,并忽略 ECR 控制台里可能出现的-amd64/-arm64架构后缀:

var ccipImageTag = regexp.MustCompile(`^(\d+\.\d+\.\d+)-ccip-(.+?)(?:-(?:amd64|arm64))?$`) var ccipGitTagWithCCIP = regexp.MustCompile(`^v(\d+\.\d+\.\d+)-ccip-(.+?)(?:-(?:amd64|arm64))?$`)

normalizeRef还会剥掉完整镜像 URI 的registry/...:tag前缀(git refname 不含:,以此区分)。当 ref 被规范化过,报告头部的 ref 行会注明映射关系(image tag → git tag),作为审计留痕——见 report.go 中的refLine

环境变量与 CI

  • GITHUB_TOKEN/GH_TOKEN——GitHub compare API 的鉴权,未设置时回退到gh auth token。由于所有被跟踪仓库都是公开的,token 只影响速率限制,不是硬性前置条件。
  • SLACK_BOT_TOKEN——使用--slack-thread时必需,bot 必须是目标 channel 成员。摘要和 flags 以消息形式发在线程中,完整 Markdown 报告作为文件上传到同一线程。

从 main.go 的投递逻辑可以看出一个关键的可靠性设计:摘要消息是审计载荷,投递失败即整体失败;而文件上传失败被视为非致命——bot token 可能缺少files:writescope,此时工具会在 CI 环境中改发一条指向 GitHub Actions run 产物的回退消息(ActionsRunURL()GITHUB_SERVER_URL/GITHUB_REPOSITORY/GITHUB_RUN_ID拼出),不会让整次运行失败。

CI 侧使用ccip-release-changelogworkflow(workflow_dispatch手动触发,见 .github/workflows/ccip-release-changelog.yml),输入参数与 CLI 相同,使用SLACK_BOT_TOKEN_RELENGsecret 鉴权。

报告结构与风险标记机制

完整报告的渲染在 internal/engine/report.go 的RenderMarkdown中,固定四个章节:

  1. ## ⚠️ Flags——顶层审计 flags 列表;无风险时输出 "No risk flags for this range. ✅";
  2. ## go.mod changes (CCIP modules)——按仓库分组,逐模块展示版本过渡,如`chainlink-ccip`: `v0.1.1-solana.0.20260101...-aaaa` → `v0.1.1-solana.0.20260202...-cccc`,并区分_(added)_/_(removed)_/no change状态;
  3. ## plugins.public.yaml changes (CCIP plugins)——按 plugin key(aptossuisolanatonevm)展示 gitRef 过渡;
  4. ## Commit changelogs——每仓库一节,节标题包含状态摘要(2 commitsno changes⚠️ rolled back (see flags)⚠️ diverged history (see flags)N commits touching tracked paths (M total in range)),每条提交格式为Title (#PR) (sha12) by @author

仓库内有一个基于内联 fixture 的 golden 样例 testdata/report.golden.md,可以直观看到 flags 的完整形态,包括 keyword match、ROLLBACK、gitRef changed、DRIFT 各类标记的渲染效果。

flags 的产生逻辑(源码:analyze.go)

repoFlags函数(internal/engine/analyze.go)对每个仓库逐条计算:

Flag 类型触发条件
plugin ADDED/REMOVED/changed该 repo 的PluginKeys在两个 ref 的 plugins.public.yaml 中新增、移除或 gitRef 变化
go.mod module ADDED/REMOVED该 repo 的GoModules在根 go.mod 中新增或消失
DRIFT双源仓库(ton、evm):plugin 的moduleURI同时出现在 go.mod 中,但两个来源在同一 ref提取出的 SHA 不一致
ROLLBACK新 pin 严格落后于旧 pin(Status == "behind",由git merge-base --is-ancestor判定)
DIVERGED新旧 pin 无直接祖先关系
keyword match提交标题命中关键词正则,附 commit 链接

其中关键词正则在analyze.go中定义:

var keywordPattern = regexp.MustCompile(`(?i)\b(breaking|revert|hotfix|security|config)\b|fix!`)

SHA 提取由 deps.go 的VersionSHA完成:支持 40 位裸 SHA 和 Go pseudo-version 尾部 12 位 hex(如v0.1.1-solana.0.20260625091148-e5618f5682ee中的e5618f5682ee);干净的 release tag(如v1.3.0)提取不到 SHA 时,两个 ref 版本字符串相同则判定identical,否则报错记录。

依赖快照的加载路径是:LoadSnapshot→ 规范化 ref →ResolveRef得到 SHA →git show <sha>:go.modgit show <sha>:plugins/plugins.public.yaml读取该 ref 处的文件 →ParseGoMod(用golang.org/x/mod/modfile)与ParsePluginsYAML只提取被跟踪的模块/插件。注意它是按解析后的 SHA读文件,而非原始 ref——因为 ref 可能经由origin/<ref>回退解析、本地并不存在。

架构:产品无关引擎与产品定义分离

README 强调的核心设计是"引擎不认识 CCIP",具体三层结构:

1. internal/engine/ —— 通用引擎

包含 git ref 解析(refs.go)、go.mod/plugins.yaml 解析(deps.go)、compare API 变更日志(github.go)、路径过滤、风险标记(analyze.go)、报告渲染(report.go)、Slack 投递(slack.go)。引擎运行的Product由调用方传入,定义见 internal/engine/product.go:

type Product struct { DisplayName string // 报告标题与 Slack 消息中的产品名,如 "CCIP" Repos []RepoConfig // 被跟踪仓库列表 NormalizeRef func(string) string // 产品特有的 ref 形态(镜像 tag/URI)→ git ref;nil 表示原样视为 git ref }

2. internal/products/ccip/ —— CCIP 产品定义

当前唯一的 CCIP 产品定义在 ccip.go,包含 7 个被跟踪仓库的完整配置(chainlink-ccip、chainlink-aptos、chainlink-sui、chainlink-solana、chainlink-ton、chainlink-evm、核心仓库 chainlink)。其中几个值得注意的细节:

  • chainlink-solana只有PluginKeys: ["solana"]不在根 go.mod 中,其变更日志完全由 plugin gitRef 驱动;
  • chainlink-tonchainlink-evm双源仓库——同时有GoModulesPluginKeys,因此参与 DRIFT 检查;
  • chainlink-evm条目中有一条显式注释:contracts/cre/gobindings故意不跟踪(CRE 团队负责),README 把这条注释作为"停止跟踪某模块"的先例;
  • 核心仓库自身以Local: true标记,IncludePaths: []string{"core/capabilities/ccip/"},即核心仓库的变更日志只收录触及 CCIP capability 树的提交。

3. cmd/release-changelog/main.go —— 接线层

ccip.Product传给engine.Generate的那一行是"产品选择"的可见、可评审位置。README 给出的扩展路径是:为其他产品(如 Core releases)新建internal/products/<name>/包(拷贝ccip.go修改),再在 main 包中接线;当第二个产品出现后,可以加--productflag 或 workflow input 在运行时选择。

重要约束:没有任何 CLI flag 或 workflow input 控制跟踪范围——改跟踪什么只能编辑internal/products/ccip/ccip.go后重跑,这是刻意设计(配置即代码,可 code review)。

RepoConfig 字段表

每个Repos条目是一个engine.RepoConfig(定义见 repo_config.go),字段语义如下:

字段含义
Name/OwnerGitHub 仓库(Owner/Name),用于 compare API 调用及 commit/PR 链接
GoModulesgo.mod中来自该仓库的模块路径,按重要性降序排列;全部出现在go.mod changes章节并参与 divergence 说明;无 plugin 条目时第一项即"主 pin"
PluginKeysplugins/plugins.public.yaml中从该仓库安装的 key(如tonevm);非空时 plugin gitRef 为主 pin
IncludePaths非空时,只有触及至少一个这些路径前缀的提交才进入 commit 变更日志
ExcludePaths仅触及这些路径前缀的提交被丢弃(在IncludePaths之前应用)
Local从本地 git checkout 读取提交日志而非 compare API,用于核心仓库自身

主 pin 的选择与 DRIFT / divergence 说明

commit 变更日志对每个仓库只对比一对old/new SHA,选择规则(pinFor函数,deps.go):

  • PluginKeys非空:plugin gitRef 为主 pin——因为它是真正被构建进发布镜像的(aptos、sui、solana、ton、evm);
  • 否则:GoModules第一项为主 pin(chainlink-ccip)。

由此衍生三类观察(divergenceNotes,analyze.go):

  • 双源漂移(DRIFT flag):仓库同时有 plugin 条目且其moduleURI也在GoModules中(当前是 ton、evm),两个来源在同一 ref 必须指向同一 SHA,否则升级为顶层 flag;
  • divergence 说明(仅信息性):同一仓库的不同 pin 指向不同 commit(如chainlink-ccip主模块 vschainlink-ccip/chains/evm子模块,或plugin:suivschainlink-sui/codec),在该仓库小节渲染为引用块笔记,不是 flag——对混合 pin 仓库属正常现象;dedupeDivergenceNotes还会在两端分叉内容相同时合并成单条 "(both refs)" 笔记;
  • 只有主 pin 的 SHA 区间生成 commit 变更日志;子模块 bump 仍然会体现在 go.mod diff 章节。

对本地核心仓库,analyzeLocalgit merge-base --is-ancestor判定 ahead/behind;两个发布分支天然不在直接祖先关系上,此时仍标记ahead并附加说明笔记,git log old..new依然精确给出"new 中有而 old 中没有"的提交。路径过滤(pathMatch)先剔除ExcludePaths前缀的文件,再要求至少一个文件匹配IncludePaths

配置编辑示例

README 给出的三个典型编辑场景:

新增仓库(出现在所有章节;主 pin 优先取 plugin gitRef,其次第一个 GoModule):

{ Name: "chainlink-tron", Owner: "smartcontractkit", GoModules: []string{"github.com/smartcontractkit/chainlink-tron/relayer"}, },

调整核心仓库跟踪路径——修改Local条目的过滤器,例如把 CCIP 部署代码也纳入:

IncludePaths: []string{"core/capabilities/ccip/", "deployment/ccip/"},

停止跟踪某模块——从GoModules中删除即可(先例:chainlink-evm 条目中对contracts/cre/gobindings的注释)。

编辑后需要做什么

golden 测试从内联引擎 fixture(而非真实产品配置)渲染,因此纯配置编辑不需要重生成 golden;但引擎层对渲染或 flag 逻辑的任何修改都需要:

UPDATE_GOLDEN=1 go test ./tools/release/release-changelog/... go test ./tools/release/release-changelog/... golangci-lint run ./tools/release/release-changelog/...

最后用真实 ref 对做一次 sanity check,例如--old v2.55.0 --new release/2.56.0

引擎侧的其他可调项

以下"旋钮"不在产品配置里,全部位于 internal/engine/:

  • 关键词高亮正则(breaking|revert|hotfix|security|config|fix!):analyze.go中的keywordPattern
  • Slack 消息/Markdown 布局:report.go
  • compare API 行为(含 commit 列表截断检测):github.go
  • git ref 解析与建议:refs.go

测试与开发流程

go test ./tools/release/release-changelog/... # 单元测试 + golden 测试 UPDATE_GOLDEN=1 go test ./tools/release/release-changelog/... # 重新生成 golden

golden 文件有两份:report.golden.md(完整 Markdown 报告)和 slack-summary.golden.txt(Slack 摘要)。测试覆盖了 ref 解析(refs_test.go)、快照加载与 pin 选择(deps_test.go)、compare 客户端(github_test.go)、渲染与 flag 逻辑(report_test.goanalyze_test.gofixture_test.go)、Slack URL 解析与投递(slack_test.go),以及 CCIP 产品自身的 ref 规范化(ccip_test.go)。

小结与文件索引

release-changelog的价值在于把"发布风险审计"从人肉 diff 变成可重复、可留痕的流程:镜像 tag 可逆映射保证输入贴合发布工程师的日常词汇;主 pin 规则保证变更日志对比的正是被构建进镜像的 commit;DRIFT/ROLLBACK/divergence/关键词四类标记覆盖 pin 管理中最常见的事故形态;引擎与产品定义分离让同一套机制可以复用到其他产品线。

关注点位置
工具文档(本文主体依据)tools/release/release-changelog/README.md
CLI 入口与 Slack 投递tools/release/release-changelog/cmd/release-changelog/main.go
引擎门面(Generate/PostSummary/UploadReport)tools/release/release-changelog/internal/engine/facade.go
产品/仓库配置模型product.go、repo_config.go
ref 解析、本地 log、路径过滤refs.go、analyze.go
go.mod / plugins.yaml 快照deps.go
报告与 Slack 摘要渲染report.go
CCIP 产品定义与镜像 tag 规范化tools/release/release-changelog/internal/products/ccip/ccip.go
CI workflow.github/workflows/ccip-release-changelog.yml
golden 样例报告tools/release/release-changelog/internal/engine/testdata/report.golden.md

【免费下载链接】chainlinknode of the decentralized oracle network, bridging on and off-chain computation项目地址: https://gitcode.com/GitHub_Trending/ch/chainlink

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询