Moment.js 发布流程全解:从版本号提升、自动化校验到 npm 发布与标签恢复
Moment.js 发布流程全解:从版本号提升、自动化校验到 npm 发布与标签恢复
导读
本文以 RELEASING.md 为核心,系统讲解 JavaScript 日期库 Moment.js 从"准备发布"到"npm 发布"的完整官方流程,包括版本号提升、分发文件再生成、GitHub Actions 自动化校验、npm 发布以及失效发布标签的恢复操作。读者读完本文后,将掌握 Moment.js 的发布命令链(pnpm release:bump-version 与 pnpm release)、底层各脚本的实际作用、发布工作流 release.yml 的 9 个校验环节,以及遇到"陈旧标签"时的三种安全恢复方案,可将其迁移用于其他 npm 包的发布实践。
一、发布前准备:四步走
Moment.js 官方在 RELEASING.md 中把一次发布的准备工作定义为四个步骤,其中前两步由 pnpm 脚本驱动,后两步是人工确认环节:
- 更新变更日志与版本号:执行
pnpm release:bump-version x.y.z(例如pnpm release:bump-version 2.31.0),同步更新 CHANGELOG 与多处版本声明。 - 运行
pnpm release:该命令会执行 lint、测试与发布构建,并更新已提交的发行版分发文件(distribution files)。 - 审查并提交:将源码、元数据与重新生成的分发文件变更一并提交。
- 确保受保护的源分支为绿色(CI 通过):例如 2.x 分支或 master 分支。
- 创建并推送裸版本标签:标签名即版本号本身,如
2.31.0。
为何生成的发行文件需要被提交进仓库?RELEASING.md 明确解释:因为 Bower 及其他遗留生态的消费者会直接从 Git 标签安装。同时 CONTRIBUTING.md 也印证:
moment.js、locale/*.js、min/*.js这些文件只在发布时生成,贡献者不应在 PR 中提交它们。
1.1 版本号提升脚本做了什么
pnpm release:bump-version 对应 scripts/bump-version.js,它接收 x.y.z 格式参数(正则校验 ^\d+\.\d+\.\d+$),并执行三类替换:
- 更新 src/moment.js 中
//! version : ...注释与moment.version = '...'赋值; - 更新 package.json 与
component.json的version字段; - 更新 meteor/package.js 的
version:声明。
这保证了源码、npm 元数据、Bower/Component 清单与 Meteor 包中的版本号在所有渠道上保持一致。
1.2 pnpm release 到底执行了什么
package.json 中 release 脚本定义为:
pnpm release = run-s lint test build release:update-index release:update-component release:minify
即按顺序串联(run-s)执行:
| 阶段 | 脚本 | 作用 |
|---|---|---|
| 静态检查 | lint |
ESLint、markdownlint、月份解析检查、Prettier 格式检查 |
| 单元测试 | test |
由 scripts/test.js 用 Rollup 打包 src/moment.js、全部 locale 与测试后运行 QUnit 测试 |
| 发布构建 | build |
由 scripts/build.js 生成 UMD/ESM 产物 |
| 分发索引 | release:update-index |
scripts/update-index.js 将构建产物拷贝到 moment.js、locale/、min/、dist/ |
| 组件清单 | release:update-component |
scripts/update-component.js 按 locale/ 实际文件重写 component.json 的 files 列表 |
| 压缩产物 | release:minify |
scripts/minify.js 用 UglifyJS 生成 min/moment.min.js、min/locales.min.js、min/moment-with-locales.min.js 及 sourcemap |
由此可见,一次 pnpm release 同时承担了"验证代码健康"与"再生成并提交发行文件"双重职责。
二、自动化校验与 npm 发布:release.yml 工作流
版本标签(如 2.31.0)被推送后,GitHub Actions 工作流 .github/workflows/release.yml 自动触发(on: push: tags,仅匹配 x.y.z 三位数字标签)。RELEASING.md 将其流程归纳为 9 个环节,与工作流的实际步骤一一对应:
- SemVer 校验:验证标签是合法语义化版本号,且严格大于
master分支上的当前版本(release.yml 用madhead/semver-utils与master分支package.json版本比较,非递增即失败)。 - 标签与 package.json 匹配:检查
GITHUB_REF_NAME与 package.json 的version是否一致。 - 运行 lint、运行时测试与发布构建:即执行
pnpm release。 - 验证重建不改变已提交的分发文件:
pnpm release后再执行git status --porcelain,若仓库出现任何改动即报 "Release artifacts are stale",说明标签不包含最新生成产物。 - 构建 npm tarball 两次并校验 SHA-256 可复现性:
pnpm pack后记录sha256sum,再次pnpm pack并sha256sum --check验证两次产物哈希一致(release.yml)。 - 安装并测试确切 tarball:在独立目录
pnpm add该 tarball,运行release:check-package(scripts/check-package.js 验证 CommonJS 入口、UMD 浏览器入口、min 产物、locale 元数据与发布必需文件齐全),并执行release:check-typescript用 TypeScript 1.8 到 7 的多个版本编译类型声明(package.json 通过typescript1~typescript6及typescript依赖实现矩阵化验证)。 - 上传 tarball 与校验和作为工作流产物(
actions/upload-artifact)。 - 发布到 npm:
pnpm publish ./moment-*.tgz --access public --provenance --no-git-checks,使用provenance生成来源证明(release.yml)。 - 将
master分支更新指向发布标签(git push origin HEAD:refs/heads/master)。
2.1 串行执行保证顺序一致
工作流通过 concurrency: group: release, cancel-in-progress: false(release.yml)实现发布串行化:同一时间只允许一个发布运行,每个标签都与"前一个刚完成的发布"进行比较,从而保证版本严格递增的比较基准不会被打乱。此外还先依赖 runtime-compatibility 复用的兼容性检查作业,发布作业声明 contents: write 与 id-token: write 权限(用于最终更新 master 分支与生成 npm 来源证明)。
2.2 发布失败时的排查指引
RELEASING.md 对失败处理给出了明确建议:
- npm 发布前的瞬时失败:直接在 GitHub Actions 中重新运行失败的任务或整个工作流即可,重跑会保留原始标签上下文;
- 发布步骤失败且结果不明确:先检查该版本是否已存在于 npm——不可重复发布不可变的版本;若 npm 已接受该版本,则应停止重跑,调查并记录这次已完成的发布。
三、从陈旧发布标签中恢复
当某个版本标签的产物校验(如第 4 步"重建无差异")失败时,npm 发布会在任何内容发布出去之前停止。此时需要修正发布提交并替换标签。以 2.31.0 为例,RELEASING.md 给出两条恢复路线:
路线一:删除旧标签再重建
# 1. 删除远程与本地标签
git push origin --delete 2.31.0
git tag --delete 2.31.0
# 2. 运行 pnpm release,提交产生的文件并推送到其源分支
pnpm release
# 3. 从修正后的提交重新创建并推送标签
git tag 2.31.0
git push origin 2.31.0
路线二:移动现有标签(先提交修正)
# 先完成修正提交,然后强制移动标签
git tag --force 2.31.0
git push --force origin refs/tags/2.31.0
重要限制
- 不要对受保护标签执行强制更新。若仓库策略禁止删除或移动该标签,应联系仓库管理员协助处理,或改发一个新版本,而不是绕过保护策略。
四、发布流程要点总结
Moment.js 的发布体系有四个核心设计:标签即版本(版本标签与 package.json 严格一致);产物随分支提交(服务 Bower 等 Git 直装消费者);可复现打包(两次 pnpm pack 哈希一致);先验证后发布(tarball 经完整安装、运行与多版本 TypeScript 声明测试后才发布)。这套流程把人工失误前置拦截在 npm 发布之前,值得同类 JavaScript 库发布管道借鉴。
相关资源
- RELEASING.md:发布流程官方文档(本文核心依据)
- .github/workflows/release.yml:发布自动化工作流完整定义
- package.json:
release、release:*等发布脚本清单 - scripts/bump-version.js:版本号同步脚本
- scripts/check-package.js:tarball 安装后完整性校验脚本
- CONTRIBUTING.md:贡献规范(含"发行文件仅发布时生成"的说明)