Changesets 一个强大而灵活的版本管理工具

112 阅读6分钟

用 Changesets 管理 Monorepo 版本?一篇朴实又详细的实战指南

写在前面:如果你正在维护一个包含多个 npm 包的 Monorepo 项目(比如使用 pnpm/yarn workspace),你一定经历过这些痛苦:

  • 每次发版都要手动改 package.json 的版本号
  • 忘记更新 CHANGELOG,用户不知道新版本改了啥
  • A 包依赖 B 包,B 升级了但 A 没同步,导致线上出问题
  • 团队成员对“这次要不要发版”争论不休

别慌,今天我要介绍的 Changesets,就是专治这些“版本管理综合症”的良药。它轻量、自动化、语义化,还能和 GitHub Actions 完美配合。本文将手把手带你从零配置到上线,没有花里胡哨的概念,只有真实可用的代码和配置


一、Changesets 是什么?能解决什么问题?

Changesets 是一个开源工具(由 Atlassian 开源),核心目标就两个:

  1. 自动计算并更新符合 SemVer(语义化版本)的版本号
  2. 自动生成清晰、规范的 CHANGELOG.md

但它最厉害的地方在于——专为 Monorepo 设计

它解决了哪些痛点?

痛点Changesets 的解法
手动改版本号容易出错自动生成,基于变更内容决定是 major/minor/patch
CHANGELOG 写着写着就懒得写了每次提交代码时顺便写一句描述,CHANGELOG 自动聚合生成
Monorepo 中包依赖关系复杂自动检测依赖,升级 A 包时,依赖 A 的 B 包也会自动 patch 升级
发布流程不透明通过 PR 预览所有变更,团队 review 后再发布
CI/CD 集成麻烦官方提供 GitHub Action,几行 YAML 就搞定自动化

一句话总结:Changesets 把“版本管理”这件事,从人工操作变成了可追溯、可协作、可自动化的流程


二、快速上手:5 分钟跑通 Changesets

我们以一个典型的 pnpm Monorepo 为例,演示如何快速集成 Changesets。

步骤 1:安装 & 初始化

# 在项目根目录执行
pnpm add -Dw @changesets/cli

# 初始化 Changesets
npx changeset init

执行后,会生成以下文件:

  • .changeset/ 目录(存放变更集)
  • .changeset/config.json(配置文件)
  • .changeset/README.md(说明文档)

步骤 2:添加脚本(方便后续使用)

在根目录 package.json 中添加:

{
  "scripts": {
    "changeset": "changeset",
    "version": "changeset version",
    "release": "changeset publish"
  }
}

步骤 3:创建你的第一个 Changeset

pnpm changeset

你会看到交互式界面:

image.png

  1. 选择受影响的包(用空格选中,回车确认)

    ◯ packages/core
    ◉ packages/ui
    
  2. 选择版本类型

    ◯ major (breaking change)
    ◉ minor (new feature)
    ◯ patch (bug fix)
    
  3. 输入变更描述

    Added Button component with theme support
    

完成后,会在 .changeset/ 下生成一个类似 funny-dogs-write.md 的文件:

---
"packages/ui": minor
---

Added Button component with theme support

💡 这个文件就是“变更集”,它会被 Git 提交,成为版本变更的唯一事实来源

步骤 4:准备发布(本地测试)

# 计算新版本号 + 更新 package.json + 生成 CHANGELOG
pnpm version

# 查看效果(别急着 push)
git diff

你会看到:

  • packages/ui/package.jsonversion1.0.01.1.0
  • packages/ui/CHANGELOG.md 新增了一条记录
  • .changeset/funny-dogs-write.md 被自动删除(已处理)

步骤 5:发布到 npm

# 先确保你已登录 npm
npm login

# 发布
pnpm release

# 推送代码和 tag
git push --follow-tags

🎉 搞定!整个过程无需手动改任何版本号。


三、Monorepo 场景下的高级用法

场景 1:A 包依赖 B 包,B 升级后 A 也要跟着升

假设:

  • packages/ui 依赖 packages/core
  • 你在 core 中修复了一个 bug(patch 变更)

