Changesets 工作流:Monorepo 项目中的版本管理与发包自动化

Changesets 工作流:Monorepo 项目中的版本管理与发包自动化

大家好,欢迎来到今天的讲座。今天我们来深入探讨一个在现代前端工程化中越来越重要的主题——Changesets 工作流,特别是在 Monorepo(多包仓库)项目中如何实现高效的版本管理和自动化发布流程

如果你正在维护一个包含多个独立模块的 Monorepo(比如使用 Lerna、Nx 或 Yarn Workspaces),那你一定遇到过这些问题:

  • 每次改了一个包,都要手动判断是否需要发版?
  • 版本号混乱?比如 v1.0.0v1.0.1 实际上只改了文档?
  • 发布时依赖关系没处理好,导致生产环境出错?
  • 团队成员不统一语义化版本规范,导致版本混乱?

这些问题,在使用 Changesets 后可以得到系统性解决。


什么是 Changesets?

Changesets 是一个由 GitHub 开源团队开发的工具,用于帮助你管理多包项目的版本变更和发布流程。它通过一个简单的“变更描述文件”机制,让你在每次代码提交后明确说明:“我这次改了什么”,然后自动决定哪些包需要发版、版本号如何递增。

它的核心思想是:

先记录变更 → 再决定发布 → 最后自动化发包

这不仅提升了版本管理的透明度,还极大减少了人为错误。


为什么要在 Monorepo 中用 Changesets?

Monorepo 的优势在于共享代码、统一构建、方便协作。但代价是:

  • 包之间相互依赖复杂
  • 手动版本管理容易出错
  • 发布流程繁琐

Changesets 提供了一种轻量级但强大的方式来应对这些挑战:

传统做法 Changesets 做法
手动打 tag + npm publish 自动识别变更并生成 changelog
依赖关系靠人工维护 自动检测依赖链并排序发布顺序
版本号随意写 严格遵循语义化版本(SemVer)规则
发布失败难回滚 变更记录可追溯,支持一键回退

✅ 它不是替代你的 CI/CD 流水线,而是为它提供智能决策依据。


如何集成 Changesets 到你的 Monorepo?

我们以一个典型的 Yarn Workspaces + TypeScript 的 Monorepo 为例(结构如下):

my-monorepo/
├── packages/
│   ├── core/
│   │   └── package.json
│   ├── ui/
│   │   └── package.json
│   └── utils/
│       └── package.json
├── .changeset/
│   └── config.json
├── package.json
└── yarn.lock

第一步:安装 Changesets

yarn add -D @changesets/cli

或者用 npm:

npm install --save-dev @changesets/cli

第二步:初始化配置

运行以下命令创建初始配置:

npx changeset init

这会生成一个 .changeset/config.json 文件,内容如下:

{
  "packages": ["packages/*"],
  "access": "public",
  "commit": false,
  "sign": false
}

关键参数解释:

