OpenZeppelin Contracts 的 Changeset 工作流:为 PR 编写符合规范的变更记录
2026/9/11 23:41:39 网站建设 项目流程

OpenZeppelin Contracts 的 Changeset 工作流:为 PR 编写符合规范的变更记录

【免费下载链接】openzeppelin-contractsOpenZeppelin Contracts is a library for secure smart contract development.项目地址: https://gitcode.com/GitHub_Trending/op/openzeppelin-contracts

本篇技术指南讲解 OpenZeppelin Contracts 仓库中为 Pull Request(PR)添加变更记录(changeset)的完整技能(SKILL)规范:什么情况下必须添加、什么情况下可以跳过、如何通过npx changeset add生成条目、格式规则有哪些,以及 changeset 如何在发布流程中驱动版本号与CHANGELOG.md的生成。读完本文,你将能够为任何改动用户可见合约行为的 PR 写出规范、可被发布机器人识别的 changeset 条目。

什么是 changeset,为什么需要它

在 OpenZeppelin Contracts 这样的大型智能合约库中,每次用户可见的改动(新增函数、修复 bug、调整错误信息)都需要被精确记录,以便发布时自动汇总到变更日志并推导下一个版本号。这一机制由@changesets/cli驱动:PR 作者在.changeset/目录下提交一个小型 Markdown 文件,描述这次改动的严重级别与内容摘要;发布时工具会消费这些文件,统一生成CHANGELOG.md并提升版本号。

仓库根目录下的 package.json 声明了完整的 changesets 工具链(@changesets/cli@changesets/changelog-github@changesets/pre@changesets/read),.changeset/config.json 则配置了发布行为,例如使用 GitHub 仓库关联生成 changelog 条目、baseBranch指向masteraccesspublic。也就是说,changeset 并不是一份孤立的手写记录,而是整个发布流水线的第一环。

何时需要添加 changeset

核心判断标准:当 PR 改变了用户可见的合约行为(user-visible contract behavior)时,必须添加 changeset。仓库的.claude/skills/add-changeset/SKILL.md明确列出了需要添加的场景清单:

  • 新增函数(new function)
  • 新增合约或扩展(new contract or extension)
  • 改变输出结果的 bug 修复(bug fix that changes outputs)
  • 新增事件(new event)
  • 新增错误(new error)
  • 新增接口 ID(new interface ID)
  • 收紧某个前置条件/要求(tighter requirement)

反过来,以下情况明确跳过

  • 仅修改 NatSpec 注释(NatSpec-only changes)
  • 无可观测影响的内部重构(internal refactors with no observable effect)
  • 仅涉及测试或 mock 的改动(test/mock-only changes)
  • 不改变已发布contracts/目录的 CI、lint、docgen、依赖升级等仓库管道(repo plumbing)改动

当判断不确定时,倾向添加(err on the side of adding one)。因为发布审查者(release reviewers)可以在打标签前通过删除该文件来把条目降级为"跳过",添加比遗漏更安全——遗漏意味着用户可见变更可能不会被记录到 changelog。

操作流程:npx changeset add

在仓库根目录执行:

npx changeset add

该命令会以交互式 CLI 引导你完成两步:

  1. 选择严重级别(severity)——见下文;
  2. 打开编辑器——编写变更描述正文。

生成的条目文件位于.changeset/目录下,文件名是随机生成的形容词-名词组合(如brown-jokes-applaud.md),内容遵循统一的 front matter 结构:

--- 'openzeppelin-solidity': minor --- `ComponentName`: One-sentence description starting with a backtick-quoted component name.

第一段 front matter 声明包名openzeppelin-solidity及其严重级别;空一行后是变更描述正文。这正是仓库 .changeset/brown-jokes-applaud.md 等真实文件的格式:

--- 'openzeppelin-solidity': patch --- `ERC7579Utils`: Add in-depth sanity check when `executionCalldata` is not the last buffer in calldata.

严重级别:patch 与 minor,没有 major

changeset CLI 提供的严重级别语义如下:

级别适用场景
patchbug 修复、gas 优化、不改变 API 表面(no API surface change)
minor新增合约、新增扩展、在既有合约上新增公开函数

值得注意的是:贡献者不会使用major。大版本号提升(major version bump)由维护者统一协调,普通贡献者只从patchminor中选择。这一点也可以从仓库中真实的 minor 条目得到印证,例如 .changeset/dull-games-fly.md:

--- 'openzeppelin-solidity': minor --- [BREAKING] `ERC2771Forwarder`: custom error `ERC2771ForwarderFailureInAtomicBatch` has been renamed to `ERC2771ForwarderNoRefundReceiver`