Changesets 会自动:

  1. core1.0.01.0.1
  2. ui 的依赖更新为 "packages/core": "workspace:^1.0.1"
  3. 同时将 ui 的版本也 patch 升级(比如 1.2.01.2.1

这是因为默认配置 "updateInternalDependencies": "patch",表示内部依赖更新时,当前包也做 patch 升级。

场景 2:多个包需要固定相同版本(如 React 生态)

如果你希望 @myapp/core@myapp/ui@myapp/utils 永远保持相同版本号(类似 Babel 的做法),只需在 .changeset/config.json 中配置:

{
  "fixed": [
    ["@myapp/core", "@myapp/ui", "@myapp/utils"]
  ]
}

这样,只要其中一个包有变更,所有包都会一起升级到同一个版本。

场景 3:预发布(alpha/beta/rc)

想先发个测试版给用户试用?

# 进入 beta 预发布模式
npx changeset pre enter beta

# 创建 changeset(和平时一样)
pnpm changeset

# 生成版本(会变成 1.0.0-beta.0)
pnpm version

# 发布到 npm 的 beta 标签
pnpm release --tag beta

# 退出预发布模式
npx changeset pre exit

四、自动化发布:GitHub Actions 配置详解

手动发布适合小项目,但团队协作必须自动化。以下是经过生产验证的 GitHub Actions 配置。

文件:.github/workflows/release.yml

name: Release

on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # 获取完整历史,保证 CHANGELOG 正确

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

      - name: Install pnpm
        uses: pnpm/action-setup@v2

      - name: Install deps
        run: pnpm install --frozen-lockfile

      - name: Create Release PR or Publish
        uses: changesets/action@v1
        with:
          version: pnpm version
          publish: pnpm release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

效果:

  1. 当你 push 到 main 分支且包含 .changeset/*.md 文件时
    → 自动创建一个名为 "Version Packages" 的 PR,里面预览所有版本变更和 CHANGELOG。
  2. 当你合并这个 PR 时
    → 自动运行 pnpm release,把包发布到 npm,并打上 Git tag。

🔑 关键点

  • 所有发布操作都通过 PR 审核,避免误发
  • 团队成员可以在 PR 里看到“这次到底改了啥”
  • 无需任何人手动执行 pnpm release

如何设置 NPM_TOKEN?

  1. npm 官网 → Account → Access Tokens → Generate New Token(选 Automation 类型)

  2. 在 GitHub 仓库 Settings → Secrets → New repository secret

    • Name: NPM_TOKEN
    • Value: 粘贴你刚生成的 token

五、最佳实践 & 常见坑

✅ 什么时候该写 Changeset?

场景是否需要
新增功能 / APIminor
修复 Bugpatch
破坏性变更(删 API、改参数)major
仅修改文档、注释、格式化❌ 不需要
更新 devDependencies❌ 不需要

✍️ Changeset 描述怎么写才专业?

updated code

Added `useFetch` hook for data fetching

- Supports automatic retry and loading states
- Returns `{ data, error, loading }`
- Example:
  ```ts
  const { data } = useFetch('/api/users');

💡 原则:站在使用者角度写,而不是开发者角度。

⚠️ 常见问题排查

Q:发布后发现版本号不对,怎么办?

A:立刻回滚 Git commit 和 npm 版本(npm unpublish 有时间限制),然后检查 .changeset/ 下是否有多余的变更集。

Q:CHANGELOG 格式不符合团队规范?

A:可以自定义 changelog 生成器,支持完全自定义格式。

Q:为什么依赖包没自动升级?

A:检查 package.json 中的依赖是否用了 workspace:^ 协议(pnpm)或 workspace:*(yarn)。例如:

{
  "dependencies": {
    "@myapp/core": "workspace:^"
  }
}

六、总结:为什么我推荐 Changesets?

对比项传统方式Changesets
版本号管理手动改,易出错自动计算,符合 SemVer
CHANGELOG经常忘记写强制关联每次提交
Monorepo 支持需要自己写脚本内置依赖分析
发布流程黑盒,只有 maintainer 知道通过 PR 透明化
CI/CD 集成复杂官方 Action,开箱即用

Changesets 的哲学很简单:把“版本变更”当作代码的一部分来管理
每个 Changeset 文件都是一个小型 RFC,记录了“为什么这次要发版”。这不仅提升了工程效率,更让团队协作更加清晰。


附:完整配置参考

.changeset/config.json

{
  "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "public",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": []
}

根目录 package.json 脚本

{
  "scripts": {
    "changeset": "changeset",
    "version": "changeset version",
    "release": "changeset publish",
    "ci:version": "changeset version && pnpm install --lockfile-only",
    "ci:publish": "pnpm build && changeset publish"
  }
}

最后:如果你的项目是 Monorepo,还在手动管理版本,真的该试试 Changesets 了。它不会让你的项目变得“高大上”,但一定能让你的发布流程少踩 80% 的坑

📚 官方文档github.com/changesets/…