参数 说明
packages 要监控的包路径(支持通配符)
access 发布权限(public / restricted
commit 是否自动 commit 变更文件(通常设为 false)
sign 是否签名(用于企业级安全场景)

第三步:编写变更记录(Change Entry)

每次你要发布新版本前,执行:

npx changeset add

你会看到交互式提示:

? What kind of change is this? (Use arrow keys)
❯ patch (bug fix or small improvement)
  minor (adds functionality in a backwards-compatible way)
  major (breaking change)
  none (no release needed)

选择后,它会创建一个 .changeset/xxx.md 文件,例如:

---
"package": "core"
"type": "patch"
"release": true
---

Fixed typo in error message.

这个文件就是“变更描述”,后续会被用来决定版本号和生成 changelog。

✅ 这个过程强制你在代码提交后明确告诉系统:“这次改了什么,要不要发版”。


自动化发布流程设计(CI/CD 集成)

现在我们来看如何将 Changesets 整合进 CI 流程,实现真正的自动化发布。

假设你使用 GitHub Actions,以下是完整的 .github/workflows/release.yml 示例:

name: Release

on:
  push:
    branches: [ main ]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          registry-url: 'https://registry.npmjs.org/'

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Create release
        run: npx changeset version
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Publish packages
        run: npx changeset publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

这段脚本做了三件事:

  1. changeset version:根据 .changeset/*.md 文件生成新的版本号,并更新每个包的 package.json
  2. changeset publish:自动发布所有标记为 release: true 的包到 npm。
  3. 使用 NODE_AUTH_TOKEN 确保认证安全。

🧠 小技巧:你可以加一个条件判断,只在主分支合并时触发发布,避免误操作。


实战案例:一个真实的 Monorepo 发布流程

假设我们的项目有三个包:

包名 当前版本 变更类型
core v1.2.0 patch(修复 bug)
ui v1.0.0 minor(新增按钮组件)
utils v0.5.0 none(仅文档修改)

当你运行 npx changeset add 并分别选择:

  • core: patch
  • ui: minor
  • utils: none

最终生成的 .changeset/ 目录结构:

.changeset/
├── 2024-06-01--core.patch.md
├── 2024-06-01--ui.minor.md
└── 2024-06-01--utils.none.md

然后执行 npx changeset version,结果如下:

// packages/core/package.json
{
  "version": "1.3.0"
}

// packages/ui/package.json
{
  "version": "1.1.0"
}

// packages/utils/package.json
{
  "version": "0.5.0" // 不变
}

接着 npx changeset publish 会按依赖顺序发布(比如 ui 依赖 core,则先发 core 再发 ui),避免依赖冲突。


高级特性:自定义版本策略 & Changelog 自动生成

Changesets 默认基于语义化版本(SemVer)进行版本递增,但它也支持插件扩展:

1. 自定义版本规则(如时间戳或内部版本号)

可以通过 @changesets/semver 插件定制版本号格式。例如:

{
  "plugins": [
    "@changesets/semver"
  ]
}

也可以配合自定义脚本处理版本逻辑,比如:

// .changeset/config.js
module.exports = {
  packages: ['packages/*'],
  plugins: [
    ['@changesets/semver', { 
      bump: (type, currentVersion) => {
        if (type === 'major') return `${currentVersion.split('.')[0]}.0.0`;
        return currentVersion; // 自定义逻辑
      }
    }]
  ]
};

2. 自动生成 Changelog(推荐)

Changesets 支持输出 Markdown 格式的 changelog,非常适合作为 release note 发布:

npx changeset generate

生成示例:

## [1.3.0](https://github.com/user/my-monorepo/compare/v1.2.0...v1.3.0) (2024-06-01)

### Patch

- Fixed typo in error message. ([core])

## [1.1.0](https://github.com/user/my-monorepo/compare/v1.0.0...v1.1.0) (2024-06-01)

### Minor

- Added new Button component. ([ui])

你可以把这个文件上传到 GitHub Releases 页面,或者集成到文档网站(如 Docusaurus、Next.js)中展示。


常见问题与最佳实践

问题 解决方案
❌ 多人同时提交,变更冲突怎么办? 使用 Git 分支隔离,建议每功能分支对应一个 .changeset/*.md 文件
❌ 发布失败怎么办? Changesets 会在失败时保留 .changeset 文件,下次继续尝试;也可用 --dry-run 测试
❌ 如何控制发布范围? .changeset/config.json 中设置 packages 字段过滤目标包
❌ 不想每次手动 add 结合 husky + lint-staged,在 git commit 前自动触发 changeset add(见下文)

最佳实践总结:

每次提交都要有变更记录 —— 即使是文档修改也要写清楚,便于追踪历史
保持变更描述简洁清晰 —— 不要写“修了一下”这种模糊语句
定期清理未发布的变更文件 —— 如果长时间未发布,可能遗漏重要信息
结合 PR 标签自动分类 —— 比如带 changelog: patch 的 PR 自动归类为 patch 类型
建立团队共识 —— 明确谁负责写变更描述,避免责任不清


总结:Changesets 是 Monorepo 的“版本指挥官”

通过今天的学习,你应该已经理解:

  • Changesets 是一套声明式、可追踪、自动化的版本管理方案;
  • 它特别适合 Monorepo 场景,能显著降低发布风险;
  • 从添加变更记录 → 自动版本升级 → 一键发布,形成闭环;
  • 可灵活扩展,满足不同团队的发布需求。

这不是一个“高级技巧”,而是一个工程化必备能力。尤其是在微前端、组件库、SDK 等场景下,Changesets 已经成为主流实践。

最后送一句来自官方文档的话:

“Changesets makes it easy to manage your monorepo’s releases without needing to think about version numbers or dependency chains.”
—— Changesets 官方文档

希望你能把这套方法应用到自己的项目中,告别混乱的版本号,拥抱清晰、可控的发布流程!

如果你还有疑问,欢迎留言讨论 👇
祝你早日打造一个稳定、高效、可维护的 Monorepo!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注