即使涉及破坏性变更(重命名自定义错误),贡献者提交的仍然是minor,破坏性提示通过描述文本中的[BREAKING]前缀表达,而非major级别。

格式规则详解

changeset 正文不是自由散文,而是被发布工作流逐字拼接(concatenate verbatim)进 changelog 的,因此有严格的格式约束:

  • 反引号内第一个词必须是被影响的合约、扩展或库名,例如ERC20ERC1155CrosschainMemoryGovernor
  • 然后是冒号加一句话,描述用户可见的变更;
  • 使用祈使语气,与 PR 标题保持一致:写 "Add"、"Fix",而不是 "Added"、"Fixed";句尾句号可选;
  • 禁止使用项目符号列表、标题、多段落散文——因为发布工作流会原样拼接这些内容。

仓库 .claude/skills/add-changeset/SKILL.md 给出了两个标准示例:

`Memory`: Add a `isReserved(Slice)` function that checks if the memory occupied by the slice is reserved (i.e. before the free memory pointer).
`ERC1155Crosschain`: Add an ERC-1155 extension to embed an ERC-7786 based crosschain bridge directly in the token contract.

还有一种允许的变体:不带前导反引号的单句描述。当一次改动涉及多个组件、单一前缀反而会产生误导时使用,例如:

Add ERC-165 detection for the `IERC6909ContentURI`, `IERC6909TokenSupply` and `IERC6909Metadata` interfaces in the `ERC6909ContentURI`, `ERC6909TokenSupply` and `ERC6909Metadata` contracts respectively.

提交后:changeset-bot 检查与豁免机制

changeset 文件随 PR 提交后,GitHub 上的changeset-bot检查会发布一条评论,确认检测到了 changeset。如果你收到 "No Changeset found" 的提示,而该 PR 本就不该有 changeset(例如纯 NatSpec 改动、无可观测影响的内部重构),处理方式是:在 PR 评论区留下说明理由的评论,维护者会据此打上ignore-changeset标签来豁免检查。换句话说,跳过 changeset 不是靠删掉检查,而是通过明确沟通 + 维护者人工豁免的流程。

从源码看 changeset 如何驱动发布流水线

changeset 文件本身不是终点,它们会在版本发布阶段被消费。仓库中的发布脚本揭示了这条链路:

  • scripts/release/version.sh 的第一步就是执行changeset version——该命令根据.changeset/下的条目内容提升版本号,并把条目合并进CHANGELOG.md
  • 随后运行 scripts/release/format-changelog.js 对 changesets 生成的 changelog 做后处理:移除### Major Changes/### Minor Changes/### Patch Changes分组标题、压缩条目间空白、把 PR 链接合并到行尾、为版本标题补上发布日期,并在非预发布(非PRERELEASE)环境下剔除rc预发布版本段;
  • scripts/release/workflow/start.sh 与 scripts/release/workflow/exit-prerelease.sh 则负责预发布/正式发布的状态切换。

这条链路解释了为什么格式规则如此严格:正因为 changelog 由条目逐字拼接生成,任何多余的列表符号、标题或多段文字都会直接污染最终的CHANGELOG.md排版。

仓库真实案例速查

当前仓库.changeset/目录中的真实条目是理解规范的最佳范本,除了上文引用的之外,还可以对比学习:

  • .changeset/eip712-drop-string-fallback.md(minor)——描述 EIP-712 域存储回退移除的行为变更,并补充说明其对代理/克隆部署场景的影响,展示如何在单句中同时交代变更与动机;
  • .changeset/governor-prevent-late-quorum-max-deadline.md(patch)——说明新增内部虚函数_maxLateQuorumVoteExtension及默认值,属于"收紧前置条件"类型的典型写法;
  • .changeset/paymaster-guarantor-effective-prefund.md(patch)——一个修复序列化行为的 bug 修复条目;
  • .changeset/lazy-hooks-return.md(patch)——描述AccountERC7579模块卸载回滚语义的变化;
  • .changeset/twenty-states-taste.md(patch)——RSA.pkcs1Sha256从 revert 改为返回false,属于"改变输出结果"的修复。

对照这些条目再回看格式规则,你会发现每条都严格遵循"反引号组件名 + 冒号 + 单句祈使句"的骨架,这正是 OpenZeppelin Contracts 发布工程化流程得以稳定运转的基础。在实际贡献 PR 时,把本文的规则当作 checklist 使用即可:判断是否用户可见 → 选择patch/minor→ 生成并审阅条目 → 提交后留意 changeset-bot 的反馈。

【免费下载链接】openzeppelin-contractsOpenZeppelin Contracts is a library for secure smart contract development.项目地址: https://gitcode.com/GitHub_Trending/op/openzeppelin-contracts

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

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

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

立即咨询