Material UI 发布与文档部署实战指南:面向 monorepo 的版本发布、Hotfix 与免发布文档上线全流程
导读
本指南基于 scripts/README.md,系统讲解 Material UI 当前 monorepo(仓库根版本见 package.json,当前为 9.4.0)从版本准备、Changelog 生成、npm 发布、GitHub Release、文档部署到公告的完整发布 SOP(标准操作流程)。文中不仅完整保留原文档的全部操作步骤与命令参数,还结合根目录 package.json、scripts/releaseChangelog.mjs、scripts/releasePack.mts、scripts/canaryRelease.mts 与 .github/workflows/publish.yml 等源码与 CI 配置,补充命令底层实现细节,使读者既能照着执行一次 minor/hotfix 发布,也能理解每条命令背后究竟发生了什么。
适用读者:Material UI monorepo 的维护者与贡献者,以及任何希望理解“大型 pnpm workspace + Lerna + GitHub Actions”发布链路的技术人员。
一、发布前置准备:远程仓库与 GitHub Token
在开始任何一次发布之前,需要先完成两项一次性配置。
1. 添加两个 Git Remote
文档部署与发布流程依赖两个远程仓库,一个用于同步上游代码,一个用于推送构建后的文档站点:
git remote add upstream https://github.com/mui/material-ui.git
git remote add material-ui-docs https://github.com/mui/material-ui-docs.git
其中 upstream 指向官方主仓库,material-ui-docs 是承载文档站点构建产物的仓库——它的各分支(latest、next、v*.x)分别对应不同版本的 mui.com 站点。
2. 生成 GitHub Token
文档原文要求在 GitHub 的设置页生成个人访问令牌,并写入 shell 配置(.bashrc 或 .zshrc)中的环境变量 GITHUB_TOKEN;若使用 Classic Token,需为其勾选 repo:public 权限范围。
从 scripts/releaseChangelog.mjs 的源码可以看到,脚本运行时优先读取 GITHUB_TOKEN 来初始化 Octokit 客户端,用于访问 GitHub API 拉取提交信息:
octokit: process.env.GITHUB_TOKEN
? new Octokit({ auth: process.env.GITHUB_TOKEN })
: undefined,
同时脚本会对从环境变量读取 Token 的做法给出废弃警告("Using GITHUB_TOKEN from environment variables have been deprecated"),并提示:若本地设置了该变量应移除,优先通过命令参数 --githubToken <my-token> 传入(见下文)。这也说明实际操作时应以最新版脚本帮助信息为准。
二、发布一个 minor 版本
minor 版本(月度功能发布)遵循如下节奏:先以 PR 形式完成准备工作 → 通过 GitHub Actions 手动触发发布 → 部署文档 → 发布 GitHub Release → 公告。
阶段一:Prepare(必须通过 PR 提交)
以下步骤的产物(版本号变更 + Changelog)必须以 Pull Request 形式提出,等待审查与 CI 通过:
第 1 步:拉取上游标签
git fetch upstream --tags
这一步保证本地拥有完整的 tag 历史,供后续 Changelog 脚本定位“上一个发布版本”。
第 2 步:更新根目录版本号
修改仓库根 package.json 中的 "version" 字段(当前仓库该字段为 "9.4.0")。它是整个 monorepo 的版本基准。
第 3 步:生成 Changelog
pnpm release:changelog
根 package.json 中该命令的定义为 node scripts/releaseChangelog.mjs。生成结果需要前置拼接到仓库顶层的 CHANGELOG.md 中。
可通过 pnpm release:changelog --help 查看该命令的全部参数。源码 scripts/releaseChangelog.mjs 展示了核心的两个选项:
| 选项 | 含义 | 默认值 |
|---|---|---|
--lastRelease |
作为对比基准的版本(如 v5.0.0-alpha.23) |
当前分支上的最新 tag |
--release |
本次要发布的 ref | master |
若 GITHUB_TOKEN 不在环境变量中,可将 Token 作为参数传入:pnpm release:changelog --githubToken <my-token>。
该脚本的底层逻辑(见 scripts/releaseChangelog.mjs)值得展开说明,方便你理解生成的 Changelog 结构:
- 通过
@mui/internal-code-infra/changelog的fetchCommitsBetweenRefs获取两个 ref 之间的提交,并过滤掉机器人提交([bot]结尾的 login)和以[website]开头的提交; - 解析每条 commit message 开头的方括号标签(如
[Slider]、[docs]),将标签转小写、升序排列后按组归类排序提交; - 对没有 PR 链接(正则
/\(#[0-9]+\)$/)的提交,追加 7 位 commit SHA 以便追溯; - 自动统计贡献者数量并生成“按字母序排列的贡献者名单”;
- 版本号从当前 package.json 的
version字段读取,输出模板中预留了TODO INSERT HIGHLIGHTS占位符,提醒维护者手动补写 release 亮点。
第 4 步:人工清理生成的 Changelog
- 使格式与仓库 GitHub Releases 页面的既有格式保持一致;
- 如适用,将包名大小写改为小写(便于与 npm 包名统一)。
第 5 步:提升各包版本号
pnpm release:version
根 package.json 中该命令定义为:
lerna version --no-changelog --no-push --no-git-tag-version --no-private --force-publish=@mui/core-downloads-tracker
它基于 Lerna 工作。执行时务必留意两条规则:
- 只有自上版以来确有变更的包才应被提升版本;
- 有变更且遵循 Material-UI 版本体系的包,应提升到与根 package.json 相同的版本——这可能需要跳过某些中间版本号(即“对齐到根版本”),因为各包发布节奏并不完全一致。
第 6-7 步:提交 PR
打开包含以上变更的 PR,等待审查与 CI 全绿后合入 master。合入后记住该 merge commit 的 SHA,后续发布工作流需要用到。
阶段二:Release(通过 GitHub Actions 触发)
- 前往仓库的 Publish packages workflow(对应仓库文件 .github/workflows/publish.yml);
- 点击 “Run workflow” 下拉框,填入以下输入项:
| 输入项 | 说明 |
|---|---|
| Branch | 固定为 master |
| Commit SHA to release from | 包含已合入 release 变更的 master 提交(该提交与后续 GitHub Release 相关联) |
| Run in dry-run mode | 调试用,勾选后不会真正发布 |
| Create GitHub release | 保持勾选,将依据 Changelog 自动创建 GitHub Release 草稿 |
| npm dist tag to publish to | 用于发布 legacy 或 canary 版本(如 next、legacy) |
从 .github/workflows/publish.yml 的 workflow_dispatch 输入定义可见,完整输入还包括 internal-packages(是否仅发布内部包),且各输入默认值清晰:dry-run 默认 false、github-release 默认 true、dist-tag 默认 latest。
- 点击 “Run workflow” 后刷新页面,点击新创建的 workflow run;
- 随后页面会出现 "@username requested your review to deploy to npm-publish" 的环境保护提示,需要点击 “Review deployments” 并授权该次 workflow run。务必只授权你自己发起的 workflow run——这是 npm 发布环境的安全门禁。
阶段三:部署文档
minor 版本发布后,执行:
pnpm docs:deploy
根 package.json 将该命令转发给 docs 包的 deploy 脚本,即 docs/package.json 中定义的:
git fetch upstream master && git push -f material-ui-docs FETCH_HEAD:latest
可见其本质是把上游 master 最新内容强制推送到 material-ui-docs 仓库的 latest 分支,触发静态站点(Netlify 上部署的 mui.com)更新。必要时允许 force push。注意:这一步使用 force push 属于该流程的既定设计,用于让 docs 分支始终与目标提交精确对齐。
阶段四:发布 GitHub Release
文档部署完成后,在 GitHub Releases 页面审查之前自动创建的 draft Release(依据你勾选的 “Create GitHub release”),确认无误后点击发布。此时 release tag 才被创建——即 tag 在流程末尾落地,早于 tag 的发布操作没有意义。
阶段五:公告
文档站点生效后,按照项目维护团队内部发布的公告清单执行 Announce 步骤(邮件、社交媒体、内部渠道等)。
三、发布一个 hotfix 版本
Hotfix 适用于:线上有回归缺陷亟待修复,等不及下一个月度 minor 发布周期,而 master 上又已积累了暂未发布的提交(这些提交不能一起带出去)。如果更早的 minor 或 patch 可以直接发布,文档明确建议优先用常规发布而非 hotfix。
阶段一:Prepare
创建 hotfix 分支需要仓库管理员协助,步骤如下:
- checkout 到最新 release tag 对应的提交;
- 创建名为
release/<PATCH_VERSION>的分支,其中<PATCH_VERSION>是从该 release tag 起算的下一个 semver patch 版本号(例如上个 tag 是v9.4.0,则分支名为release/9.4.1); - 将分支强制推送到
upstream:
git push -f upstream release/<PATCH_VERSION>
后续步骤同样必须以 PR 形式提交到 release/<PATCH_VERSION> 分支:
-
checkout 该分支,并在其上 cherry-pick 需要修复的 hotfix 提交;
-
生成 Changelog(命令与 minor 流程一致,同样支持
--githubToken传参):pnpm release:changelog输出同样前置拼接到根 CHANGELOG.md;
-
人工清理 Changelog:格式对齐 GitHub Releases 页面、包名大小写统一为小写;
-
更新根 package.json 的
version为 patch 版本号; -
执行
pnpm release:version,遵循与 minor 流程相同的两条规则(仅提升有变更的包、同步到根版本号,必要时跳过中间版本); -
打开 PR 等待审查与 CI;
-
CI 全绿且获批后合入
release/<PATCH_VERSION>; -
再从
release/<PATCH_VERSION>打开并合入一个回到 master 的 PR,用于回写正确的包版本并更新 Changelog(因为 master 上本次未走常规发布,需要把版本状态对齐)。
阶段二:发布
hotfix 分支合入后,同样通过 “Publish packages” 工作流手动触发,区别在于选择的 commit SHA 来自 release/<PATCH_VERSION> 分支上的合入提交,随后走与 minor 相同的 Review deployments 授权步骤。
阶段三:部署文档
hotfix 的文档部署不走 pnpm docs:deploy(那是 master 语义),而是直接推送到 docs 仓库:
git push -f material-ui-docs HEAD:latest
阶段四:发布 GitHub Release
发布步骤中 workflow 会自动创建 draft Release;文档部署完成后审查并发布该草稿。
阶段五:Cleanup
发布完成后把 release/<PATCH_VERSION> 分支合并回 master。合入时注意解决冲突——master 在此期间可能已对相同文件产生后续改动。
阶段六:公告
文档上线后,同样执行公告清单。
四、执行 npm 包发布与 GitHub Release
无论 minor 还是 hotfix,最终“真正把包推送到 npm”这一步既可交给上述 GitHub Actions 工作流,也可在本地通过命令完成(两种方式殊途同归,本地命令本质是触发同一套发布逻辑)。
pnpm release:publish
命令说明与参数
根 package.json 中的定义:
"release:publish": "code-infra publish --github-release",
"release:publish:dry-run": "code-infra publish --github-release --dry-run",
code-infra 是 MUI 内部的代码基础设施 CLI(来自 @mui/internal-code-infra 依赖),--github-release 标志表示发布后创建 GitHub Release。
执行要点:
-
首次运行(或间隔很久)时,可能会要求先通过 GitHub 认证;
-
命令会自动拉取最新已合入的 release PR,并在发布前要求你确认;
-
若已知道要发布的 commit SHA,可直接传入,跳过交互式确认:
pnpm release:publish --sha <your-sha> -
其他可用标志:
| 标志 | 用途 |
|---|---|
--dry-run |
调试用,不真正发布;等价于直接运行 pnpm release:publish:dry-run |
--dist-tag |
发布 legacy 或 canary 版本时指定 npm dist-tag |
- 该命令会调用 .github/workflows/publish.yml 中定义的 Publish GitHub Action,并打印出 workflow run 的 URL 供追踪;
- 同样需要进入工作流页面,点击 “Review deployments” 授权本次发布。再次强调:不要批准任何非你发起的 workflow run。
其他辅助发布命令
仓库中还配套了另外两个发布相关命令,可结合需要了解:
pnpm release:pack(定义于 package.json,实现见 scripts/releasePack.mts):将各公开 workspace 执行pnpm pack打包为.tgz到./packed目录(可用--packages、--outDir、--concurrency调整),并生成manifest.json,用于发布前的产物校验,默认只打包非 private 的公开包;pnpm canary:release(定义于 package.json,实现见 scripts/canaryRelease.mts):面向 canary 版本,通过pnpm list --filter ...[baseline]找出相对基线(默认git describe --abbrev=0的最新 tag)变更的公开包,自动改写版本号为<version>-dev.<yyyyMMdd-HHmmss>-<short-sha>后以--tag canary发布。工作流层面则由 .github/workflows/publish-canaries.yml 承接。
五、不发布版本的情况下单独部署文档
有些场景需要只部署选中的某些提交,而不携带自上次发布以来合入 master 的全部变更(例如发布一篇博客文章,或紧急修复文档站点)。此时可绕过“发布循环”,直接向 material-ui-docs 仓库的指定分支推送。
分支与站点子域名对应关系
| 目标分支 | 对应站点 |
|---|---|
latest |
https://mui.com/ |
next |
https://next.mui.com/ |
v*.x |
https://v*.mui.com/(如 v5.x 对应当前 master 之外的归档站点) |
若需部署到不同子域名,将下面命令中的 latest 替换为对应分支名即可。
操作步骤
1. 添加 remote(若尚未添加):
git remote add material-ui-docs https://github.com/mui/material-ui-docs.git
2. 拉取目标分支最新内容:
git fetch material-ui-docs latest
3. 切换到 detached HEAD(以 material-ui-docs/latest 为基准,避免干扰本地分支):
git switch --detach material-ui-docs/latest
4. Cherry-pick 需要纳入本次部署的提交:
git cherry-pick <commit>
无冲突时命令会自动完成提交;有冲突则需手动解决冲突后手动提交。
若命令报 bad revision,说明该提交不存在于本地仓库——它可能产生于远程分支(通常在合入 master 或 v*.x 时创建),此时先 fetch 对应远程分支的最新内容再重试即可。
5. 推送到 material-ui-docs 远端:
git push material-ui-docs HEAD:latest
6. 切回原分支(退出 detached HEAD 状态):
git checkout -
六、发布流程速查表与关键文件索引
为便于快速查阅,下表汇总本文全部核心命令及其底层定义位置:
| 操作 | 命令 | 实现位置 |
|---|---|---|
| 生成 Changelog | pnpm release:changelog [--githubToken ...] [--lastRelease ...] [--release ...] |
package.json / scripts/releaseChangelog.mjs |
| 提升各包版本 | pnpm release:version |
package.json(Lerna) |
| 发布包 + GitHub Release | pnpm release:publish [--sha ...] |
package.json |
| 发布演练 | pnpm release:publish:dry-run |
package.json |
| 打包产物校验 | pnpm release:pack |
package.json / scripts/releasePack.mts |
| 发布 canary | pnpm canary:release |
package.json / scripts/canaryRelease.mts |
| minor 后部署文档 | pnpm docs:deploy |
package.json / docs/package.json |
| hotfix/免发布部署文档 | git push -f material-ui-docs HEAD:latest |
— |
| 手动触发 CI 发布 | Publish packages 工作流 | .github/workflows/publish.yml |
整个流程的核心闭环可以概括为:master(或 release/<PATCH_VERSION>)上以 PR 完成“版本号 + Changelog + 包版本对齐” → 合入后手动触发 Publish workflow(携带精确 SHA 与 dry-run/dist-tag 等开关)→ 授权 npm-publish 环境 → 部署文档站点 → 发布 GitHub Release(tag 在此落地)→ 公告。
需要特别留意三个易错点:其一,release tag 是在发布 GitHub Release 时才创建的,不要提前打 tag;其二,只批准自己发起的发布工作流,这是 npm 凭证环境的最后一道防线;其三,文档部署的 latest/next/v*.x 分支语义不同,免发布部署时务必选对分支,避免把未发布内容错推到 mui.com 主站。
结语
本文完整继承了 scripts/README.md 的全部发布操作细节,并补充了各命令在 package.json、scripts/releaseChangelog.mjs、scripts/releasePack.mts、scripts/canaryRelease.mts 与 .github/workflows/publish.yml 中的底层实现与参数定义。如果你正在为 Material UI monorepo 准备下一次 minor 或 hotfix 发布,可直接按本文的“Prepare → Release → Documentation → Publish GitHub release → Cleanup/Announce”五段式流程推进;如果你只是希望单独上线文档内容,第四节“免发布部署”是最轻量的路径。实际执行任何命令前,建议结合当前仓库版本再次核对参数帮助输出(如 pnpm release:changelog --help),因为仓库工具链会随版本演进而调整。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00