Moment.js 发布流程全解:从版本号提升、自动化校验到 npm 发布与标签恢复

原创2026-09-29 12:54:241,438 阅读
文章标签:前端后端

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 脚本驱动,后两步是人工确认环节:

  1. 更新变更日志与版本号:执行 pnpm release:bump-version x.y.z(例如 pnpm release:bump-version 2.31.0),同步更新 CHANGELOG 与多处版本声明。
  2. 运行 pnpm release:该命令会执行 lint、测试与发布构建,并更新已提交的发行版分发文件(distribution files)。
  3. 审查并提交:将源码、元数据与重新生成的分发文件变更一并提交。
  4. 确保受保护的源分支为绿色(CI 通过):例如 2.x 分支或 master 分支。
  5. 创建并推送裸版本标签:标签名即版本号本身,如 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+$),并执行三类替换:

这保证了源码、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 个环节,与工作流的实际步骤一一对应:

  1. SemVer 校验:验证标签是合法语义化版本号,且严格大于 master 分支上的当前版本(release.yml 用 madhead/semver-utils 与 master 分支 package.json 版本比较,非递增即失败)。
  2. 标签与 package.json 匹配:检查 GITHUB_REF_NAME 与 package.json 的 version 是否一致。
  3. 运行 lint、运行时测试与发布构建:即执行 pnpm release。
  4. 验证重建不改变已提交的分发文件:pnpm release 后再执行 git status --porcelain,若仓库出现任何改动即报 "Release artifacts are stale",说明标签不包含最新生成产物。
  5. 构建 npm tarball 两次并校验 SHA-256 可复现性:pnpm pack 后记录 sha256sum,再次 pnpm pack 并 sha256sum --check 验证两次产物哈希一致(release.yml)。
  6. 安装并测试确切 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 依赖实现矩阵化验证)。
  7. 上传 tarball 与校验和作为工作流产物(actions/upload-artifact)。
  8. 发布到 npm:pnpm publish ./moment-*.tgz --access public --provenance --no-git-checks,使用 provenance 生成来源证明(release.yml)。
  9. 将 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 库发布管道借鉴。

相关资源

登录后查看全文
moment