change-versions 1.4.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -2
- package/dist/cli.js +24 -24
- package/docs/commands.md +113 -36
- package/docs/release-flow.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,44 @@ bunx --no-install change-versions release --retry-unpublished
|
|
|
43
43
|
|
|
44
44
|
运行 CLI 需要 Node.js,无需 Bun;Bun 用于源码开发与构建。正式档位使用 `version patch|minor|major`。第一次 version 提交建立基线后,可以省略 `--baseline`。日常使用 release 串起升版与发布;version/publish 也可分开执行。重试使用 release --resume,避免再次升版。
|
|
45
45
|
|
|
46
|
-
`change-versions.yml`
|
|
46
|
+
`change-versions.yml` 使用按 `publish` / `test` 分类的配置,不兼容旧版缩进目录树配置。可先运行 init,在生成的文件中补上源码路径和 hooks。配置 `publish.branch` 时,version 必须在该分支执行;不配置则不推送。
|
|
47
|
+
|
|
48
|
+
此次配置结构为 **Breaking Change**:旧的顶层发布字段移入 `publish`,`test.global` 改为 `test.allPackagesInputs`。旧写法会报错提示迁移,不再兼容。字段对照见 [配置迁移](docs/commands.md#配置迁移breaking-change)。
|
|
49
|
+
|
|
50
|
+
## 按变更运行测试
|
|
51
|
+
|
|
52
|
+
在 `change-versions.yml` 中分别声明发布与测试配置:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
publish:
|
|
56
|
+
include:
|
|
57
|
+
- packages/*/src/**
|
|
58
|
+
exclude:
|
|
59
|
+
- packages/*/src/**/__tests__/**
|
|
60
|
+
hooks:
|
|
61
|
+
beforePublish: bun run build
|
|
62
|
+
|
|
63
|
+
test:
|
|
64
|
+
command: bun run test
|
|
65
|
+
include:
|
|
66
|
+
- packages/*/**
|
|
67
|
+
exclude:
|
|
68
|
+
- "**/*.md"
|
|
69
|
+
allPackagesInputs:
|
|
70
|
+
- package.json
|
|
71
|
+
- bun.lock
|
|
72
|
+
- test-utils/**
|
|
73
|
+
concurrency: 4
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
change-versions test # 本地未提交变更及未跟踪文件
|
|
78
|
+
change-versions test --base origin/main # CI:共同祖先到 HEAD 的已提交变更
|
|
79
|
+
change-versions test --base origin/main --dry-run
|
|
80
|
+
change-versions test --all # 全量验证
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
默认测试变更包及其直接、间接依赖方,根配置或锁文件变化触发全量测试。测试选择与发布选择独立:测试文件可以触发测试,同时被 `publish.include/exclude` 排除,不会因此升版。`test: {}` 使用默认规则和 `npm run test`,缺少测试脚本的包显示跳过;自定义命令默认始终执行,可加 `--if-present` 跳过没有 `scripts.test` 的包;实际测试失败仍返回非零退出码。共享测试工具、传播范围与依赖构建约定见 [test 命令说明](docs/commands.md#test)。
|
|
47
84
|
|
|
48
85
|
## 本包发布准备
|
|
49
86
|
|
|
@@ -103,7 +140,7 @@ change-versions release patch --yes # 脚本/CI,不提问
|
|
|
103
140
|
|
|
104
141
|
合并后丢失自动识别标记时,可使用 `publish --commit=<版本提交>` 或 `release --commit=<版本提交>`,版本与内容校验仍然生效。失败引导中的命令也固定 commit。
|
|
105
142
|
|
|
106
|
-
实际操作使用仓库锁避免并发发布。升版仅暂存本次允许的文件;失败仅恢复仍属于本次写入的内容,外部文件与意外 hook 提交会保留。额外的 hook 生成文件通过 YAML 的 `commitFiles` 声明。配置仍兼容现有 YAML,无需迁移 JS 配置,也不增加 Bun 运行时依赖。完整规则见 [命令文档](docs/commands.md)。
|
|
143
|
+
实际操作使用仓库锁避免并发发布。升版仅暂存本次允许的文件;失败仅恢复仍属于本次写入的内容,外部文件与意外 hook 提交会保留。额外的 hook 生成文件通过 YAML 的 `publish.commitFiles` 声明。配置仍兼容现有 YAML,无需迁移 JS 配置,也不增加 Bun 运行时依赖。完整规则见 [命令文档](docs/commands.md)。
|
|
107
144
|
|
|
108
145
|
使用项目需要在包含这些命令的新版本发布后升级依赖,再将现有流程包装换成 release;1.2.1 不包含这些入口。构建通过既有 beforePublish 和发布上下文接入,增量构建策略继续由项目决定。
|
|
109
146
|
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`),
|
|
4
|
-
`)}function
|
|
2
|
+
var V=(e,t,n)=>()=>{if(e)try{t=e(e=0)}catch(s){n=[s]}if(n)throw n[0];return t};import{styleText as mn}from"node:util";function W(e,t,n){return mn(e,t,{stream:n})}function R(e){return W(["bold","cyan"],e,process.stdout)}function X(e){return W("bold",e,process.stdout)}function ce(e){return W("green",e,process.stdout)}function C(e){return W("dim",e,process.stdout)}function I(e){return W("cyan",e,process.stdout)}function pt(e,t,n){return`${X(e)} ${C(t)} ${C("=>")} ${ce(n)}`}function H(e){let[t,...n]=e.split(`
|
|
3
|
+
`),s=W("red",t,process.stderr);if(!n.length)return s;return[s,...n.map((o)=>W("yellow",o,process.stderr))].join(`
|
|
4
|
+
`)}function B(e){return W("green",e,process.stdout)}function S(e){return W("yellow",e,process.stdout)}function Ie(e){return W("red",e,process.stdout)}var j=()=>{};function tt(){let e=(process.env.LC_ALL||process.env.LC_MESSAGES||process.env.LANG||"").split(".")[0];return/^zh/i.test(e)?"zh":"en"}function i(e,t,n=tt()){let s=un[e][n];if(t)for(let[o,r]of Object.entries(t))s=s.replaceAll(`{${o}}`,String(r));return s}var un;var D=V(()=>{un={cmdTest:{zh:"按变更范围运行包测试",en:"Run tests for affected workspace packages"},configFieldsMoved:{zh:"配置结构已发生破坏性变更,请迁移字段: {fields}。旧字段不再支持。",en:"Breaking configuration change. Move these fields: {fields}. Legacy fields are no longer supported."},publishConfigInvalid:{zh:"publish 必须是包含 include 等发布配置的对象。",en:"publish must be an object containing include and other publication settings."},optTestIfPresent:{zh:"跳过没有 scripts.test 的包(也适用于自定义测试命令)",en:"Skip packages without scripts.test, including with a custom test command"},optTestBase:{zh:"比较指定提交与 HEAD 的共同祖先到 HEAD(仅已提交变更)",en:"Compare the merge-base of this revision and HEAD to HEAD (committed changes only)"},optTestAll:{zh:"运行所有包的测试",en:"Run tests for all packages"},optTestDryRun:{zh:"显示入选原因,不执行测试",en:"Show selection reasons without running tests"},optTestConcurrency:{zh:"最大并发包数(正整数)",en:"Maximum concurrent packages (positive integer)"},testConfigInvalid:{zh:"测试配置无效: {field}",en:"Invalid test configuration: {field}"},testConfigMissing:{zh:"请先在配置中声明 test: {},或填写 test 的筛选规则与命令。",en:"Declare test: {} in the configuration first, or configure test rules and a command."},testManifestInvalid:{zh:"无法解析测试包配置: {folder}/package.json",en:"Invalid test package manifest: {folder}/package.json"},testAllBaseConflict:{zh:"--all 不能与 --base 同时使用。",en:"--all cannot be combined with --base."},testRootRequired:{zh:"测试要求 --root 为 Git 仓库根目录,包目录必须位于仓库内。",en:"Tests require --root to be the Git repository root, with package directories inside it."},testBaseMissing:{zh:"无法找到 {base} 与 HEAD 的共同祖先,请补齐 Git 历史或使用 --all。",en:"No merge-base found between {base} and HEAD. Fetch the missing history or use --all."},testAllPackagesReason:{zh:"触发所有包: {file}",en:"input for all packages: {file}"},testDependencyReason:{zh:"依赖 {name}",en:"depends on {name}"},testPlan:{zh:"【测试计划】{count} 个包;比较起点: {base}",en:"[Test plan] {count} packages; comparison source: {base}"},testUnaffected:{zh:" {name}: 未受影响",en:" {name}: unaffected"},testNoScript:{zh:" {name}: 跳过(没有 scripts.test)",en:" {name}: skipped (no scripts.test)"},testDryRun:{zh:"预览完成,未执行测试。",en:"Preview complete; no tests executed."},testRunning:{zh:"运行 {name}: {command}",en:"Running {name}: {command}"},testPassed:{zh:"通过: {name}",en:"Passed: {name}"},testFailed:{zh:"失败: {name}",en:"Failed: {name}"},testSummary:{zh:"测试结果: {passed} 个通过,{failed} 个失败。",en:"Test results: {passed} passed, {failed} failed."},testInterrupted:{zh:"测试已中断。",en:"Tests interrupted."},testFailures:{zh:"测试失败: {names}",en:"Tests failed: {names}"},interactiveCancelled:{zh:"已取消,尚未执行的步骤不会继续。",en:"Cancelled. Remaining steps will not run."},confirmAnswer:{zh:"请输入 y 确认、n 返回,或 q 退出。",en:"Enter y to confirm, n to decline, or q to quit."},chooseReleaseKind:{zh:"选择版本类型",en:"Choose a release type"},releaseKindFormal:{zh:"正式版(发布到 latest)",en:"Formal release (default tag: latest)"},releaseKindPre:{zh:"预发布(例如 alpha、beta)",en:"Prerelease (for example alpha or beta)"},choosePreid:{zh:"预发布标识,回车采用默认值,q 退出:",en:"Prerelease identifier; Enter accepts the default, q quits:"},invalidPreid:{zh:"标识不符合 semver,例如可使用 alpha、beta、rc。请重新输入。",en:"Invalid semver prerelease identifier. Try alpha, beta or rc."},chooseDistTag:{zh:"目标 npm 标签,回车采用默认值,q 退出:",en:"Target npm tag; Enter accepts the default, q quits:"},firstBaseline:{zh:"尚未找到版本基线。请指定一条已存在的提交或 Git 标签作为本轮比较起点;q 退出。",en:"No release baseline found. Choose an existing commit or Git tag as the comparison start; q quits."},chooseBaseline:{zh:"首次版本基线(SHA 或 Git 标签):",en:"First release baseline (SHA or Git tag):"},wizardCandidates:{zh:"本次入选包(含依赖方):",en:"Packages to version (including dependents):"},wizardExecutionPlan:{zh:"执行前确认发布设置",en:"Review release settings before execution"},wizardPlanContext:{zh:"目标 npm 标签: {tag};推送分支: {branch}",en:"Target npm tag: {tag}; push branch: {branch}"},wizardNeedsConfig:{zh:"未找到 change-versions.yml,请先初始化配置。",en:"No change-versions.yml found. Initialize the configuration first."},wizardAction:{zh:"选择操作(输入序号,q 退出)",en:"Choose an action (enter a number, q quits)"},actionNewRelease:{zh:"发布新版本",en:"Release new versions"},actionResume:{zh:"恢复上一次发布(不升版)",en:"Resume publication (no version bump)"},actionStatus:{zh:"查看发布状态",en:"View publication status"},actionInit:{zh:"初始化配置",en:"Initialize configuration"},actionExit:{zh:"退出",en:"Exit"},actionRetry:{zh:"重试当前批次(不升版)",en:"Retry this batch (no version bump)"},actionRetryUnpublished:{zh:"使用当前代码重试未发布的包(保留原版本号)",en:"Retry unpublished packages using current code (no version bump)"},optRetryUnpublished:{zh:"记录已提交的修复代码,保留原版本号,仅补发未发布包",en:"Record committed fixes and retry unpublished packages without changing versions"},repairPublication:{zh:"【修复后补发】保留原版本号,检查未发布包与源码提交",en:"[Repair publication] Keep versions and check unpublished package sources"},repairPlan:{zh:"将创建一条空的恢复记录提交,不修改版本或文件。仅未发布包可采用当前已提交的代码;已发布包继续对应原源码提交。",en:"An empty recovery commit will record package sources without changing versions or files. Only unpublished packages may use current committed code; published packages retain their original sources."},repairCurrentCode:{zh:"(采用修复后的代码)",en:"(use repaired code)"},confirmRepairPublication:{zh:"是否记录修复后的源码提交并保留原版本号?",en:"Record repaired package sources without changing versions?"},repairPublishedChanged:{zh:"已发布或私有包发生改动: {name}@{version}。此入口仅允许未发布的公开包采用修复代码;请还原该包改动,或为它另发新版本。",en:"Already published package changed: {name}@{version} (or the package is private). Only unpublished public packages may use repaired sources; restore this package or release a new version."},repairRecorded:{zh:"修复补发记录已提交,所有版本号保持不变。后续可直接恢复发布。版本提交: {commit}",en:"Recovery sources recorded; all versions are unchanged. Ordinary publication can now resume. Version commit: {commit}"},actionExitFailed:{zh:"退出,保留失败状态",en:"Exit with failure status"},recoveryAction:{zh:"发布未完成,选择下一步",en:"Publication incomplete. Choose the next action"},pendingAction:{zh:"上一批尚未完成,选择下一步",en:"The previous batch is incomplete. Choose the next action"},optDryRunRelease:{zh:"预览升版或恢复发布计划,不执行修改",en:"Preview versioning or resumed publication without changes"},badDistTag:{zh:"dist-tag 必须是有效的 npm 标签,不能是版本范围或含空白等 URL 保留字符,例如 latest、alpha、next。",en:"dist-tag must be a valid npm tag, not a version range or a value containing whitespace or URL-reserved characters. Try latest, alpha or next."},distTagDowngrade:{zh:"拒绝回退 npm 标签 {tag}:{name} 当前指向 {current},本批次目标为 {version}。请核对批次或改用其他发布标签。",en:"Refusing to move {tag} backward for {name}: it currently points to {current}, but this batch targets {version}. Review the batch or choose another publication tag."},registryVersionDisappeared:{zh:"之前已存在的版本 {name}@{version} 已无法查询确认。已停止,不将标签修复自动转换为重新上传;请核对 registry 后恢复。",en:"Previously published version {name}@{version} can no longer be confirmed. Stopped without converting a tag repair into an upload; check the registry before resuming."},publishSourceDirty:{zh:"发布期间工作区被修改。已成功上传的包保留;请检查并清理变更后恢复发布。",en:"The working tree changed during publication. Successful uploads are preserved; review and clean the changes before resuming."},cmdRelease:{zh:"升版并发布,失败后可恢复发布",en:"Version and publish, with publication recovery"},cmdStatus:{zh:"只读查询发布批次的 registry 与 Git 标签状态",en:"Inspect registry and Git tag state without changing anything"},optReleaseCommit:{zh:"指定待发布版本提交(必须在当前历史中)",en:"Use this version commit (must be an ancestor of HEAD)"},optResume:{zh:"恢复上一批发布,不再次升版",en:"Resume publication without another version bump"},badReleaseCommit:{zh:"无效的版本提交 {commit}:需要当前历史中的提交,正文包含唯一且有效的包名 old => new 版本列表。请核对后重试。",en:"Invalid version commit {commit}: it must be in the current history with unique package old => new version lines. Review the commit before retrying."},repositoryLocked:{zh:`另一个发布操作持有仓库锁: {file}
|
|
5
5
|
持有者: {owner}
|
|
6
6
|
下一步:等待该进程结束;若进程已退出,确认没有其他发布操作后再删除该锁并重试。`,en:`Another release operation holds the repository lock: {file}
|
|
7
7
|
Owner: {owner}
|
|
@@ -13,7 +13,7 @@ For committed fixes to unpublished packages, append --retry-unpublished to keep
|
|
|
13
13
|
{detail}`,en:`git failed. Next: rerun this inside a git repository
|
|
14
14
|
{detail}`},noPackagesSince:{zh:"自 {rev} 以来没有需要升版的包",en:"No packages to version since {rev}"},changedHeader:{zh:"变更:",en:"Changed:"},dependentsHeader:{zh:"依赖方:",en:"Dependents:"},confirmVersions:{zh:"以上版本是否确认?",en:"Apply these versions?"},cancelledVersion:{zh:"已取消。还没改文件。想继续就再跑同一条命令",en:"Cancelled. No files were changed. Rerun the same command to continue"},branchPushFailed:{zh:`分支推送失败。commit 已在本地。下一步: git push origin {branch};成功后恢复 publish,不要重跑 version
|
|
15
15
|
{detail}`,en:`Branch push failed. The commit is local. Next: git push origin {branch}, then resume publish. Do not rerun version
|
|
16
|
-
{detail}`},noVersionCommit:{zh:"没有 version 提交。下一步: 尚未升版时先跑 version;若已升版,请先检查提交正文的 Co-authored-by: change-versions <publish@change-versions.com> 是否保留;也可用 publish --commit=<版本提交> 明确指定",en:"No version commit. Next: run version first if no bump exists; otherwise check that the version commit retains Co-authored-by: change-versions <publish@change-versions.com>, or use publish --commit=<version commit>"},packageNotInDir:{zh:"正文里的包不在 {dir}: {name}",en:"Package from the commit body is not in {dir}: {name}"},noGitTag:{zh:"不打 git tag",en:"No git tag"},willPublish:{zh:"即将执行(确认后开始):",en:"Publication plan (starts after confirmation):"},distTagOnlyMark:{zh:"(版本已存在,仅更新 npm 标签)",en:"(version exists; npm tag update only)"},privateMark:{zh:"(私有)",en:"(private)"},confirmPublish:{zh:"是否发布?",en:"Publish?"},cancelledPublish:{zh:"已取消。不跑 hook,不上传,不打 git tag。想继续时重跑同一条 publish,无需再次升版",en:"Cancelled. No hooks, no upload, no git tag. Rerun the same publish to continue without bumping versions"},publishInterrupted:{zh:"发布已中断,已完成的操作保留;registry 和 Git 标签可能部分完成。下一步: 重跑同一条 publish 确认状态,不要重跑 version",en:"Publish interrupted. Completed operations remain; registry and Git tags may be partially complete. Next: rerun the same publish to check state. Do not rerun version"},badBaseline:{zh:"修订无法解析: {rev}。下一步: 换成 git rev-parse 能解析的 SHA 或 tag",en:"Cannot resolve revision: {rev}. Next: pass a SHA or tag that git rev-parse accepts"},noBaseline:{zh:"没有基线。下一步: changed 或 version 加上 --baseline=SHA或tag",en:"No baseline. Next: rerun changed or version with --baseline=<sha-or-tag>"},chooseBump:{zh:"选择档位",en:"Select a bump"},versionRestored:{zh:"已尝试恢复本次拥有且未再次变化的文件。检查 git status 后重试",en:"Restored the tree to HEAD from when the command started. Fix the error, then rerun version"},tagPointsElsewhere:{zh:"git tag {tag} 指向别的 commit。下一步: 人工处理这个同名 tag 后,再重跑 publish。不要重跑 version",en:"git tag {tag} points at another commit. Next: fix that tag by hand, then rerun publish. Do not rerun version"},tagPushFailed:{zh:`git tag 没推上去。当前包已在 registry,其余包可能尚未发布。下一步: 检查 Git 远端权限后重跑同一条 publish,补齐该包标签并继续;配置的 hook 仍会执行,不要重跑 version
|
|
16
|
+
{detail}`},noVersionCommit:{zh:"没有 version 提交。下一步: 尚未升版时先跑 version;若已升版,请先检查提交正文的 Co-authored-by: change-versions <publish@change-versions.com> 是否保留;也可用 publish --commit=<版本提交> 明确指定",en:"No version commit. Next: run version first if no bump exists; otherwise check that the version commit retains Co-authored-by: change-versions <publish@change-versions.com>, or use publish --commit=<version commit>"},packageNotInDir:{zh:"正文里的包不在 {dir}: {name}",en:"Package from the commit body is not in {dir}: {name}"},noGitTag:{zh:"不打 git tag",en:"No git tag"},willPublish:{zh:"即将执行(确认后开始):",en:"Publication plan (starts after confirmation):"},distTagOnlyMark:{zh:"(版本已存在,仅更新 npm 标签)",en:"(version exists; npm tag update only)"},privateMark:{zh:"(私有)",en:"(private)"},privateVersionHint:{zh:"私有包仅升版,不上传,也不创建 Git tag。",en:"Private packages are versioned only; they are not uploaded and receive no Git tags."},confirmPublish:{zh:"是否发布?",en:"Publish?"},cancelledPublish:{zh:"已取消。不跑 hook,不上传,不打 git tag。想继续时重跑同一条 publish,无需再次升版",en:"Cancelled. No hooks, no upload, no git tag. Rerun the same publish to continue without bumping versions"},publishInterrupted:{zh:"发布已中断,已完成的操作保留;registry 和 Git 标签可能部分完成。下一步: 重跑同一条 publish 确认状态,不要重跑 version",en:"Publish interrupted. Completed operations remain; registry and Git tags may be partially complete. Next: rerun the same publish to check state. Do not rerun version"},badBaseline:{zh:"修订无法解析: {rev}。下一步: 换成 git rev-parse 能解析的 SHA 或 tag",en:"Cannot resolve revision: {rev}. Next: pass a SHA or tag that git rev-parse accepts"},noBaseline:{zh:"没有基线。下一步: changed 或 version 加上 --baseline=SHA或tag",en:"No baseline. Next: rerun changed or version with --baseline=<sha-or-tag>"},chooseBump:{zh:"选择档位",en:"Select a bump"},versionRestored:{zh:"已尝试恢复本次拥有且未再次变化的文件。检查 git status 后重试",en:"Restored the tree to HEAD from when the command started. Fix the error, then rerun version"},tagPointsElsewhere:{zh:"git tag {tag} 指向别的 commit。下一步: 人工处理这个同名 tag 后,再重跑 publish。不要重跑 version",en:"git tag {tag} points at another commit. Next: fix that tag by hand, then rerun publish. Do not rerun version"},tagPushFailed:{zh:`git tag 没推上去。当前包已在 registry,其余包可能尚未发布。下一步: 检查 Git 远端权限后重跑同一条 publish,补齐该包标签并继续;配置的 hook 仍会执行,不要重跑 version
|
|
17
17
|
{detail}`,en:`git tag was not pushed. This package is on the registry; other packages may still be pending. Next: check Git remote permissions, then rerun the same publish to finish its tag and continue; configured hooks still run. Do not rerun version
|
|
18
18
|
{detail}`},networkFailed:{zh:`操作失败,已重试 2 次。后续发布步骤已停止。下一步: 重跑同一条 publish,不要重跑 version
|
|
19
19
|
{detail}`,en:`Operation failed after 2 retries. Later publication steps were stopped. Next: rerun the same publish. Do not rerun version
|
|
@@ -23,16 +23,16 @@ For committed fixes to unpublished packages, append --retry-unpublished to keep
|
|
|
23
23
|
{detail}`,en:`Cannot read the npm registry:
|
|
24
24
|
{detail}`},npmFailed:{zh:"npm 失败",en:"npm failed"},npmViewFailed:{zh:"npm view 失败",en:"npm view failed"},hookFailed:{zh:`{name} 失败。下一步: 修好 hook 后重跑 publish。已上传的包及已创建的 Git 标签保留
|
|
25
25
|
{detail}`,en:`{name} failed. Next: fix the hook, then rerun publish. Uploaded packages and existing Git tags are preserved
|
|
26
|
-
{detail}`},beforePublishDirty:{zh:"beforePublish 改了被跟踪的文件。下一步: hook 只能改 git 忽略的构建产物,弄干净后重跑 publish",en:"beforePublish changed tracked files. Next: the hook may only change gitignored build output. Clean the tree, then rerun publish"},afterPublishDirty:{zh:"afterPublish 改了被跟踪的文件。已上传包和已创建的 Git 标签保留。下一步: 让 hook 不改被跟踪的文件,弄干净后重跑 publish",en:"afterPublish changed tracked files. Uploaded packages and existing Git tags are preserved. Next: keep the hook off tracked files, clean the tree, then rerun publish"},configUnreadable:{zh:"配置读不了: {file}。下一步: 补上 include,删掉不认识的字段",en:"Cannot read config: {file}. Next: add include and remove unknown fields"},packagesDirMissing:{zh:"找不到目录 {dir}",en:"Directory not found: {dir}"},packageJsonIncomplete:{zh:"{folder} 的 package.json 缺少 name 或 version",en:"{folder} package.json is missing name or version"},publicDependsOnPrivate:{zh:"public 包 {name} 不能依赖 private 包 {dep}。下一步: 改依赖后重跑 version",en:"Public package {name} cannot depend on private package {dep}. Next: change the dependency, then rerun version"},missingVersionField:{zh:"{folder} 缺少 version",en:"{folder} is missing version"},badBump:{zh:"档位非法: {bump}。可用 {list}",en:"Invalid bump: {bump}. Use {list}"},preidRequired:{zh:"{kind} 必须带 --preid。预发布用 prepatch 这类档位",en:"{kind} requires --preid. Use a pre* bump for prereleases"},preidForbidden:{zh:"正式版不能带 --preid。预发布用 prepatch 这类档位",en:"A stable bump cannot take --preid. Use a pre* bump for prereleases"},badLine:{zh:"第 {line} 行无法解析",en:"Cannot parse line {line}"},unknownField:{zh:"不认识的字段 {name}",en:"Unknown field {name}"},missingValue:{zh:"{name} 缺少值",en:"{name} is missing a value"},includeRequired:{zh:"include 不能为空。下一步: 补上 include,删掉不认识的字段",en:"include must not be empty. Next: add include and remove unknown fields"},commitMessageNeedsToken:{zh:"commitMessage 需要 %s",en:"commitMessage must contain %s"},patternBang:{zh:"模式不能包含 !: {pattern}",en:"Patterns cannot contain !: {pattern}"},patternClass:{zh:"不支持字符组: {pattern}",en:"Character classes are not supported: {pattern}"},badHook:{zh:"hooks 无法解析: {line}",en:"Cannot parse hooks: {line}"},unknownHook:{zh:"不认识的 hook {name}",en:"Unknown hook {name}"},hookMissingCommand:{zh:"{name} 缺少命令",en:"{name} is missing a command"},badPatternLine:{zh:"路径列表无法解析: {line}",en:"Cannot parse path list: {line}"},badSemver:{zh:"无法解析版本 {version}",en:"Cannot parse version {version}"},dependencyCycle:{zh:"依赖成环: {name}",en:"Dependency cycle: {name}"},bumpRequired:{zh:"非交互环境或 --yes 模式必须指定档位,例如 release patch --yes;交互式发布请运行 release。",en:"A bump is required when not interactive or using --yes. Use release patch --yes, or run release interactively."},chooseIndex:{zh:"请输入序号(q 退出): ",en:"Enter a number (q to quit): "},invalidChoice:{zh:"无效选择: {answer}",en:"Invalid choice: {answer}"},emptyAnswer:{zh:"(空)",en:"(empty)"},yesRequired:{zh:"非交互环境需要 --yes",en:"Pass --yes when not interactive"},commandFailed:{zh:"{command} 失败: {detail}",en:"{command} failed: {detail}"},commandFailedShort:{zh:"{command} 失败",en:"{command} failed"},commandExit:{zh:"{command} 退出 {code}",en:"{command} exited {code}"},cliDescription:{zh:"只给需要发的包升版本,再按那次提交发布",en:"Version only the packages that need a release, then publish that commit"},optRoot:{zh:"仓库根目录",en:"Repository root"},optVersionsFile:{zh:"配置文件",en:"Config file"},optPackages:{zh:"覆盖 packagesDir",en:"Override packagesDir"},cmdChanged:{zh:"列出会升版的包",en:"List packages that would be versioned"},optBaseline:{zh:"这一次的比较起点,SHA 或 tag",en:"Comparison start for this run, SHA or tag"},cmdVersion:{zh:"升版本并提交",en:"Bump versions and commit"},argBump:{zh:"patch、minor、major、prerelease、prepatch、preminor、premajor",en:"patch, minor, major, prerelease, prepatch, preminor, or premajor"},optPreid:{zh:"预发布标识,pre* 必填",en:"Prerelease identifier, required for pre*"},optYes:{zh:"自动执行,不补问缺失参数",en:"Run without prompts; supply required options"},optDryRunVersion:{zh:"只打印旧版本到新版本",en:"Print old and new versions only"},optBranch:{zh:"覆盖配置里的 branch",en:"Override branch from the config"},optCommitMessage:{zh:"覆盖 commitMessage",en:"Override commitMessage"},cmdPublish:{zh:"发布最近一次 version 提交里的包",en:"Publish packages listed in the latest version commit"},optDistTag:{zh:"npm dist-tag",en:"npm dist-tag"},optDryRunPublish:{zh:"查询 registry 并预览计划;不执行 hook、上传、标签修改或推送",en:"Query the registry and preview the plan without hooks, uploads, tag changes or pushes"},cmdInit:{zh:"写入带注释的配置",en:"Write a commented config file"},optYesInit:{zh:"不提问,按探测到的目录写默认配置",en:"Write defaults from the detected directory, without prompts"},configExists:{zh:"已有 {file}。下一步: 直接改这个文件,或删掉后重跑 init",en:"Config already exists: {file}. Next: edit that file, or delete it and rerun init"},initIntro:{zh:"写入 {file}",en:"Write {file}"},initFoundDirs:{zh:"发现包目录: {dirs}",en:"Package directories: {dirs}"},initPackagesDir:{zh:"包目录",en:"Package directory"},initInclude:{zh:"源码路径,逗号分隔",en:"Source paths, comma-separated"},initExclude:{zh:"排除路径,逗号分隔",en:"Excluded paths, comma-separated"},initBranch:{zh:"推送分支,回车跳过",en:"Branch to push, Enter to skip"},initBeforeCommit:{zh:"beforeCommit,回车跳过",en:"beforeCommit, Enter to skip"},initBeforePublish:{zh:"beforePublish,回车跳过",en:"beforePublish, Enter to skip"},initWrote:{zh:"已写入 {file}",en:"Wrote {file}"},initNotePackagesDir:{zh:"包所在目录。这次可用 --packages 覆盖",en:"Directory of packages. Override once with --packages"},initNoteCommitMessage:{zh:"%s 换成档位。有 --preid 时再空一格接上,例如 bump: prerelease alpha。查找看正文末尾的 Co-authored-by: change-versions <publish@change-versions.com>,不改提交作者",en:"%s becomes the bump. With --preid, a space and the id follow, for example bump: prerelease alpha. Lookup uses the Co-authored-by: change-versions <publish@change-versions.com> trailer. The commit author stays unchanged"},initNoteBranch:{zh:"不写就不推送。version 只推这个分支,不推 tag",en:"Omit to skip pushing. version pushes this branch only, not tags"},initNoteBeforeCommit:{zh:"版本和 CHANGELOG 写完之后、git commit 之前。失败仅恢复本次拥有的文件",en:"After versions and CHANGELOG are written, before git commit. Failure restores only files owned by this operation"},initNoteBeforePublish:{zh:"确认之后、npm publish 之前。只能改 git 忽略的构建产物",en:"After confirm, before npm publish. May only change gitignored build output"},initNoteAfterPublish:{zh:"要发的包都已在 registry 上之后。不能把工作区弄脏",en:"After every selected package is on the registry. Must leave the tree clean"},initNoteInclude:{zh:"相对仓库根。* 和 ? 不跨过 /,** 可以。命中目录时,目录里的文件也算",en:"Relative to the repo root. * and ? do not cross /, ** may. A matching directory includes files under it"},initNoteExclude:{zh:"命中这里的文件不触发升版。package.json 另比,去掉 devDependencies 后有差异才算",en:"Files that match here do not trigger a bump. package.json is compared separately, after dropping devDependencies"}}});import lt from"picomatch";import Te from"semver";import{YAMLParseError as sn,parse as an}from"yaml";function Xe(e){return dt.has(e)}function De(e,t){if(!mt.includes(e))throw new l(i("badBump",{bump:e,list:mt.join(", ")}));let n=e;if(cn.has(n)&&!t)throw new l(i("preidRequired",{kind:n}));if(dt.has(n)&&t)throw new l(i("preidForbidden"));return n}function Fe(e){let t=gn(e);for(let d of Object.keys(t))if(!ln.has(d))throw new l(i("unknownField",{name:d}));let n=t.packagesDir===void 0?"packages":Qe(t.packagesDir,"packagesDir"),r=t.commitMessage===void 0?"bump: %s":Qe(t.commitMessage,"commitMessage"),s=t.branch===void 0?void 0:Qe(t.branch,"branch"),o=t.hooks==null?{}:un(t.hooks),a=t.include==null?[]:Ye(t.include,"include"),c=t.exclude==null?[]:Ye(t.exclude,"exclude");if(!a.length)throw new l(i("includeRequired"));if(!r.includes("%s"))throw new l(i("commitMessageNeedsToken"));for(let d of[...a,...c]){if(d.includes("!"))throw new l(i("patternBang",{pattern:d}));if(/\[[^\]]+\]/.test(d))throw new l(i("patternClass",{pattern:d}))}let m=t.commitFiles==null?void 0:Ye(t.commitFiles,"commitFiles");return{packagesDir:n,commitMessage:r,branch:s,hooks:o,include:a,exclude:c,...m?{commitFiles:m}:{}}}function gn(e){try{let t=an(e);if(t==null)return{};if(!pt(t))throw new l(i("badLine",{line:1}));return t}catch(t){if(t instanceof l)throw t;if(t instanceof sn)throw new l(i("badLine",{line:t.linePos?.[0]?.line??1}));throw t}}function un(e){if(!pt(e))throw new l(i("badHook",{line:"hooks"}));let t={};for(let[n,r]of Object.entries(e)){if(!mn.has(n))throw new l(i("unknownHook",{name:n}));if(typeof r!=="string"||!r)throw new l(i("hookMissingCommand",{name:n}));t[n]=r}return t}function Ye(e,t){if(!Array.isArray(e))throw new l(i("badPatternLine",{line:t}));return e.map((n)=>{if(typeof n!=="string"||!n)throw new l(i("badPatternLine",{line:t}));return n})}function Qe(e,t){if(typeof e!=="string"||!e)throw new l(i("missingValue",{name:t}));return e}function pt(e){return Boolean(e)&&typeof e==="object"&&!Array.isArray(e)}function gt(e,t){let n={dot:!0};if(lt(t,n)(e))return!0;if(t.endsWith("/**"))return!1;return lt(`${t}/**`,n)(e)}function Ze(e,t,n){return t.some((r)=>gt(e,r))&&!n.some((r)=>gt(e,r))}function ht(e,t,n){let r=new Set(n),s=`${t}/`,o=new Map;for(let a of e.split(`
|
|
27
|
-
`)){if(!a.startsWith(
|
|
28
|
-
`)){let r=/^(\S+) (\S+) => (\S+)$/.exec(n.trim());if(r)t.push({name:r[1],from:r[2],to:r[3]})}return t}function Pt(e){let t=new Map(e.map((o)=>[o.folder,o])),n=new Set,r=[],s=(o,a)=>{if(n.has(o.folder))return;if(a.has(o.folder))throw new l(i("dependencyCycle",{name:o.name}));a.add(o.folder);for(let c of o.deps){let m=t.get(c);if(m)s(m,a)}a.delete(o.folder),n.add(o.folder),r.push(o)};for(let o of e)s(o,new Set);return r}function ye(e){return/ENEEDAUTH|E401|E403|need auth|not logged in|401 Unauthorized|403 Forbidden/i.test(e)}function ve(e){try{return JSON.parse(e)?.error?.code==="E404"}catch{return!1}}var l,mt,dt,cn,ln,mn,dn="publish@change-versions.com",pn,ue;var N=G(()=>{D();l=class l extends Error{auth;constructor(e,t=!1){super(e);this.name="Fail",this.auth=t}};mt=["patch","minor","major","prerelease","prepatch","preminor","premajor"],dt=new Set(["patch","minor","major"]),cn=new Set(["prerelease","prepatch","preminor","premajor"]);ln=new Set(["packagesDir","commitMessage","branch","hooks","include","exclude","commitFiles"]),mn=new Set(["beforeCommit","beforePublish","afterPublish"]);pn=`change-versions <${dn}>`,ue=`Co-authored-by: ${pn}`});import{spawn as xt}from"node:child_process";function P(e,t,n){return new Promise((r)=>{let s=xt(e,t,{cwd:n,stdio:["ignore","pipe","pipe"]}),o="",a="";s.stdout.on("data",(c)=>{o+=c}),s.stderr.on("data",(c)=>{a+=c}),s.on("error",(c)=>r({code:1,stdout:o,stderr:`${a}${c.message}`})),s.on("close",(c)=>r({code:c??1,stdout:o,stderr:a}))})}async function y(e,t,n){let r=await P(e,t,n);if(r.code!==0){let s=(r.stderr||r.stdout).trim();throw new l(s?i("commandFailed",{command:e,detail:s}):i("commandFailedShort",{command:e}))}return r.stdout}function Ee(e,t,n,r){return new Promise((s,o)=>{let a=xt(e,t,{cwd:n,stdio:"inherit",env:{...process.env,...r}});a.on("error",o),a.on("exit",(c)=>{if(c===0)s();else o(new l(i("commandExit",{command:e,code:c??1})))})})}var W=G(()=>{D();N()});function Ct(e){let t=e.packages.map((m)=>m.folder),n=new Map(e.packages.map((m)=>[m.folder,m])),r=new Map(e.packages.map((m)=>[m.name,m])),s=new Map(e.packages.map((m)=>[m.name,m.folder])),{changed:o}=ft(t,e.filesByFolder,e.include,e.exclude,(m)=>e.devOnly.has(m),e.packagesDir),a=yt(t,o,(m)=>hn(n.get(m),s));for(let m of a){let d=n.get(m);if(!d||d.private)continue;for(let h of Object.keys(d.dependencies??{}))if(r.get(h)?.private)throw new l(i("publicDependsOnPrivate",{name:d.name,dep:h}))}let c=new Set(o);return{changed:o,dependents:a.filter((m)=>!c.has(m))}}function hn(e,t){if(!e)return[];return[...Object.keys(e.dependencies??{}),...Object.keys(e.devDependencies??{})].flatMap((r)=>{let s=t.get(r);return s?[s]:[]})}var St=G(()=>{D();N()});import pe from"node:fs";import tt from"node:path";import fn from"node:os";import{randomUUID as bn}from"node:crypto";import{AsyncLocalStorage as wn}from"node:async_hooks";async function X(e,t,n){if(t)return n();e=tt.resolve(e);let r=tt.resolve(e,(await y("git",["rev-parse","--git-common-dir"],e)).trim()),s=tt.join(r,"change-versions.lock");if(nt.getStore()?.has(s))return n();let o={pid:process.pid,host:fn.hostname(),token:bn()},a;try{a=pe.openSync(s,"wx",384)}catch(d){if(d.code!=="EEXIST")throw d;let h="";try{h=pe.readFileSync(s,"utf8")}catch{}throw new l(i("repositoryLocked",{file:s,owner:h.trim()}))}let c=()=>{try{if(JSON.parse(pe.readFileSync(s,"utf8")).token===o.token)pe.unlinkSync(s)}catch{}};try{pe.writeFileSync(a,JSON.stringify(o))}finally{pe.closeSync(a)}let m=()=>{if(process.listenerCount("SIGINT")<=1&&process.listenerCount("SIGTERM")<=1)process.exit(1)};process.on("exit",c),process.on("SIGINT",m),process.on("SIGTERM",m);try{return await nt.run(new Set([...nt.getStore()??[],s]),n)}finally{process.off("SIGINT",m),process.off("SIGTERM",m),process.off("exit",c),c()}}var nt;var ke=G(()=>{W();N();D();nt=new wn});function vn(e){if(/^[a-zA-Z0-9_./:@=+-]+$/.test(e))return e;return"'"+e.replaceAll("'","'\\''")+"'"}function O(e){let t=["change-versions","publish",`--dist-tag=${e.distTag}`,`--root=${e.root}`,`--versions-file=${e.versionsFile}`];if(e.commit)t.push(`--commit=${e.commit}`);if(e.packages)t.push(`--packages=${e.packages}`);if(e.retryUnpublished)t.push("--retry-unpublished");return t.map(vn).join(" ")}import kn from"node:readline/promises";import{stdin as Rt,stdout as zt}from"node:process";async function it(e){let t=kn.createInterface({input:Rt,output:zt}),n=new AbortController,r=()=>n.abort();t.once("SIGINT",r),t.once("close",r);try{return await t.question(e,{signal:n.signal})}catch(s){if(n.signal.aborted)throw new J;throw s}finally{t.off("SIGINT",r),t.off("close",r),t.close()}}function T(){return Boolean(Rt.isTTY&&zt.isTTY)}async function Tt(e,t){if(!T())throw new l(i("bumpRequired"));console.log(S(e)),t.forEach((n,r)=>{let s=n.tone?Pn[n.tone](n.label):n.label;console.log(` ${F(String(r+1))}) ${s}`)});while(!0){let n=(await it(F(i("chooseIndex")))).trim();if(n.toLowerCase()==="q")throw new J;let r=/^\d+$/.test(n)?Number(n)-1:-1;if(Number.isInteger(r)&&r>=0&&r<t.length)return r;console.log(v(i("invalidChoice",{answer:n||i("emptyAnswer")})))}}async function $t(e,t){return t[await Tt(e,t.map((n)=>({label:n})))]}async function Z(e,t){return t[await Tt(e,t)].value}async function L(e,t=""){if(!T())throw new l(i("yesRequired"));let n=t?` ${M(`[${t}]`)}`:"",r=(await it(`${S(e)}${n} `)).trim();if(r.toLowerCase()==="q")throw new J;return r||t}async function se(e){if(!T())throw new l(i("yesRequired"));console.log(S(e));while(!0){let t=(await it(`${v("[y/N/q]")} `)).trim().toLowerCase();if(t==="q")throw new J;if(t==="y"||t==="yes")return!0;if(!t||t==="n"||t==="no")return!1;console.log(v(i("confirmAnswer")))}}var J,Pn;var _=G(()=>{I();D();N();J=class J extends l{constructor(){super(i("interactiveCancelled"))}};Pn={positive:M,caution:v,muted:C}});function Dt(e){let t=[];if(e.beforePublish)t.push({kind:"hook",name:"beforePublish",command:e.beforePublish});for(let n of Pt(e.packages.filter((r)=>!r.private))){let r=n.registry??"";if(!n.published||n.distTagVersion!==n.version)t.push(n.published?{kind:"dist-tag",name:n.name,version:n.version,folder:n.folder,registry:r,tag:e.distTag}:{kind:"publish",name:n.name,version:n.version,folder:n.folder,registry:r,tag:e.distTag});if(e.formal)t.push({kind:"tag",name:n.name,version:n.version,commit:n.commit??e.commit})}if(e.afterPublish)t.push({kind:"hook",name:"afterPublish",command:e.afterPublish});return t}var Ft=G(()=>{N()});import Ie from"node:path";import je from"semver";async function Bt(e){return X(e.root,Boolean(e.dryRun),()=>xn(e))}async function xn(e){console.log(e.dryRun?v(i("publishPreview")):S(i("publishPreparing")));let{root:t,startHead:n,config:r,release:s,entries:o,formal:a,registryOf:c,published:m,distTagVersions:d,steps:h}=await ae(e),x=O({...e,root:t,commit:s.hash}),f={CHANGE_VERSIONS_PUBLISH_CONTEXT:JSON.stringify({schemaVersion:1,root:t,packagesDir:r.packagesDir,commit:s.hash,distTag:e.distTag,packages:h.flatMap((g)=>g.kind==="publish"?[{name:g.name,version:g.version,path:Ie.join(r.packagesDir,g.folder)}]:[])})};for(let g of o)if(m.has(g.folder)&&d.get(g.folder)===g.to)console.log(C(i("alreadyTagged",{name:g.name,version:g.to,tag:e.distTag,registry:c.get(g.folder)||""})));if(console.log(i("publishPlanSummary",{uploads:h.filter((g)=>g.kind==="publish").length,retags:h.filter((g)=>g.kind==="dist-tag").length,tags:h.filter((g)=>g.kind==="tag").length})),e.dryRun){rt({steps:h,formal:a,config:r,entries:o},x);return}console.log(C(i("publishResumeInfo"))),console.log(S(i("willPublish")));for(let g of o){if(m.has(g.folder)&&d.get(g.folder)===g.to)continue;let E=g.pkg.private?` ${C(i("privateMark"))}`:m.has(g.folder)?` ${C(i("distTagOnlyMark"))}`:"";console.log(` ${Q(g.name)} ${oe(g.to)}${E}`)}if(!e.yes&&!await se(i("confirmPublish")))throw new l(i("cancelledPublish"));let u=new Map(o.map((g)=>[g.folder,g.pkg.private?"summaryPrivate":m.has(g.folder)&&d.get(g.folder)===g.to?"summarySkipped":"summaryPending"])),w="beforePublish",p,k=!1,b=(g)=>g==="summaryPublished"||g==="summaryRetagged"?M:g==="summaryFailed"?ze:g==="summaryPending"?v:C,R=()=>{console.log((k?M:ze)(i(k?"publishComplete":"publishIncomplete"))),console.log(i("publishSummaryContext",{commit:s.hash,tag:e.distTag}));let g=new Map;for(let E of o){let z=u.get(E.folder);g.set(z,(g.get(z)??0)+1);let re=c.get(E.folder);console.log(` ${b(z)(i(z))}: ${Q(E.name)}@${Q(E.to)}${re?` — ${C(re)}`:""}`)}if(console.log([...g].map(([E,z])=>`${b(E)(i(E))}: ${Q(String(z))}`).join(", ")),k)console.log(M(i("publishReady"))),console.log(C(i(h.some((E)=>E.kind==="tag")?r.branch?"gitTagsPushed":"gitTagsLocal":"noGitTag")));else{if(console.log(ze(i("publishFailedStage",{stage:w}))),w==="git tag / git push")console.log(v(i("publishTagRecovery")));console.log(v(i("publishRetryTitle"))),console.log(S(i("publishRetry"))),console.log(C(i("publishResumeInfo"))),console.log(F(x))}},B=()=>{if(p)u.set(p,"summaryFailed");R(),console.error(H(i("publishInterrupted"))),process.exit(1)};process.on("SIGINT",B),process.on("SIGTERM",B);try{await At(t,r.hooks.beforePublish,"beforePublish",f),await Pe(t,i("beforePublishDirty")),await Ae(t,n);for(let g of h){if(g.kind==="tag"){w="git tag / git push",console.log(S(i("executingStep",{step:Be(g)}))),await Cn(t,r.branch,[{name:g.name,to:g.version,commit:g.commit}]);continue}if(g.kind!=="publish"&&g.kind!=="dist-tag")continue;let E=Ie.join(t,r.packagesDir,g.folder);w=`${g.kind==="publish"?"npm publish":"npm dist-tag add"} ${g.name}@${g.version}`,p=g.folder;let z="summaryPending";await It(async()=>{let re=await jt(t,g.name,g.version,g.registry);if(!re&&g.kind==="dist-tag")throw new l(i("registryVersionDisappeared",{name:g.name,version:g.version}));let at=await Mt(t,g.name,g.tag,g.registry,!re);if(Ht(g.name,g.version,at,g.tag),await Pe(t,i("publishSourceDirty")),await Ae(t,n),re&&at===g.version)z="summarySkipped",console.log(C(i("alreadyTagged",{name:g.name,version:g.version,tag:g.tag,registry:g.registry})));else if(re)w=`npm dist-tag add ${g.name}@${g.version}`,console.log(S(i("executingStep",{step:Be({...g,kind:"dist-tag"})}))),await Nt(t,["dist-tag","add",`${g.name}@${g.version}`,g.tag,`--registry=${g.registry}`],g.registry),z="summaryRetagged";else w=`npm publish ${g.name}@${g.version}`,console.log(S(i("executingStep",{step:Be({...g,kind:"publish"})}))),await Nt(E,["publish",`--tag=${g.tag}`,`--registry=${g.registry}`],g.registry),z="summaryPublished"}),u.set(g.folder,z),p=void 0,await Pe(t,i("publishSourceDirty")),await Ae(t,n)}w="afterPublish",await At(t,r.hooks.afterPublish,"afterPublish",f),await Pe(t,i("afterPublishDirty")),await Ae(t,n),k=!0}catch(g){if(p)u.set(p,"summaryFailed");throw g}finally{process.off("SIGINT",B),process.off("SIGTERM",B),R()}}async function ae(e,t=!1,n=!1){let r=Ie.resolve(e.root);if(!t)await V(r);let s=(await y("git",["rev-parse","HEAD"],r)).trim(),o=he(r,e.versionsFile);if(e.packages)o.packagesDir=e.packages;let a=await j(r,e.commit);if(!a?.lines.length)throw new l(i("noVersionCommit"));let c=ee(r,o.packagesDir),m=new Map(c.map((b)=>[A(r,o.packagesDir,b).name,b])),d=a.lines.map((b)=>{let R=m.get(b.name);if(!R)throw new l(i("packageNotInDir",{dir:o.packagesDir,name:b.name}));let B=A(r,o.packagesDir,R);if(B.version!==b.to)throw new l(i("releaseVersionMismatch",{name:b.name,expected:b.to,actual:B.version,commit:a.hash}));return{...b,folder:R,pkg:B,commit:a.sourceCommits.get(b.name)??a.hash,deps:He(B,m)}}),h=new Set;for(let b of d){let R=await P("git",["diff","--quiet",b.commit,s,"--",`${o.packagesDir}/${b.folder}`],r);if(R.code===1)h.add(b.folder);if(R.code!==0&&R.code!==1)throw new l(i("gitFailed",{detail:R.stderr.trim()}))}if(h.size&&!n)throw new l(i("releaseContentsChanged"));if(!t)console.log(i("publishSummaryContext",{commit:a.hash,tag:e.distTag}));let x=Ne(a.lines);if(x)await Me(r,d.filter((b)=>!b.pkg.private));let f=new Map;for(let b of d){if(b.pkg.private)continue;let R=await Rn(r,b.pkg);if(f.set(b.folder,R),!t)console.log(i("checkingRegistry",{name:b.name,version:b.to,registry:R}))}let u=new Set;for(let b of d){let R=f.get(b.folder);if(!R||u.has(R))continue;if(u.add(R),!t)await Sn(r,R)}let w=new Set,p=new Map;for(let b of d){if(b.pkg.private)continue;let R=f.get(b.folder);if(!R)continue;await It(async()=>{let B=await jt(r,b.name,b.to,R);if(B)w.add(b.folder);let g=await Mt(r,b.name,e.distTag,R,!B);if(!t)Ht(b.name,b.to,g,e.distTag);p.set(b.folder,g)})}let k=Dt({packages:d.map((b)=>({name:b.name,version:b.to,folder:b.folder,private:b.pkg.private,deps:b.deps,registry:f.get(b.folder),published:w.has(b.folder),distTagVersion:p.get(b.folder),commit:b.commit})),distTag:e.distTag,formal:x,commit:a.hash,beforePublish:o.hooks.beforePublish,afterPublish:o.hooks.afterPublish});return{root:r,startHead:s,config:o,release:a,entries:d,formal:x,registryOf:f,published:w,distTagVersions:p,steps:k,changed:h}}function rt(e,t){for(let n of e.steps)if(console.log(`${v(i("plannedStep",{step:""}))}${Be(n)}`),n.kind==="tag"&&e.config.branch)console.log(v(i("plannedStep",{step:`git push origin refs/tags/${n.name}@${n.version}`})));if(!e.formal)console.log(C(i("noGitTag")));console.log(v(i("publishPreviewComplete"))),console.log(S(i("publishPreviewNext"))),console.log(F(t))}function Be(e){if(e.kind==="hook")return`${C(`${e.name}:`)} ${F(e.command)}`;if(e.kind==="tag")return F(`git tag ${e.name}@${e.version} ${e.commit}`);if(e.kind==="dist-tag")return F(`npm dist-tag add ${e.name}@${e.version} ${e.tag} --registry=${e.registry}`);return F(`npm publish --tag=${e.tag} --registry=${e.registry} ${e.name}@${e.version}`)}async function j(e,t){if((await P("git",["rev-parse","--verify","HEAD"],e)).code!==0)return null;let r;if(t){let c=await P("git",["rev-parse","--verify","--end-of-options",`${t}^{commit}`],e);if(c.code!==0)throw new l(i("badReleaseCommit",{commit:t}));let m=c.stdout.trim();if((await P("git",["merge-base","--is-ancestor",m,"HEAD"],e)).code!==0)throw new l(i("badReleaseCommit",{commit:t}));r={hash:m,subject:(await y("git",["show","-s","--format=%s",m],e)).trim(),body:await y("git",["show","-s","--format=%b",m],e)}}else r=(await fe(e))[0];if(!r)return null;let s=de(r.body);if(!s.length){if(t)throw new l(i("badReleaseCommit",{commit:t}));return null}if(new Set(s.map((c)=>c.name)).size!==s.length||s.some((c)=>!je.valid(c.from)||!je.valid(c.to)))throw new l(i("badReleaseCommit",{commit:r.hash}));let o=/^Change-Versions-Dist-Tag: (\S+)$/m.exec(r.body)?.[1],a=new Map;for(let c of r.body.split(`
|
|
29
|
-
`).filter((
|
|
30
|
-
`).filter(Boolean).map((n)=>{let[
|
|
26
|
+
{detail}`},beforePublishDirty:{zh:"beforePublish 改了被跟踪的文件。下一步: hook 只能改 git 忽略的构建产物,弄干净后重跑 publish",en:"beforePublish changed tracked files. Next: the hook may only change gitignored build output. Clean the tree, then rerun publish"},afterPublishDirty:{zh:"afterPublish 改了被跟踪的文件。已上传包和已创建的 Git 标签保留。下一步: 让 hook 不改被跟踪的文件,弄干净后重跑 publish",en:"afterPublish changed tracked files. Uploaded packages and existing Git tags are preserved. Next: keep the hook off tracked files, clean the tree, then rerun publish"},configUnreadable:{zh:"配置读不了: {file}。下一步: 检查配置文件路径与 publish.include",en:"Cannot read config: {file}. Next: check the file path and publish.include"},packagesDirMissing:{zh:"找不到目录 {dir}",en:"Directory not found: {dir}"},packageJsonIncomplete:{zh:"{folder} 的 package.json 缺少 name 或 version",en:"{folder} package.json is missing name or version"},publicDependsOnPrivate:{zh:"public 包 {name} 不能依赖 private 包 {dep}。下一步: 改依赖后重跑 version",en:"Public package {name} cannot depend on private package {dep}. Next: change the dependency, then rerun version"},missingVersionField:{zh:"{folder} 缺少 version",en:"{folder} is missing version"},badBump:{zh:"档位非法: {bump}。可用 {list}",en:"Invalid bump: {bump}. Use {list}"},preidRequired:{zh:"{kind} 必须带 --preid。预发布用 prepatch 这类档位",en:"{kind} requires --preid. Use a pre* bump for prereleases"},preidForbidden:{zh:"正式版不能带 --preid。预发布用 prepatch 这类档位",en:"A stable bump cannot take --preid. Use a pre* bump for prereleases"},badLine:{zh:"第 {line} 行无法解析",en:"Cannot parse line {line}"},unknownField:{zh:"不认识的字段 {name}",en:"Unknown field {name}"},missingValue:{zh:"{name} 缺少值",en:"{name} is missing a value"},includeRequired:{zh:"publish.include 不能为空。请在 publish 下声明发布文件路径。",en:"publish.include must not be empty. Declare publication input paths under publish."},commitMessageNeedsToken:{zh:"publish.commitMessage 需要 %s",en:"publish.commitMessage must contain %s"},patternBang:{zh:"模式不能包含 !: {pattern}",en:"Patterns cannot contain !: {pattern}"},patternClass:{zh:"不支持字符组: {pattern}",en:"Character classes are not supported: {pattern}"},badHook:{zh:"hooks 无法解析: {line}",en:"Cannot parse hooks: {line}"},unknownHook:{zh:"不认识的 hook {name}",en:"Unknown hook {name}"},hookMissingCommand:{zh:"{name} 缺少命令",en:"{name} is missing a command"},badPatternLine:{zh:"路径列表无法解析: {line}",en:"Cannot parse path list: {line}"},badSemver:{zh:"无法解析版本 {version}",en:"Cannot parse version {version}"},dependencyCycle:{zh:"依赖成环: {name}",en:"Dependency cycle: {name}"},bumpRequired:{zh:"非交互环境或 --yes 模式必须指定档位,例如 release patch --yes;交互式发布请运行 release。",en:"A bump is required when not interactive or using --yes. Use release patch --yes, or run release interactively."},chooseIndex:{zh:"请输入序号(q 退出): ",en:"Enter a number (q to quit): "},invalidChoice:{zh:"无效选择: {answer}",en:"Invalid choice: {answer}"},emptyAnswer:{zh:"(空)",en:"(empty)"},yesRequired:{zh:"非交互环境需要 --yes",en:"Pass --yes when not interactive"},commandFailed:{zh:"{command} 失败: {detail}",en:"{command} failed: {detail}"},commandFailedShort:{zh:"{command} 失败",en:"{command} failed"},commandExit:{zh:"{command} 退出 {code}",en:"{command} exited {code}"},cliDescription:{zh:"只给需要发的包升版本,再按那次提交发布",en:"Version only the packages that need a release, then publish that commit"},optRoot:{zh:"仓库根目录",en:"Repository root"},optVersionsFile:{zh:"配置文件",en:"Config file"},optPackages:{zh:"覆盖 packagesDir",en:"Override packagesDir"},cmdChanged:{zh:"列出会升版的包",en:"List packages that would be versioned"},optBaseline:{zh:"这一次的比较起点,SHA 或 tag",en:"Comparison start for this run, SHA or tag"},cmdVersion:{zh:"升版本并提交",en:"Bump versions and commit"},argBump:{zh:"patch、minor、major、prerelease、prepatch、preminor、premajor",en:"patch, minor, major, prerelease, prepatch, preminor, or premajor"},optPreid:{zh:"预发布标识,pre* 必填",en:"Prerelease identifier, required for pre*"},optYes:{zh:"自动执行,不补问缺失参数",en:"Run without prompts; supply required options"},optDryRunVersion:{zh:"只打印旧版本到新版本",en:"Print old and new versions only"},optBranch:{zh:"覆盖配置里的 branch",en:"Override branch from the config"},optCommitMessage:{zh:"覆盖 commitMessage",en:"Override commitMessage"},cmdPublish:{zh:"发布最近一次 version 提交里的包",en:"Publish packages listed in the latest version commit"},optDistTag:{zh:"npm dist-tag",en:"npm dist-tag"},optDryRunPublish:{zh:"查询 registry 并预览计划;不执行 hook、上传、标签修改或推送",en:"Query the registry and preview the plan without hooks, uploads, tag changes or pushes"},cmdInit:{zh:"写入带注释的配置",en:"Write a commented config file"},optYesInit:{zh:"不提问,按探测到的目录写默认配置",en:"Write defaults from the detected directory, without prompts"},configExists:{zh:"已有 {file}。下一步: 直接改这个文件,或删掉后重跑 init",en:"Config already exists: {file}. Next: edit that file, or delete it and rerun init"},initIntro:{zh:"写入 {file}",en:"Write {file}"},initFoundDirs:{zh:"发现包目录: {dirs}",en:"Package directories: {dirs}"},initPackagesDir:{zh:"包目录",en:"Package directory"},initInclude:{zh:"发布输入路径(publish.include),逗号分隔",en:"Publication input paths (publish.include), comma-separated"},initExclude:{zh:"发布排除路径(publish.exclude),逗号分隔",en:"Publication excluded paths (publish.exclude), comma-separated"},initBranch:{zh:"发布推送分支(publish.branch),回车跳过",en:"Publication branch (publish.branch), Enter to skip"},initBeforeCommit:{zh:"publish.hooks.beforeCommit,回车跳过",en:"publish.hooks.beforeCommit, Enter to skip"},initBeforePublish:{zh:"publish.hooks.beforePublish,回车跳过",en:"publish.hooks.beforePublish, Enter to skip"},initWrote:{zh:"已写入 {file}",en:"Wrote {file}"},initNotePackagesDir:{zh:"包所在目录。这次可用 --packages 覆盖",en:"Directory of packages. Override once with --packages"},initNoteCommitMessage:{zh:"%s 换成档位。有 --preid 时再空一格接上,例如 bump: prerelease alpha。查找看正文末尾的 Co-authored-by: change-versions <publish@change-versions.com>,不改提交作者",en:"%s becomes the bump. With --preid, a space and the id follow, for example bump: prerelease alpha. Lookup uses the Co-authored-by: change-versions <publish@change-versions.com> trailer. The commit author stays unchanged"},initNoteBranch:{zh:"不写就不推送。version 只推这个分支,不推 tag",en:"Omit to skip pushing. version pushes this branch only, not tags"},initNoteBeforeCommit:{zh:"版本和 CHANGELOG 写完之后、git commit 之前。失败仅恢复本次拥有的文件",en:"After versions and CHANGELOG are written, before git commit. Failure restores only files owned by this operation"},initNoteBeforePublish:{zh:"确认之后、npm publish 之前。只能改 git 忽略的构建产物",en:"After confirm, before npm publish. May only change gitignored build output"},initNoteAfterPublish:{zh:"要发的包都已在 registry 上之后。不能把工作区弄脏",en:"After every selected package is on the registry. Must leave the tree clean"},initNoteInclude:{zh:"相对仓库根。* 和 ? 不跨过 /,** 可以。命中目录时,目录里的文件也算",en:"Relative to the repo root. * and ? do not cross /, ** may. A matching directory includes files under it"},initNoteExclude:{zh:"命中这里的文件不触发升版。package.json 另比,去掉 devDependencies 后有差异才算",en:"Files that match here do not trigger a bump. package.json is compared separately, after dropping devDependencies"}}});import ht from"picomatch";import Ee from"semver";import{YAMLParseError as dn,parse as pn}from"yaml";function it(e){return wt.has(e)}function je(e,t){if(!ft.includes(e))throw new l(i("badBump",{bump:e,list:ft.join(", ")}));let n=e;if(hn.has(n)&&!t)throw new l(i("preidRequired",{kind:n}));if(wt.has(n)&&t)throw new l(i("preidForbidden"));return n}function Me(e,t){let n=vn(e),s=Object.keys(n).filter((c)=>yt.has(c));if(Pe(n.test)&&"global"in n.test)s.push("test.global");if(s.length)throw new l(i("configFieldsMoved",{fields:s.map((c)=>c==="test.global"?"test.global → test.allPackagesInputs":`${c} → publish.${c}`).join(", ")}));for(let c of Object.keys(n))if(!fn.has(c))throw new l(i("unknownField",{name:c}));let o=t??(n.packagesDir===void 0?"packages":nt(n.packagesDir,"packagesDir")),r=wn(n.publish),a=n.test===void 0?void 0:yn(n.test,o);return{packagesDir:o,publish:r,...a?{test:a}:{}}}function wn(e){if(e===void 0)throw new l(i("includeRequired"));if(!Pe(e))throw new l(i("publishConfigInvalid"));let t=e;for(let g of Object.keys(t))if(!yt.has(g))throw new l(i("unknownField",{name:`publish.${g}`}));let n=t.commitMessage===void 0?"bump: %s":nt(t.commitMessage,"publish.commitMessage"),s=t.branch===void 0?void 0:nt(t.branch,"publish.branch"),o=t.hooks==null?{}:kn(t.hooks),r=t.include==null?[]:Ne(t.include,"publish.include"),a=t.exclude==null?[]:Ne(t.exclude,"publish.exclude");if(!r.length)throw new l(i("includeRequired"));if(!n.includes("%s"))throw new l(i("commitMessageNeedsToken"));for(let g of[...r,...a]){if(g.includes("!"))throw new l(i("patternBang",{pattern:g}));if(/\[[^\]]+\]/.test(g))throw new l(i("patternClass",{pattern:g}))}let c=t.commitFiles==null?void 0:Ne(t.commitFiles,"publish.commitFiles");return{commitMessage:n,branch:s,hooks:o,include:r,exclude:a,...c?{commitFiles:c}:{}}}function yn(e,t){if(!Pe(e))throw new l(i("testConfigInvalid",{field:"test"}));if("global"in e)throw new l(i("configFieldsMoved",{fields:"test.global → test.allPackagesInputs"}));let n=new Set(["command","include","exclude","allPackagesInputs","dependentInputs","dependents","concurrency"]);for(let a of Object.keys(e))if(!n.has(a))throw new l(i("unknownField",{name:`test.${a}`}));let s=(a,c)=>{let g=e[a]===void 0?c:Ne(e[a],`test.${a}`);for(let d of g){if(d.includes("!"))throw new l(i("patternBang",{pattern:d}));if(/\[[^\]]+\]/.test(d))throw new l(i("patternClass",{pattern:d}))}return g},o=s("include",[`${t}/*/**`]);if(e.command!==void 0&&(typeof e.command!=="string"||!e.command.trim()))throw new l(i("testConfigInvalid",{field:"test.command"}));if(e.dependents!==void 0&&typeof e.dependents!=="boolean")throw new l(i("testConfigInvalid",{field:"test.dependents"}));let r=e.concurrency===void 0?4:e.concurrency;if(typeof r!=="number"||!Number.isSafeInteger(r)||r<1)throw new l(i("testConfigInvalid",{field:"test.concurrency"}));return{...e.command===void 0?{}:{command:e.command},include:o,exclude:s("exclude",[]),allPackagesInputs:s("allPackagesInputs",["package.json","bun.lock","bun.lockb","package-lock.json","npm-shrinkwrap.json","yarn.lock","pnpm-lock.yaml","pnpm-workspace.yaml","tsconfig.json"]),dependentInputs:s("dependentInputs",o),dependents:e.dependents===void 0?!0:e.dependents,concurrency:r}}function vn(e){try{let t=pn(e);if(t==null)return{};if(!Pe(t))throw new l(i("badLine",{line:1}));return t}catch(t){if(t instanceof l)throw t;if(t instanceof dn)throw new l(i("badLine",{line:t.linePos?.[0]?.line??1}));throw t}}function kn(e){if(!Pe(e))throw new l(i("badHook",{line:"hooks"}));let t={};for(let[n,s]of Object.entries(e)){if(!bn.has(n))throw new l(i("unknownHook",{name:n}));if(typeof s!=="string"||!s)throw new l(i("hookMissingCommand",{name:n}));t[n]=s}return t}function Ne(e,t){if(!Array.isArray(e))throw new l(i("badPatternLine",{line:t}));return e.map((n)=>{if(typeof n!=="string"||!n)throw new l(i("badPatternLine",{line:t}));return n})}function nt(e,t){if(typeof e!=="string"||!e)throw new l(i("missingValue",{name:t}));return e}function Pe(e){return Boolean(e)&&typeof e==="object"&&!Array.isArray(e)}function ke(e,t){let n={dot:!0,flags:"s"};if(ht(t,n)(e))return!0;if(t.endsWith("/**"))return!1;return ht(`${t}/**`,n)(e)}function xe(e,t,n){return t.some((s)=>ke(e,s))&&!n.some((s)=>ke(e,s))}function vt(e,t,n){let s=new Set(n),o=`${t}/`,r=new Map;for(let a of e.split(`
|
|
27
|
+
`)){if(!a.startsWith(o))continue;let c=a.slice(o.length),g=c.split("/")[0];if(!g||!c.includes("/")||!s.has(g))continue;let d=r.get(g)??[];d.push(a),r.set(g,d)}return r}function kt(e,t,n,s,o,r){let a=[],c=[];for(let g of e){let d=t.get(g);if(!d?.length){c.push(g);continue}let f=`${r}/${g}/package.json`;if(d.some((h)=>h!==f&&xe(h,n,s))||d.includes(f)&&!o(g))a.push(g);else c.push(g)}return{changed:a,ignored:c}}function Pt(e,t){return JSON.stringify(Ae(bt(e)))===JSON.stringify(Ae(bt(t)))}function bt(e){if(!e||typeof e!=="object"||Array.isArray(e))return e;let t={...e};return delete t.devDependencies,t}function Ae(e){if(Array.isArray(e))return e.map(Ae);if(e&&typeof e==="object"){let t=e;return Object.fromEntries(Object.keys(t).sort().map((n)=>[n,Ae(t[n])]))}return e}function xt(e,t,n){let s=n?Ee.inc(e,t,n):Ee.inc(e,t);if(!s)throw new l(i("badSemver",{version:e}));return s}function Ct(e,t,n){let s=new Set(t),o=new Map(e.map((a)=>[a,[]]));for(let a of e)for(let c of n(a))o.get(c)?.push(a);let r=[...t];while(r.length){let a=r.pop();if(!a)break;for(let c of o.get(a)??[]){if(s.has(c))continue;s.add(c),r.push(c)}}return e.filter((a)=>s.has(a))}function St(e,t,n){let s=e.replace("%s",t);return n?`${s} ${n}`:s}function Be(e){return e.length>0&&e.every((t)=>Boolean(Ee.valid(t.to))&&Ee.prerelease(t.to)==null)}function st(e){return/^v\d/.test(e)}function Rt(e){return/^v\d+\.\d+\.\d+$/.test(e)}function he(e){let t=[];for(let n of e.split(`
|
|
28
|
+
`)){let s=/^(\S+) (\S+) => (\S+)$/.exec(n.trim());if(s)t.push({name:s[1],from:s[2],to:s[3]})}return t}function zt(e){let t=new Map(e.map((r)=>[r.folder,r])),n=new Set,s=[],o=(r,a)=>{if(n.has(r.folder))return;if(a.has(r.folder))throw new l(i("dependencyCycle",{name:r.name}));a.add(r.folder);for(let c of r.deps){let g=t.get(c);if(g)o(g,a)}a.delete(r.folder),n.add(r.folder),s.push(r)};for(let r of e)o(r,new Set);return s}function Ce(e){return/ENEEDAUTH|E401|E403|need auth|not logged in|401 Unauthorized|403 Forbidden/i.test(e)}function Se(e){try{return JSON.parse(e)?.error?.code==="E404"}catch{return!1}}var l,ft,wt,hn,fn,yt,bn,Pn="publish@change-versions.com",xn,pe;var N=V(()=>{D();l=class l extends Error{auth;constructor(e,t=!1){super(e);this.name="Fail",this.auth=t}};ft=["patch","minor","major","prerelease","prepatch","preminor","premajor"],wt=new Set(["patch","minor","major"]),hn=new Set(["prerelease","prepatch","preminor","premajor"]);fn=new Set(["packagesDir","publish","test"]),yt=new Set(["commitMessage","branch","hooks","include","exclude","commitFiles"]),bn=new Set(["beforeCommit","beforePublish","afterPublish"]);xn=`change-versions <${Pn}>`,pe=`Co-authored-by: ${xn}`});import{spawn as Tt}from"node:child_process";function x(e,t,n){return new Promise((s)=>{let o=Tt(e,t,{cwd:n,stdio:["ignore","pipe","pipe"]}),r="",a="";o.stdout.on("data",(c)=>{r+=c}),o.stderr.on("data",(c)=>{a+=c}),o.on("error",(c)=>s({code:1,stdout:r,stderr:`${a}${c.message}`})),o.on("close",(c)=>s({code:c??1,stdout:r,stderr:a}))})}async function v(e,t,n){let s=await x(e,t,n);if(s.code!==0){let o=(s.stderr||s.stdout).trim();throw new l(o?i("commandFailed",{command:e,detail:o}):i("commandFailedShort",{command:e}))}return s.stdout}function He(e,t,n,s){return new Promise((o,r)=>{let a=Tt(e,t,{cwd:n,stdio:"inherit",env:{...process.env,...s}});a.on("error",r),a.on("exit",(c)=>{if(c===0)o();else r(new l(i("commandExit",{command:e,code:c??1})))})})}var L=V(()=>{D();N()});function $t(e){let t=e.packages.map((g)=>g.folder),n=new Map(e.packages.map((g)=>[g.folder,g])),s=new Map(e.packages.map((g)=>[g.name,g])),o=new Map(e.packages.map((g)=>[g.name,g.folder])),{changed:r}=kt(t,e.filesByFolder,e.include,e.exclude,(g)=>e.devOnly.has(g),e.packagesDir),a=Ct(t,r,(g)=>Cn(n.get(g),o));for(let g of a){let d=n.get(g);if(!d||d.private)continue;for(let f of Object.keys(d.dependencies??{}))if(s.get(f)?.private)throw new l(i("publicDependsOnPrivate",{name:d.name,dep:f}))}let c=new Set(r);return{changed:r,dependents:a.filter((g)=>!c.has(g))}}function Cn(e,t){if(!e)return[];return[...Object.keys(e.dependencies??{}),...Object.keys(e.devDependencies??{})].flatMap((s)=>{let o=t.get(s);return o?[o]:[]})}var Dt=V(()=>{D();N()});import fe from"node:fs";import rt from"node:path";import Sn from"node:os";import{randomUUID as Rn}from"node:crypto";import{AsyncLocalStorage as zn}from"node:async_hooks";async function Z(e,t,n){if(t)return n();e=rt.resolve(e);let s=rt.resolve(e,(await v("git",["rev-parse","--git-common-dir"],e)).trim()),o=rt.join(s,"change-versions.lock");if(ot.getStore()?.has(o))return n();let r={pid:process.pid,host:Sn.hostname(),token:Rn()},a;try{a=fe.openSync(o,"wx",384)}catch(d){if(d.code!=="EEXIST")throw d;let f="";try{f=fe.readFileSync(o,"utf8")}catch{}throw new l(i("repositoryLocked",{file:o,owner:f.trim()}))}let c=()=>{try{if(JSON.parse(fe.readFileSync(o,"utf8")).token===r.token)fe.unlinkSync(o)}catch{}};try{fe.writeFileSync(a,JSON.stringify(r))}finally{fe.closeSync(a)}let g=()=>{if(process.listenerCount("SIGINT")<=1&&process.listenerCount("SIGTERM")<=1)process.exit(1)};process.on("exit",c),process.on("SIGINT",g),process.on("SIGTERM",g);try{return await ot.run(new Set([...ot.getStore()??[],o]),n)}finally{process.off("SIGINT",g),process.off("SIGTERM",g),process.off("exit",c),c()}}var ot;var Re=V(()=>{L();N();D();ot=new zn});function $n(e){if(/^[a-zA-Z0-9_./:@=+-]+$/.test(e))return e;return"'"+e.replaceAll("'","'\\''")+"'"}function O(e){let t=["change-versions","publish",`--dist-tag=${e.distTag}`,`--root=${e.root}`,`--versions-file=${e.versionsFile}`];if(e.commit)t.push(`--commit=${e.commit}`);if(e.packages)t.push(`--packages=${e.packages}`);if(e.retryUnpublished)t.push("--retry-unpublished");return t.map($n).join(" ")}import Dn from"node:readline/promises";import{stdin as Ft,stdout as It}from"node:process";async function at(e){let t=Dn.createInterface({input:Ft,output:It}),n=new AbortController,s=()=>n.abort();t.once("SIGINT",s),t.once("close",s);try{return await t.question(e,{signal:n.signal})}catch(o){if(n.signal.aborted)throw new J;throw o}finally{t.off("SIGINT",s),t.off("close",s),t.close()}}function F(){return Boolean(Ft.isTTY&&It.isTTY)}async function Nt(e,t){if(!F())throw new l(i("bumpRequired"));console.log(R(e)),t.forEach((n,s)=>{let o=n.tone?Fn[n.tone](n.label):n.label;console.log(` ${I(String(s+1))}) ${o}`)});while(!0){let n=(await at(I(i("chooseIndex")))).trim();if(n.toLowerCase()==="q")throw new J;let s=/^\d+$/.test(n)?Number(n)-1:-1;if(Number.isInteger(s)&&s>=0&&s<t.length)return s;console.log(S(i("invalidChoice",{answer:n||i("emptyAnswer")})))}}async function Et(e,t){return t[await Nt(e,t.map((n)=>({label:n})))]}async function ee(e,t){return t[await Nt(e,t)].value}async function q(e,t=""){if(!F())throw new l(i("yesRequired"));let n=t?` ${B(`[${t}]`)}`:"",s=(await at(`${R(e)}${n} `)).trim();if(s.toLowerCase()==="q")throw new J;return s||t}async function le(e){if(!F())throw new l(i("yesRequired"));console.log(R(e));while(!0){let t=(await at(`${S("[y/N/q]")} `)).trim().toLowerCase();if(t==="q")throw new J;if(t==="y"||t==="yes")return!0;if(!t||t==="n"||t==="no")return!1;console.log(S(i("confirmAnswer")))}}var J,Fn;var _=V(()=>{j();D();N();J=class J extends l{constructor(){super(i("interactiveCancelled"))}};Fn={positive:B,caution:S,muted:C}});function At(e){let t=[];if(e.beforePublish)t.push({kind:"hook",name:"beforePublish",command:e.beforePublish});for(let n of zt(e.packages.filter((s)=>!s.private))){let s=n.registry??"";if(!n.published||n.distTagVersion!==n.version)t.push(n.published?{kind:"dist-tag",name:n.name,version:n.version,folder:n.folder,registry:s,tag:e.distTag}:{kind:"publish",name:n.name,version:n.version,folder:n.folder,registry:s,tag:e.distTag});if(e.formal)t.push({kind:"tag",name:n.name,version:n.version,commit:n.commit??e.commit})}if(e.afterPublish)t.push({kind:"hook",name:"afterPublish",command:e.afterPublish});return t}var jt=V(()=>{N()});import Ve from"node:path";import Le from"semver";async function Ot(e){return Z(e.root,Boolean(e.dryRun),()=>In(e))}async function In(e){console.log(e.dryRun?S(i("publishPreview")):R(i("publishPreparing")));let{root:t,startHead:n,config:s,release:o,entries:r,formal:a,registryOf:c,published:g,distTagVersions:d,steps:f}=await ge(e),k=O({...e,root:t,commit:o.hash}),h={CHANGE_VERSIONS_PUBLISH_CONTEXT:JSON.stringify({schemaVersion:1,root:t,packagesDir:s.packagesDir,commit:o.hash,distTag:e.distTag,packages:f.flatMap((u)=>u.kind==="publish"?[{name:u.name,version:u.version,path:Ve.join(s.packagesDir,u.folder)}]:[])})};for(let u of r)if(g.has(u.folder)&&d.get(u.folder)===u.to)console.log(C(i("alreadyTagged",{name:u.name,version:u.to,tag:e.distTag,registry:c.get(u.folder)||""})));if(console.log(i("publishPlanSummary",{uploads:f.filter((u)=>u.kind==="publish").length,retags:f.filter((u)=>u.kind==="dist-tag").length,tags:f.filter((u)=>u.kind==="tag").length})),e.dryRun){ct({steps:f,formal:a,config:s,entries:r},k);return}console.log(C(i("publishResumeInfo"))),console.log(R(i("willPublish")));for(let u of r){if(g.has(u.folder)&&d.get(u.folder)===u.to)continue;let E=u.pkg.private?` ${C(i("privateMark"))}`:g.has(u.folder)?` ${C(i("distTagOnlyMark"))}`:"";console.log(` ${X(u.name)} ${ce(u.to)}${E}`)}if(!e.yes&&!await le(i("confirmPublish")))throw new l(i("cancelledPublish"));let m=new Map(r.map((u)=>[u.folder,u.pkg.private?"summaryPrivate":g.has(u.folder)&&d.get(u.folder)===u.to?"summarySkipped":"summaryPending"])),b="beforePublish",p,y=!1,w=(u)=>u==="summaryPublished"||u==="summaryRetagged"?B:u==="summaryFailed"?Ie:u==="summaryPending"?S:C,P=()=>{console.log((y?B:Ie)(i(y?"publishComplete":"publishIncomplete"))),console.log(i("publishSummaryContext",{commit:o.hash,tag:e.distTag}));let u=new Map;for(let E of r){let T=m.get(E.folder);u.set(T,(u.get(T)??0)+1);let ae=c.get(E.folder);console.log(` ${w(T)(i(T))}: ${X(E.name)}@${X(E.to)}${ae?` — ${C(ae)}`:""}`)}if(console.log([...u].map(([E,T])=>`${w(E)(i(E))}: ${X(String(T))}`).join(", ")),y)console.log(B(i("publishReady"))),console.log(C(i(f.some((E)=>E.kind==="tag")?s.publish.branch?"gitTagsPushed":"gitTagsLocal":"noGitTag")));else{if(console.log(Ie(i("publishFailedStage",{stage:b}))),b==="git tag / git push")console.log(S(i("publishTagRecovery")));console.log(S(i("publishRetryTitle"))),console.log(R(i("publishRetry"))),console.log(C(i("publishResumeInfo"))),console.log(I(k))}},z=()=>{if(p)m.set(p,"summaryFailed");P(),console.error(H(i("publishInterrupted"))),process.exit(1)};process.on("SIGINT",z),process.on("SIGTERM",z);try{await Ht(t,s.publish.hooks.beforePublish,"beforePublish",h),await ze(t,i("beforePublishDirty")),await Oe(t,n);for(let u of f){if(u.kind==="tag"){b="git tag / git push",console.log(R(i("executingStep",{step:Ge(u)}))),await Nn(t,s.publish.branch,[{name:u.name,to:u.version,commit:u.commit}]);continue}if(u.kind!=="publish"&&u.kind!=="dist-tag")continue;let E=Ve.join(t,s.packagesDir,u.folder);b=`${u.kind==="publish"?"npm publish":"npm dist-tag add"} ${u.name}@${u.version}`,p=u.folder;let T="summaryPending";await Gt(async()=>{let ae=await Vt(t,u.name,u.version,u.registry);if(!ae&&u.kind==="dist-tag")throw new l(i("registryVersionDisappeared",{name:u.name,version:u.version}));let dt=await Lt(t,u.name,u.tag,u.registry,!ae);if(qt(u.name,u.version,dt,u.tag),await ze(t,i("publishSourceDirty")),await Oe(t,n),ae&&dt===u.version)T="summarySkipped",console.log(C(i("alreadyTagged",{name:u.name,version:u.version,tag:u.tag,registry:u.registry})));else if(ae)b=`npm dist-tag add ${u.name}@${u.version}`,console.log(R(i("executingStep",{step:Ge({...u,kind:"dist-tag"})}))),await Mt(t,["dist-tag","add",`${u.name}@${u.version}`,u.tag,`--registry=${u.registry}`],u.registry),T="summaryRetagged";else b=`npm publish ${u.name}@${u.version}`,console.log(R(i("executingStep",{step:Ge({...u,kind:"publish"})}))),await Mt(E,["publish",`--tag=${u.tag}`,`--registry=${u.registry}`],u.registry),T="summaryPublished"}),m.set(u.folder,T),p=void 0,await ze(t,i("publishSourceDirty")),await Oe(t,n)}b="afterPublish",await Ht(t,s.publish.hooks.afterPublish,"afterPublish",h),await ze(t,i("afterPublishDirty")),await Oe(t,n),y=!0}catch(u){if(p)m.set(p,"summaryFailed");throw u}finally{process.off("SIGINT",z),process.off("SIGTERM",z),P()}}async function ge(e,t=!1,n=!1){let s=Ve.resolve(e.root);if(!t)await G(s);let o=(await v("git",["rev-parse","HEAD"],s)).trim(),r=te(s,e.versionsFile);if(e.packages)r.packagesDir=e.packages;let a=await M(s,e.commit);if(!a?.lines.length)throw new l(i("noVersionCommit"));let c=K(s,r.packagesDir),g=new Map(c.map((w)=>[A(s,r.packagesDir,w).name,w])),d=a.lines.map((w)=>{let P=g.get(w.name);if(!P)throw new l(i("packageNotInDir",{dir:r.packagesDir,name:w.name}));let z=A(s,r.packagesDir,P);if(z.version!==w.to)throw new l(i("releaseVersionMismatch",{name:w.name,expected:w.to,actual:z.version,commit:a.hash}));return{...w,folder:P,pkg:z,commit:a.sourceCommits.get(w.name)??a.hash,deps:Ue(z,g)}}),f=new Set;for(let w of d){let P=await x("git",["diff","--quiet",w.commit,o,"--",`${r.packagesDir}/${w.folder}`],s);if(P.code===1)f.add(w.folder);if(P.code!==0&&P.code!==1)throw new l(i("gitFailed",{detail:P.stderr.trim()}))}if(f.size&&!n)throw new l(i("releaseContentsChanged"));if(!t)console.log(i("publishSummaryContext",{commit:a.hash,tag:e.distTag}));let k=Be(a.lines);if(k)await qe(s,d.filter((w)=>!w.pkg.private));let h=new Map;for(let w of d){if(w.pkg.private)continue;let P=await An(s,w.pkg);if(h.set(w.folder,P),!t)console.log(i("checkingRegistry",{name:w.name,version:w.to,registry:P}))}let m=new Set;for(let w of d){let P=h.get(w.folder);if(!P||m.has(P))continue;if(m.add(P),!t)await En(s,P)}let b=new Set,p=new Map;for(let w of d){if(w.pkg.private)continue;let P=h.get(w.folder);if(!P)continue;await Gt(async()=>{let z=await Vt(s,w.name,w.to,P);if(z)b.add(w.folder);let u=await Lt(s,w.name,e.distTag,P,!z);if(!t)qt(w.name,w.to,u,e.distTag);p.set(w.folder,u)})}let y=At({packages:d.map((w)=>({name:w.name,version:w.to,folder:w.folder,private:w.pkg.private,deps:w.deps,registry:h.get(w.folder),published:b.has(w.folder),distTagVersion:p.get(w.folder),commit:w.commit})),distTag:e.distTag,formal:k,commit:a.hash,beforePublish:r.publish.hooks.beforePublish,afterPublish:r.publish.hooks.afterPublish});return{root:s,startHead:o,config:r,release:a,entries:d,formal:k,registryOf:h,published:b,distTagVersions:p,steps:y,changed:f}}function ct(e,t){for(let n of e.steps)if(console.log(`${S(i("plannedStep",{step:""}))}${Ge(n)}`),n.kind==="tag"&&e.config.publish.branch)console.log(S(i("plannedStep",{step:`git push origin refs/tags/${n.name}@${n.version}`})));if(!e.formal)console.log(C(i("noGitTag")));console.log(S(i("publishPreviewComplete"))),console.log(R(i("publishPreviewNext"))),console.log(I(t))}function Ge(e){if(e.kind==="hook")return`${C(`${e.name}:`)} ${I(e.command)}`;if(e.kind==="tag")return I(`git tag ${e.name}@${e.version} ${e.commit}`);if(e.kind==="dist-tag")return I(`npm dist-tag add ${e.name}@${e.version} ${e.tag} --registry=${e.registry}`);return I(`npm publish --tag=${e.tag} --registry=${e.registry} ${e.name}@${e.version}`)}async function M(e,t){if((await x("git",["rev-parse","--verify","HEAD"],e)).code!==0)return null;let s;if(t){let c=await x("git",["rev-parse","--verify","--end-of-options",`${t}^{commit}`],e);if(c.code!==0)throw new l(i("badReleaseCommit",{commit:t}));let g=c.stdout.trim();if((await x("git",["merge-base","--is-ancestor",g,"HEAD"],e)).code!==0)throw new l(i("badReleaseCommit",{commit:t}));s={hash:g,subject:(await v("git",["show","-s","--format=%s",g],e)).trim(),body:await v("git",["show","-s","--format=%b",g],e)}}else s=(await be(e))[0];if(!s)return null;let o=he(s.body);if(!o.length){if(t)throw new l(i("badReleaseCommit",{commit:t}));return null}if(new Set(o.map((c)=>c.name)).size!==o.length||o.some((c)=>!Le.valid(c.from)||!Le.valid(c.to)))throw new l(i("badReleaseCommit",{commit:s.hash}));let r=/^Change-Versions-Dist-Tag: (\S+)$/m.exec(s.body)?.[1],a=new Map;for(let c of s.body.split(`
|
|
29
|
+
`).filter((g)=>g.startsWith("Change-Versions-Source:"))){let g=/^Change-Versions-Source: (\S+) ([a-f0-9]{40}|[a-f0-9]{64})$/.exec(c);if(!g||a.has(g[1])||!o.some((d)=>d.name===g[1]))throw new l(i("badReleaseCommit",{commit:s.hash}));a.set(g[1],g[2])}if(a.size&&a.size!==o.length)throw new l(i("badReleaseCommit",{commit:s.hash}));for(let c of new Set(a.values()))if((await x("git",["merge-base","--is-ancestor",c,s.hash],e)).code!==0)throw new l(i("badReleaseCommit",{commit:s.hash}));return{hash:s.hash,subject:s.subject,lines:o,distTag:r,sourceCommits:a}}async function Nn(e,t,n){await qe(e,n);let s=n.map((r)=>`${r.name}@${r.to}`);for(let r of n){let a=`${r.name}@${r.to}`;if((await x("git",["rev-parse","--verify",`refs/tags/${a}^{commit}`],e)).code===0)continue;await v("git",["tag",a,r.commit],e)}if(!t)return;let o=await x("git",["push","origin",...s.map((r)=>`refs/tags/${r}`)],e);if(o.code!==0)throw new l(i("tagPushFailed",{detail:o.stderr.trim()}))}async function qe(e,t){for(let n of t){let s=`${n.name}@${n.to}`,o=await x("git",["rev-parse","--verify",`refs/tags/${s}^{commit}`],e);if(o.code===0&&o.stdout.trim()!==n.commit)throw new l(i("tagPointsElsewhere",{tag:s}))}}async function Oe(e,t){if((await v("git",["rev-parse","HEAD"],e)).trim()!==t)throw new l(i("publishHeadChanged"))}async function Gt(e){let t="";for(let n=0;n<3;n++)try{await e();return}catch(s){if(s instanceof l)throw s;t=s instanceof Error?s.message:String(s)}throw new l(i("networkFailed",{detail:t}))}async function Mt(e,t,n){let s=await x("npm",t,e);if(s.code===0)return;let o=`${s.stderr}${s.stdout}`;if(process.stderr.write(s.stderr),Ce(o))throw new l(i("notLoggedIn",{registry:n}),!0);throw Error(o.trim()||i("npmFailed"))}async function Vt(e,t,n,s){let o=await x("npm",["view",`${t}@${n}`,"version","--json",`--registry=${s}`],e);if(o.code===0){let a;try{a=JSON.parse(o.stdout)}catch{}if(a===n||Array.isArray(a)&&a.length===1&&a[0]===n)return!0;throw Error(i("unexpectedRegistryVersion",{name:t,version:n,registry:s}))}let r=`${o.stderr}${o.stdout}`;if(Ce(r))throw new l(i("notLoggedIn",{registry:s}),!0);if(Se(o.stdout)||Se(o.stderr))return!1;throw Error(r.trim()||i("npmViewFailed"))}async function Lt(e,t,n,s,o=!1){let r=await x("npm",["view",t,"dist-tags","--json",`--registry=${s}`],e);if(r.code!==0){let g=`${r.stderr}${r.stdout}`.trim();if(Ce(g))throw new l(i("notLoggedIn",{registry:s}),!0);if(o&&(Se(r.stdout)||Se(r.stderr)))return;throw Error(g||i("npmViewFailed"))}let a;try{a=JSON.parse(r.stdout)}catch{}if(Array.isArray(a)&&a.length===1)a=a[0];if(!a||typeof a!=="object"||Array.isArray(a))throw Error(i("unexpectedRegistryTags",{name:t,tag:n,registry:s}));let c=Object.prototype.hasOwnProperty.call(a,n)?a[n]:void 0;if(c!==void 0&&(typeof c!=="string"||!Le.valid(c)))throw Error(i("unexpectedRegistryTags",{name:t,tag:n,registry:s}));return c}function qt(e,t,n,s){if(n&&Le.gt(n,t))throw new l(i("distTagDowngrade",{name:e,version:t,current:n,tag:s}))}async function En(e,t){let n=await x("npm",["whoami",`--registry=${t}`],e);if(n.code===0)return;let s=`${n.stderr}${n.stdout}`;if(Ce(s))throw new l(i("notLoggedIn",{registry:t}),!0);throw new l(i("registryUnreachable",{registry:t,detail:s.trim()}))}async function An(e,t){if(t.publishConfig?.registry)return t.publishConfig.registry.replace(/\/$/,"");if(t.name.startsWith("@")&&t.name.includes("/")){let s=t.name.slice(0,t.name.indexOf("/")),o=await Bt(e,`${s}:registry`);if(o&&o!=="undefined"&&o!=="null")return o.replace(/\/$/,"")}let n=await Bt(e,"registry");if(!n||n==="undefined"||n==="null")throw new l(i("noNpmRegistry",{detail:"npm config get registry: empty value"}));return n.replace(/\/$/,"")}async function Bt(e,t){let n=await x("npm",["config","get",t],e);if(n.code!==0){let s=n.stderr.trim()||n.stdout.trim()||`npm config get ${t}: exit ${n.code}`;throw new l(i("noNpmRegistry",{detail:s}))}return n.stdout.trim()}async function Ht(e,t,n,s){if(!t)return;console.log(R(i("executingStep",{step:`${n}: ${t}`})));try{await He("sh",["-c",t],e,s)}catch(o){let r=o instanceof Error?o.message:String(o);throw new l(i("hookFailed",{name:n,detail:r}))}}async function Te(e,t,n){return t??(await M(Ve.resolve(e),n))?.distTag??"latest"}var Q=V(()=>{Re();j();D();N();L();_();jt();ne()});import we from"node:fs";import We from"node:path";function te(e,t,n){let s=We.resolve(e,t);if(!we.existsSync(s))throw new l(i("configUnreadable",{file:t}));return Me(we.readFileSync(s,"utf8"),n)}function K(e,t){let n=We.join(e,t);if(!we.existsSync(n))throw new l(i("packagesDirMissing",{dir:t}));return we.readdirSync(n).filter((s)=>!s.startsWith(".")&&we.existsSync(We.join(n,s,"package.json")))}function A(e,t,n){let s=JSON.parse(we.readFileSync(We.join(e,t,n,"package.json"),"utf8"));if(!s.name||!s.version)throw new l(i("packageJsonIncomplete",{folder:n}));return s}function Ue(e,t){let n=[];for(let s of["dependencies","devDependencies"])for(let o of Object.keys(e[s]??{})){let r=t.get(o);if(r)n.push(r)}return n}async function G(e){let t=await x("git",["status","--porcelain"],e);if(t.code!==0)throw new l(i("gitFailed",{detail:t.stderr.trim()}));if(t.stdout.trim())throw new l(i("dirtyTree"))}async function ze(e,t){let n=await x("git",["status","--porcelain"],e);if(n.code!==0)throw new l(i("gitFailed",{detail:n.stderr.trim()}));if(n.stdout.trim())throw new l(t)}async function Je(e){return(await v("git",["log","--format=%H%x1f%s"],e)).split(`
|
|
30
|
+
`).filter(Boolean).map((n)=>{let[s,o]=n.split("\x1F");return{hash:s,subject:o??""}})}async function be(e){let t=await v("git",["log","-F",`--grep=${pe}`,"--format=%H%x1f%s%x1f%b%x1e"],e),n=[];for(let s of t.split("\x1E")){let o=s.replace(/^\n/,"").trim();if(!o)continue;let[r,a,c]=o.split("\x1F");if(!r)continue;n.push({hash:r,subject:a??"",body:c??""})}return n}async function ie(e,t){if(t){let r=await x("git",["rev-parse","--verify",`${t}^{commit}`],e);if(r.code!==0)throw new l(i("badBaseline",{rev:t}));return r.stdout.trim()}if((await x("git",["rev-parse","--verify","HEAD"],e)).code!==0)throw new l(i("noBaseline"));let s=await be(e),o=(await Je(e)).find((r)=>st(r.subject));if(s[0]){if(o&&o.hash!==s[0].hash&&(await x("git",["merge-base","--is-ancestor",s[0].hash,o.hash],e)).code===0)return o.hash;return s[0].hash}return o?.hash??Mn()}async function _e(e,t,n,s=!n){let o=await ie(e,n),r=K(e,t.packagesDir),a=r.map((m)=>({...A(e,t.packagesDir,m),folder:m})),c=await jn(e,a,o,s),g=new Map,d=new Map;for(let m of r){let b=c.get(m);d.set(b,[...d.get(b)??[],m])}for(let[m,b]of d){let p=await v("git",["diff","--name-only",m,"HEAD","--",...b.map((y)=>`${t.packagesDir}/${y}`)],e);for(let[y,w]of vt(p,t.packagesDir,b))g.set(y,w)}let f=new Set;for(let[m,b]of g){let p=c.get(m),y=`${t.packagesDir}/${m}/package.json`;if(b.includes(y)&&await lt(e,p,"HEAD",y))f.add(m)}let{changed:k,dependents:h}=$t({packages:a,filesByFolder:g,devOnly:f,include:t.publish.include,exclude:t.publish.exclude,packagesDir:t.packagesDir});return{baseline:o,changed:k,dependents:h}}async function jn(e,t,n,s){let o=new Map(t.map((h)=>[h.folder,n]));if(!s)return o;let r=await be(e);if(!r.length||r[0].hash!==n)return o;let a=(await Je(e)).find((h)=>st(h.subject))?.hash,c=r[r.length-1].hash,g=await x("git",["rev-parse","--verify",`${c}^`],e),d=a??(g.code===0?g.stdout.trim():c);for(let h of t)o.set(h.folder,d);let f=new Map(t.map((h)=>[h.name,h.folder]));await Promise.resolve().then(() => Q());for(let h of r){if(!f.size)break;if(!he(h.body).some((b)=>f.has(b.name)))continue;let m=await M(e,h.hash);if(!m)continue;for(let b of m.lines){let p=f.get(b.name);if(!p)continue;let y=m.sourceCommits.get(b.name)??m.hash,w=a&&(await x("git",["merge-base","--is-ancestor",y,a],e)).code===0;o.set(p,w?a:y),f.delete(b.name)}}return o}async function lt(e,t,n,s){let o=await Ut(e,`${t}:${s}`),r=await Ut(e,`${n}:${s}`);if(o===null||r===null)return!1;try{return Pt(JSON.parse(o),JSON.parse(r))}catch{return!1}}async function Ut(e,t){let n=await x("git",["show",t],e);return n.code===0?n.stdout:null}function Mn(){throw new l(i("noBaseline"))}var ne=V(()=>{D();N();L();Dt()});import{CommitParser as Xn}from"conventional-commits-parser";import{writeChangelogString as Zn}from"conventional-changelog-writer";function nn(e){return e.find((t)=>Be(he(t.body)))?.hash}function sn(e,t,n,s,o,r){let a=`${n}/${t}/package.json`,c=`${n}/${t}/`,g=[];for(let d of e)if(d.files.some((k)=>k!==a&&k.startsWith(c)&&xe(k,s,o))||d.files.includes(a)&&r.has(d.hash))g.push({hash:d.hash,body:d.body});return g}async function rn(e){let t=new Date().toISOString().slice(0,10),n=[`## ${e.version} (${t})`];if(e.previousTag){let r=`../../compare/${encodeURIComponent(e.previousTag)}...${encodeURIComponent(e.currentTag)}`;n.push(`[${e.previousTag}...${e.currentTag}](${r})`)}let s=new Xn({noteKeywords:["BREAKING CHANGE","BREAKING-CHANGE"]}),o=[];for(let r of e.messages){let a;try{a=s.parse(r.body)}catch{continue}o.push({...a,hash:r.hash.slice(0,7),notes:a.notes??[],subject:a.subject})}if(o.length){let a=(await Zn(o,{version:e.version,date:t},{groupBy:"type",mainTemplate:ti,transform(c){let d=(c.notes??[]).some((k)=>/breaking/i.test(k.title)),f=c.type??"";if(ei.has(f)&&!d)return null;if(!en[f]&&!d)return null;return{type:en[f]??null}}})).trim();if(a)n.push("",a);else if(e.dependencies.length)n.push("",tn(e.dependencies))}else if(e.dependencies.length)n.push("",tn(e.dependencies).trimEnd());return`${n.join(`
|
|
31
31
|
`).trim()}
|
|
32
|
-
`}function
|
|
32
|
+
`}function tn(e){return`### Dependencies
|
|
33
33
|
|
|
34
34
|
* update dependencies ${e.map((t)=>`${t.name}@${t.version}`).join(", ")}
|
|
35
|
-
`}var
|
|
35
|
+
`}var ei,en,ti=`{{#each commitGroups}}
|
|
36
36
|
{{#if title}}
|
|
37
37
|
### {{title}}
|
|
38
38
|
|
|
@@ -50,23 +50,23 @@ For committed fixes to unpublished packages, append --retry-unpublished to keep
|
|
|
50
50
|
{{/each}}
|
|
51
51
|
|
|
52
52
|
{{/each}}
|
|
53
|
-
`;var
|
|
53
|
+
`;var on=V(()=>{N();ei=new Set(["docs","style","chore","refactor","test","build","ci"]),en={feat:"Features",feature:"Features",fix:"Bug Fixes",perf:"Performance Improvements",revert:"Reverts"}});import{Command as di}from"commander";import{readFileSync as pi}from"node:fs";j();D();L();ne();import Bn from"node:path";async function Wt(e){let t=Bn.resolve(e.root),n=te(t,e.versionsFile);if(e.packages)n.packagesDir=e.packages;await G(t);let{baseline:s,changed:o,dependents:r}=await _e(t,n,e.baseline);if(!o.length&&!r.length){let a=(await v("git",["rev-parse","--short",s],t)).trim();console.log(C(i("noPackagesSince",{rev:a})));return}console.log(R(i("changedHeader")));for(let a of o){let c=A(t,n.packagesDir,a);console.log(` ${X(c.name)}${c.private?` ${C(i("privateMark"))}`:""}`)}if(r.length){console.log(R(i("dependentsHeader")));for(let a of r){let c=A(t,n.packagesDir,a);console.log(` ${ce(c.name)}${c.private?` ${C(i("privateMark"))}`:""}`)}}}N();D();L();ne();import gt from"node:fs";import se from"node:path";import{spawn as Hn}from"node:child_process";var $e=(e)=>e.split("\x00").filter(Boolean);async function mt(e,t,n){let s=$e(await v("git",t===":"?["ls-files","-z","--",n]:["ls-tree","-r","--name-only","-z",t,"--",n],e)),o=`${n}/`,r=[];for(let a of s){if(!a.startsWith(o))continue;let c=a.slice(o.length).split("/");if(c.length!==2||c[1]!=="package.json"||c[0].startsWith("."))continue;r.push(Jt(await v("git",["show",`${t===":"?"":t}:${a}`],e),c[0]))}return r}function Jt(e,t){try{let n=JSON.parse(e);if(!n||typeof n.name!=="string"||!n.name)throw Error("name");for(let s of["dependencies","devDependencies","peerDependencies","optionalDependencies","scripts"]){let o=n[s];if(o!==void 0&&(!o||typeof o!=="object"||Array.isArray(o)||Object.values(o).some((r)=>typeof r!=="string")))throw Error(s)}return{...n,folder:t}}catch{throw new l(i("testManifestInvalid",{folder:t}))}}function On(e,t,n,s,o,r=!1){let a=new Map,c=(d,f)=>{let k=a.get(d)??new Set;k.add(f),a.set(d,k)},g=n.filter((d)=>s.allPackagesInputs.some((f)=>ke(d,f)));if(r||g.length)for(let d of e){if(r)c(d.folder,"--all");for(let f of g)c(d.folder,i("testAllPackagesReason",{file:f}))}else{let d=new Set([...t,...e].map((h)=>h.folder)),f=new Set,k=`${o}/`;for(let h of n){if(!h.startsWith(k)||!xe(h,s.include,s.exclude))continue;let m=h.slice(k.length).split("/")[0];if(!d.has(m))continue;if(c(m,h),s.dependentInputs.some((b)=>ke(h,b)))f.add(m)}if(s.dependents){let h=new Map;for(let y of[...t,...e]){let w=h.get(y.name)??new Set;w.add(y.folder),h.set(y.name,w)}let m=new Map;for(let y of[...t,...e]){let w={...y.dependencies,...y.devDependencies,...y.peerDependencies,...y.optionalDependencies};for(let P of Object.keys(w))for(let z of h.get(P)??[]){let u=m.get(z)??new Set;u.add(y.folder),m.set(z,u)}}let b=new Set(f),p=[...f];for(let y=0;y<p.length;y++){let w=p[y];for(let P of m.get(w)??[]){if(P===w)continue;if(c(P,i("testDependencyReason",{name:w})),!b.has(P))b.add(P),p.push(P)}}}}return new Map(e.filter((d)=>a.has(d.folder)).map((d)=>[d.folder,[...a.get(d.folder)]]))}async function _t(e){if(e.all&&e.base)throw new l(i("testAllBaseConflict"));let t=se.resolve(e.root),n=te(t,e.versionsFile,e.packages),s=n.test;if(!s)throw new l(i("testConfigMissing"));let o=e.concurrency===void 0?s.concurrency:Number(e.concurrency);if(!Number.isSafeInteger(o)||o<1)throw new l(i("testConfigInvalid",{field:"--concurrency"}));if((await v("git",["rev-parse","--show-prefix"],t)).trim())throw new l(i("testRootRequired"));let a="HEAD";if(e.base){let m=(await v("git",["rev-parse","--verify","--end-of-options",`${e.base}^{commit}`],t)).trim(),b=await x("git",["merge-base",m,"HEAD"],t);if(b.code!==0)throw new l(i("testBaseMissing",{base:e.base}));a=b.stdout.trim()}let c=[],g=[];if(!e.all){if(e.base)c=$e(await v("git",["diff","--name-only","--no-renames","-z",a,"HEAD","--"],t));else c=[...$e(await v("git",["diff","--cached","--name-only","--no-renames","-z","HEAD","--"],t)),...$e(await v("git",["diff","--name-only","--no-renames","-z","--"],t)),...$e(await v("git",["ls-files","--others","--exclude-standard","-z"],t))];if(g=await mt(t,a,n.packagesDir),e.base)g.push(...await mt(t,"HEAD",n.packagesDir));else g.push(...await mt(t,":",n.packagesDir))}let d=K(t,n.packagesDir).sort().map((m)=>{let b=se.join(t,n.packagesDir,m);if(!gt.realpathSync(b).startsWith(gt.realpathSync(t)+se.sep))throw new l(i("testRootRequired"));return Jt(gt.readFileSync(se.join(b,"package.json"),"utf8"),m)}),f={...s,allPackagesInputs:[...s.allPackagesInputs,se.relative(t,se.resolve(t,e.versionsFile)).split(se.sep).join("/")]},k=On(d,g,[...new Set(c)],f,n.packagesDir,e.all);console.log(i("testPlan",{count:k.size,base:e.all?"--all":a}));let h=[];for(let m of d){let b=k.get(m.folder);if(!b){console.log(i("testUnaffected",{name:m.name}));continue}if(console.log(` ${m.name}: ${b.join("; ")}`),(!s.command||e.ifPresent)&&!m.scripts?.test?.trim())console.log(i("testNoScript",{name:m.name}));else h.push(m)}if(e.dryRun){console.log(i("testDryRun"));return}await Gn(t,n.packagesDir,h,s.command??"npm run test",o)}async function Gn(e,t,n,s,o){let r=new Set,a=new Set,c=[],g=0,d=0,f=!1,k,h=(b)=>{for(let p of a)try{process.kill(-p,b)}catch{}if(process.platform==="win32")for(let p of r)p.kill(b)},m=()=>{f=!0,h("SIGTERM"),k??=new Promise((b)=>setTimeout(()=>{h("SIGKILL"),b()},2000))};process.on("SIGINT",m),process.on("SIGTERM",m);try{let b=async()=>{while(!f&&d<n.length){let p=n[d++];console.log(i("testRunning",{name:p.name,command:s}));let y=await new Promise((w)=>{let P=Hn(s,{cwd:se.join(e,t,p.folder),shell:!0,stdio:"inherit",detached:process.platform!=="win32"});if(r.add(P),process.platform!=="win32"&&P.pid)a.add(P.pid);P.once("error",(z)=>{console.error(z.message),r.delete(P),w(!1)}),P.once("close",(z)=>{if(r.delete(P),!f&&P.pid)a.delete(P.pid);w(z===0)})});if(console.log(i(y?"testPassed":"testFailed",{name:p.name})),y)g++;else c.push(p.name)}};await Promise.all(Array.from({length:Math.min(o,n.length)},b))}finally{await k,process.off("SIGINT",m),process.off("SIGTERM",m)}if(console.log(i("testSummary",{passed:g,failed:c.length})),f)throw new l(i("testInterrupted"));if(c.length)throw new l(i("testFailures",{names:c.join(", ")}))}j();D();N();_();import U from"node:fs";import me from"node:path";function Vn(e){let t=new Set,n=[];for(let s of[...Wn(e),...Jn(e)]){if(t.has(s))continue;t.add(s),n.push(s)}return n}function Ln(e,t=tt()){let n=(r,a="")=>i(r,void 0,t).split(`
|
|
54
54
|
`).map((c)=>`${a}# ${c}`).join(`
|
|
55
|
-
`),
|
|
56
|
-
`),
|
|
55
|
+
`),s=(r,a,c,g)=>[n(g," "),a?` ${r}: ${De(a)}`:` # ${r}: ${c}`].join(`
|
|
56
|
+
`),o=[n("initNotePackagesDir"),`packagesDir: ${De(e.packagesDir)}`,"","publish:",n("initNoteCommitMessage"," "),' commitMessage: "bump: %s"',"",n("initNoteBranch"," "),e.branch?` branch: ${De(e.branch)}`:" # branch: main",""," hooks:",s("beforeCommit",e.beforeCommit,"bun install","initNoteBeforeCommit"),s("beforePublish",e.beforePublish,"bun run build","initNoteBeforePublish"),s("afterPublish",void 0,"echo published","initNoteAfterPublish"),"",n("initNoteInclude"," ")," include:",...e.include.map((r)=>` - ${De(r)}`),` # - ${e.packagesDir}/*/bin`,"",n("initNoteExclude"," ")];if(e.exclude.length)o.push(" exclude:",...e.exclude.map((r)=>` - ${De(r)}`));else o.push(" # exclude:",` # - ${e.packagesDir}/*/src/**/__tests__/**`);return`${o.join(`
|
|
57
57
|
`)}
|
|
58
|
-
`}async function
|
|
58
|
+
`}async function Ke(e){let t=me.resolve(e.root),n=me.resolve(t,e.versionsFile);if(U.existsSync(n))throw new l(i("configExists",{file:e.versionsFile}));let s=Vn(t),o=s[0]??"packages",r=e.yes?qn(o):await Un(e.versionsFile,s,o);if(!r.include.length)throw new l(i("includeRequired"));let a=Ln(r);Me(a),U.mkdirSync(me.dirname(n),{recursive:!0}),U.writeFileSync(n,a),console.log(ce(i("initWrote",{file:e.versionsFile})))}function qn(e){return{packagesDir:e,include:[`${e}/*/src`],exclude:[`${e}/*/src/**/__tests__/**`]}}async function Un(e,t,n){if(!F())throw new l(i("yesRequired"));if(console.log(R(i("initIntro",{file:e}))),t.length)console.log(C(i("initFoundDirs",{dirs:t.join(", ")})));let s=await q(i("initPackagesDir"),n),o=Kt(await q(i("initInclude"),`${s}/*/src`)),r=Kt(await q(i("initExclude"),`${s}/*/src/**/__tests__/**`)),a=await q(i("initBranch")),c=await q(i("initBeforeCommit")),g=await q(i("initBeforePublish"));return{packagesDir:s,branch:a||void 0,beforeCommit:c||void 0,beforePublish:g||void 0,include:o,exclude:r}}function Kt(e){return e.split(",").map((t)=>t.trim()).filter(Boolean)}function De(e){return/^[A-Za-z0-9_./@*+-]+$/.test(e)?e:JSON.stringify(e)}function Wn(e){return Kn(e).flatMap((t)=>{let n=t.replace(/\/\*\*?$/,"");if(!n||n===t||n.includes("*"))return[];return U.existsSync(me.join(e,n))?[n]:[]})}function Jn(e){if(!U.existsSync(e))return[];let t=[];for(let n of U.readdirSync(e)){if(n.startsWith(".")||n==="node_modules")continue;let s=me.join(e,n);if(!U.statSync(s).isDirectory())continue;if(_n(s))t.push(n)}return t}function _n(e){return U.readdirSync(e).some((t)=>U.existsSync(me.join(e,t,"package.json")))}function Kn(e){let t=me.join(e,"package.json");if(!U.existsSync(t))return[];try{let n=JSON.parse(U.readFileSync(t,"utf8")),s=Array.isArray(n.workspaces)?n.workspaces:n.workspaces?.packages;return Array.isArray(s)?s.filter((o)=>typeof o==="string"):[]}catch{return[]}}Q();Re();Q();import gi from"node:path";import ln from"semver";Re();import de from"node:fs";import Qe from"node:path";L();N();j();D();import ue from"node:fs";import re from"node:path";import{spawnSync as Yn}from"node:child_process";function ut(e){try{let t=ue.lstatSync(e);if(!t.isFile())throw new l(i("unsafeCommitFile",{file:e}));return{bytes:ue.readFileSync(e),mode:t.mode&511}}catch(t){if(t.code==="ENOENT")return;throw t}}function Qn(e,t){return e===t||Boolean(e&&t&&e.mode===t.mode&&e.bytes.equals(t.bytes))}async function Yt(e,t,n){let s=(await v("git",["rev-parse","HEAD"],e)).trim();if(n&&s!==n)throw new l(i("versionHeadChanged"));let o=new Map;for(let h of new Set(t)){let m=re.resolve(e,h),b=re.relative(e,m);if(!b||b.startsWith("..")||re.isAbsolute(b)||/[*?\[\]\\]/.test(h)||b.split(re.sep).includes(".git"))throw new l(i("unsafeCommitFile",{file:h}));for(let p=re.dirname(m);p!==e;p=re.dirname(p))if(ue.existsSync(p)&&ue.lstatSync(p).isSymbolicLink())throw new l(i("unsafeCommitFile",{file:h}));o.set(b,ut(m))}let r=new Map(o),a,c=!0,g=()=>{r=new Map([...o.keys()].map((h)=>[h,ut(re.join(e,h))]))},d=(...h)=>Yn("git",h,{cwd:e,encoding:"utf8"}),f=()=>{if(!c)return;c=!1,process.off("SIGINT",k),process.off("SIGTERM",k);let h=d("rev-parse","HEAD");if(h.status!==0||h.stdout.trim()!==s){console.error(H(i("versionHeadChanged")));return}if(a!==void 0&&d("write-tree").stdout.trim()===a)d("restore","--staged","--",...o.keys());for(let[m,b]of o){let p=re.join(e,m);try{if(!Qn(ut(p),r.get(m)))continue;if(b)ue.writeFileSync(p,b.bytes),ue.chmodSync(p,b.mode);else ue.rmSync(p,{force:!0})}catch{}}},k=()=>{f(),console.error(H(i("versionRecovery"))),process.exit(1)};return process.on("SIGINT",k),process.on("SIGTERM",k),{checkpoint:g,async stage(){if((await v("git",["rev-parse","HEAD"],e)).trim()!==s)throw new l(i("versionHeadChanged"));if(await v("git",["diff","--cached","--name-only","-z"],e))throw new l(i("versionUnexpectedIndex"));let m=(await v("git",["diff","--name-only","-z"],e)).split("\x00").filter(Boolean),b=(await v("git",["ls-files","--others","--exclude-standard","-z"],e)).split("\x00").filter(Boolean),p=[...m,...b].filter((P)=>!o.has(P));if(p.length)throw new l(i("versionUnexpectedFiles",{files:p.join(", ")}));g();let y=[...new Set([...m,...b])];if(!y.length)throw new l(i("versionUnexpectedIndex"));if(await v("git",["add","--",...y],e),a=(await v("git",["write-tree"],e)).trim(),(await v("git",["diff","--cached","--name-only","-z"],e)).split("\x00").filter(Boolean).some((P)=>!o.has(P)))throw new l(i("versionUnexpectedIndex"))},async verifyCommit(){let h=(await v("git",["rev-parse","HEAD^"],e)).trim(),m=(await v("git",["rev-parse","HEAD^{tree}"],e)).trim();if(h!==s||m!==a)throw new l(i("versionHeadChanged"))},disarm(){c=!1,process.off("SIGINT",k),process.off("SIGTERM",k)},fail(h){throw f(),h}}}j();D();Q();N();L();_();j();D();N();_();ne();import Qt from"semver";async function Xt(e){let{bump:t,preid:n}=e;if(!t){if(e.yes||!F())throw new l(i("bumpRequired"));let s=n?"pre":await ee(i("chooseReleaseKind"),[{value:"formal",label:i("releaseKindFormal")},{value:"pre",label:i("releaseKindPre")}]);t=await Et(i("chooseBump"),s==="pre"?["prerelease","prepatch","preminor","premajor"]:["patch","minor","major"])}if(!t.startsWith("pre")||n||!["prerelease","prepatch","preminor","premajor"].includes(t))return{bump:je(t,n),preid:n};if(e.yes||!F())return{bump:je(t,n),preid:n};while(!n){let s=await q(i("choosePreid"),"alpha");if(Qt.valid(`0.0.0-${s}.0`))n=s;else console.log(S(i("invalidPreid")))}return{bump:je(t,n),preid:n}}function Ye(e){return Boolean(e)&&encodeURIComponent(e)===e&&!Qt.validRange(e)}async function Fe(e,t,n){if(e!==void 0||n||!F()){let s=e??t;if(!Ye(s))throw new l(i("badDistTag"));return s}while(!0){let s=await q(i("chooseDistTag"),t);if(Ye(s))return s;console.log(S(i("badDistTag")))}}async function Zt(e,t,n){try{return await ie(e,t)}catch(s){if(t||n||!F()||!(s instanceof l)||s.message!==i("noBaseline"))throw s}console.log(S(i("firstBaseline")));while(!0){let s=await q(i("chooseBaseline"));if(!s)continue;try{return await ie(e,s)}catch(o){if(!(o instanceof l))throw o;console.log(S(o.message))}}}ne();async function Xe(e){return Z(e.root,Boolean(e.dryRun),()=>ni(e))}async function ni(e){let t=Qe.resolve(e.root),n=te(t,e.versionsFile);if(e.packages)n.packagesDir=e.packages;if(e.messageFromCli&&e.commitMessage)n.publish.commitMessage=e.commitMessage;if(e.branchFromCli)n.publish.branch=e.branch||void 0;if(await G(t),n.publish.branch){let m=await x("git",["symbolic-ref","--quiet","--short","HEAD"],t);if(m.code!==0||m.stdout.trim()!==n.publish.branch)throw new l(i("wrongBranch",{branch:n.publish.branch}))}let s=await Zt(t,e.baseline,e.yes),o=(await v("git",["rev-parse","HEAD"],t)).trim(),r=e.force?{baseline:s,changed:await an(t,n),dependents:[]}:await _e(t,n,s,!e.baseline);if(!r.changed.length&&!r.dependents.length){let m=(await v("git",["rev-parse","--short",r.baseline],t)).trim();if(console.log(C(i("noPackagesSince",{rev:m}))),console.log(C(i("noVersionChangesNext"))),e.yes||!F()||!await M(t)||!await le(i("confirmForceVersion")))return;r.changed=await an(t,n)}if(F()&&!e.yes){console.log(R(i("wizardCandidates")));for(let m of[...r.changed,...r.dependents]){let b=A(t,n.packagesDir,m);console.log(` ${b.name}${b.private?` ${C(i("privateMark"))}`:""}`)}}let{bump:a,preid:c}=await Xt(e),g=[...r.changed,...r.dependents].map((m)=>{let b=A(t,n.packagesDir,m);return{folder:m,name:b.name,private:b.private,from:b.version,to:xt(b.version,a,c)}});if(e.dryRun)console.log(S(i("versionPreview")));for(let m of g)console.log(`${pt(m.name,m.from,m.to)}${m.private?` ${C(i("privateMark"))}`:""}`);if(g.some((m)=>m.private))console.log(C(i("privateVersionHint")));let d=e.preparePublish?await e.preparePublish({preid:c,config:n}):e.publishTag;if(e.dryRun){console.log(R(i("versionPreviewComplete")));return}if(!e.yes&&!await le(i("confirmVersions")))throw new l(i("cancelledVersion"));if(await G(t),(await v("git",["rev-parse","HEAD"],t)).trim()!==o)throw new l(i("versionHeadChanged"));let f=K(t,n.packagesDir).map((m)=>`${n.packagesDir}/${m}/package.json`);if(it(a))f.push(...g.map((m)=>`${n.packagesDir}/${m.folder}/CHANGELOG.md`));f.push("bun.lock","bun.lockb","package-lock.json","npm-shrinkwrap.json","yarn.lock","pnpm-lock.yaml",...n.publish.commitFiles??[]);let k=await Yt(t,f,o);try{try{for(let p of g)oi(t,n.packagesDir,p.folder,p.to);if(ai(t,n.packagesDir),it(a))await ii(t,n,g,s)}finally{k.checkpoint()}if(n.publish.hooks.beforeCommit)await He("sh",["-c",n.publish.hooks.beforeCommit],t);let m=St(n.publish.commitMessage,a,c),b=g.map((p)=>`${p.name} ${p.from} => ${p.to}`).join(`
|
|
59
59
|
`)+(d?`
|
|
60
60
|
|
|
61
|
-
Change-Versions-Dist-Tag: ${d}`:"");await
|
|
62
|
-
${E}`:
|
|
63
|
-
`).filter(Boolean),
|
|
64
|
-
`).filter(Boolean):[]})}return
|
|
65
|
-
`)}function
|
|
66
|
-
`)}}}
|
|
67
|
-
`).filter(Boolean)){let[c,
|
|
61
|
+
Change-Versions-Dist-Tag: ${d}`:"");await k.stage(),await v("git",["commit","-m",m,"-m",b,"-m",pe],t),await k.verifyCommit(),k.disarm()}catch(m){await k.fail(m)}let h=(await v("git",["rev-parse","HEAD"],t)).trim();if(console.log(B(i("versionCommitted",{commit:h}))),console.log(S(i("versionTrailerHint"))),n.publish.branch){let m=await x("git",["push","origin",n.publish.branch],t);if(m.code!==0)throw new l(i("branchPushFailed",{branch:n.publish.branch,detail:m.stderr.trim()}))}return console.log(R(i("versionPublishNext"))),console.log(I(O({...e,root:t,commit:h,distTag:d??c??"latest"}))),h}async function an(e,t){let n=await M(e);if(!n)throw new l(i("noVersionCommit"));let s=K(e,t.packagesDir).map((r)=>({folder:r,...A(e,t.packagesDir,r)})),o=new Map(s.map((r)=>[r.name,r]));return console.log(S(i("forcePreviousBatch",{commit:n.hash}))),n.lines.map((r)=>{let a=o.get(r.name);if(!a)throw new l(i("packageNotInDir",{dir:t.packagesDir,name:r.name}));if(a.version!==r.to)throw new l(i("releaseVersionMismatch",{name:r.name,expected:r.to,actual:a.version,commit:n.hash}));if(!a.private){for(let c of Object.keys(a.dependencies??{}))if(o.get(c)?.private)throw new l(i("publicDependsOnPrivate",{name:a.name,dep:c}))}return a.folder})}async function ii(e,t,n,s){await Promise.resolve().then(() => on());let c=await be(e),g=(await Je(e)).find((m)=>Rt(m.subject))?.hash,d=nn(c)??g??(s?await ie(e,s):void 0),f=await si(e,d),k=new Set(n.map((m)=>m.folder)),h=new Map(K(e,t.packagesDir).map((m)=>[A(e,t.packagesDir,m).name,m]));for(let m of n){let b=`${t.packagesDir}/${m.folder}/package.json`,p=new Set;for(let T of f)if(T.files.includes(b)&&await ri(e,T.hash,b))p.add(T.hash);let y=sn(f,m.folder,t.packagesDir,t.publish.include,t.publish.exclude,p),w=A(e,t.packagesDir,m.folder),P=y.length?[]:Ue(w,h).filter((T)=>k.has(T)).map((T)=>A(e,t.packagesDir,T)).map((T)=>({name:T.name,version:T.version})),z=await rn({messages:y,version:m.to,previousTag:`${m.name}@${m.from}`,currentTag:`${m.name}@${m.to}`,dependencies:P}),u=Qe.join(e,t.packagesDir,m.folder,"CHANGELOG.md"),E=de.existsSync(u)?de.readFileSync(u,"utf8"):"";de.writeFileSync(u,E?`${z}
|
|
62
|
+
${E}`:z)}}async function si(e,t){let n=t?["rev-list","--reverse",`${t}..HEAD`]:["rev-list","--reverse","HEAD"],s=(await v("git",n,e)).split(`
|
|
63
|
+
`).filter(Boolean),o=[];for(let r of s){let a=await v("git",["log","-1","--format=%B",r],e),c=await x("git",["diff-tree","--no-commit-id","--name-only","-r","--root",r],e);o.push({hash:r,body:a,files:c.code===0?c.stdout.split(`
|
|
64
|
+
`).filter(Boolean):[]})}return o}async function ri(e,t,n){return!await lt(e,`${t}^`,t,n)}function oi(e,t,n,s){let o=Qe.join(e,t,n,"package.json"),r=de.readFileSync(o,"utf8"),a=JSON.parse(r);if(typeof a.version!=="string")throw new l(i("missingVersionField",{folder:n}));a.version=s;let c=/\n([\t ]+)"/.exec(r)?.[1]??" ";de.writeFileSync(o,JSON.stringify(a,null,c)+`
|
|
65
|
+
`)}function ai(e,t){let n=K(e,t),s=new Map(n.map((o)=>[A(e,t,o).name,A(e,t,o)]));for(let o of n){let r=Qe.join(e,t,o,"package.json"),a=de.readFileSync(r,"utf8"),c=JSON.parse(a),g=!1;for(let d of["dependencies","devDependencies"])for(let f of Object.keys(c[d]??{})){let k=s.get(f);if(!k)continue;let h=`^${k.version}`;if(c[d][f]!==h)c[d][f]=h,g=!0}if(g){let d=/\n([\t ]+)"/.exec(a)?.[1]??" ";de.writeFileSync(r,JSON.stringify(c,null,d)+`
|
|
66
|
+
`)}}}ne();L();async function Ze(e){let t=[],n=new Map,s;if(!e.formal||e.entries.every((o)=>o.pkg.private))return{entries:t,remoteError:s};if(e.config.publish.branch){let o=e.entries.filter((a)=>!a.pkg.private).flatMap((a)=>[`refs/tags/${a.name}@${a.to}`,`refs/tags/${a.name}@${a.to}^{}`]),r=await x("git",["ls-remote","--tags","origin",...o],e.root);if(r.code!==0)s=r.stderr.trim()||r.stdout.trim()||`git ls-remote: exit ${r.code}`;else for(let a of r.stdout.trim().split(`
|
|
67
|
+
`).filter(Boolean)){let[c,g]=a.split(/\s+/);n.set(g,c)}}for(let o of e.entries){if(o.pkg.private)continue;let r=`${o.name}@${o.to}`,a=await x("git",["rev-parse","--verify",`refs/tags/${r}^{commit}`],e.root),c=n.get(`refs/tags/${r}^{}`)??n.get(`refs/tags/${r}`);t.push({tag:r,localReady:a.code===0&&a.stdout.trim()===o.commit,remoteReady:!e.config.publish.branch||!s&&c===o.commit})}return{entries:t,remoteError:s}}N();D();j();j();N();D();Q();_();j();D();Q();import ci from"node:path";L();async function oe(e){let t=ci.resolve(e.root);if(await v("git",["rev-parse","--show-toplevel"],t),!await M(t,e.commit)){console.log(C(i("statusNoBatch")));return}console.log(R(i("statusHeading")));let n=await ge(e,!0);console.log(i("publishSummaryContext",{commit:n.release.hash,tag:e.distTag}));let s=!0;for(let a of n.entries){let c=a.pkg.private?"summaryPrivate":!n.published.has(a.folder)?"statusUpload":n.distTagVersions.get(a.folder)!==a.to?"statusRetag":"summarySkipped";if(c==="statusUpload"||c==="statusRetag")s=!1;console.log(` ${(c==="statusUpload"||c==="statusRetag"?S:C)(i(c))}: ${a.name}@${a.to}`)}let o=await Ze(n);if(o.remoteError)s=!1,console.log(S(i("statusRemoteUnknown",{detail:o.remoteError})));for(let{tag:a,localReady:c,remoteReady:g}of o.entries){if(!c||!g)s=!1;console.log((c&&g?C:S)(i("statusGitTag",{tag:a,local:c?i("statusReady"):i("statusMissing"),remote:n.config.publish.branch?g?i("statusReady"):i("statusMissing"):i("statusNotConfigured")})))}let r=await x("git",["status","--porcelain"],t);if(r.code!==0||r.stdout.trim())s=!1,console.log(S(i("dirtyTree")));console.log((s?B:S)(i(s?"statusExternalReady":"statusPending"))),console.log(C(i("statusHooksUnknown"))),console.log(I(O({...e,root:t,commit:n.release.hash})))}Re();j();D();N();L();_();Q();ne();async function cn(e,t){console.log(R(i("repairPublication")));let n=await ge(e,!1,!0),{root:s,startHead:o,config:r,release:a,entries:c,changed:g,published:d}=n;for(let p of c){if(!g.has(p.folder))continue;if(d.has(p.folder)||p.pkg.private)throw new l(i("repairPublishedChanged",{name:p.name,version:p.to}))}if(!g.size)return a.hash;if(r.publish.branch){let p=await x("git",["symbolic-ref","--quiet","--short","HEAD"],s);if(p.code!==0||p.stdout.trim()!==r.publish.branch)throw new l(i("wrongBranch",{branch:r.publish.branch}))}let f=c.map((p)=>({...p,commit:g.has(p.folder)?o:p.commit}));if(n.formal)await qe(s,f.filter((p)=>!p.pkg.private));console.log(S(i("repairPlan")));for(let p of f)console.log(` ${p.name}@${p.to}: ${p.commit}${g.has(p.folder)?` ${i("repairCurrentCode")}`:""}`);if(e.dryRun){console.log(S(i("publishPreview"))),console.log(S(i("plannedStep",{step:i("confirmRepairPublication")}))),console.log(i("publishPlanSummary",{uploads:n.steps.filter((y)=>y.kind==="publish").length,retags:n.steps.filter((y)=>y.kind==="dist-tag").length,tags:n.steps.filter((y)=>y.kind==="tag").length}));let p=new Map(f.map((y)=>[y.name,y.commit]));ct({...n,entries:f,steps:n.steps.map((y)=>y.kind==="tag"?{...y,commit:p.get(y.name)}:y)},O({...e,root:s,commit:a.hash}));return}if(!e.yes&&!await le(i("confirmRepairPublication")))throw new l(i("cancelledPublish"));if(await G(s),(await v("git",["rev-parse","HEAD"],s)).trim()!==o)throw new l(i("versionHeadChanged"));let k=a.lines.map((p)=>`${p.name} ${p.from} => ${p.to}`).join(`
|
|
68
68
|
`)+`
|
|
69
69
|
|
|
70
70
|
Change-Versions-Dist-Tag: ${e.distTag}
|
|
71
|
-
`+
|
|
72
|
-
`);await
|
|
71
|
+
`+f.map((p)=>`Change-Versions-Source: ${p.name} ${p.commit}`).join(`
|
|
72
|
+
`);await v("git",["commit","--allow-empty","-m","chore: record repaired publication","-m",k,"-m",pe],s);let h=(await v("git",["rev-parse","HEAD"],s)).trim(),m=(await v("git",["rev-parse","HEAD^"],s)).trim(),b=(await v("git",["rev-parse","HEAD^{tree}"],s)).trim();if(m!==o||b!==(await v("git",["rev-parse",`${o}^{tree}`],s)).trim())throw new l(i("versionHeadChanged"));if(await G(s),t?.(h),console.log(B(i("repairRecorded",{commit:h}))),console.log(I(O({...e,root:s,commit:h,retryUnpublished:!1}))),r.publish.branch){let p=await x("git",["push","origin",r.publish.branch],s);if(p.code!==0)throw new l(i("branchPushFailed",{branch:r.publish.branch,detail:p.stderr.trim()}))}return console.log(C(i("publishResumeInfo"))),h}async function ye(e){return Z(e.root,Boolean(e.dryRun),()=>li(e))}async function li(e){let t=await M(e.root,e.commit),n={...e,commit:t?.hash??e.commit};while(!0)try{if(n.retryUnpublished){let s=await cn(n,(o)=>{n.commit=o,n.retryUnpublished=!1});if(!s)return;n.commit=s,n.retryUnpublished=!1}await Ot(n);return}catch(s){if(s instanceof J||s instanceof l&&s.message===i("cancelledPublish")||e.yes||e.dryRun||!F()||!n.commit)throw s;console.error(H(s instanceof Error?s.message:String(s)));while(!0){let o;try{o=await ee(i("recoveryAction"),[{value:"retry",label:i("actionRetry"),tone:"positive"},{value:"status",label:i("actionStatus")},{value:"exit",label:i("actionExitFailed"),tone:"muted"},{value:"repair",label:i("actionRetryUnpublished"),tone:"caution"}])}catch(r){if(r instanceof J)throw s;throw r}if(o==="exit")throw s;if(o==="retry")break;if(o==="repair"){n.retryUnpublished=!0;break}try{await oe(n)}catch(r){console.error(H(r instanceof Error?r.message:String(r)))}}}}_();async function et(e){let t=gi.resolve(e.root);if(e.distTag!==void 0&&!Ye(e.distTag))throw new l(i("badDistTag"));await Z(t,Boolean(e.dryRun),async()=>{if(e.resume||e.commit||e.retryUnpublished){if(e.force||e.bump||e.baseline||e.branchFromCli||e.messageFromCli)throw new l(i("releaseResumeOptions"));let r=await M(t,e.commit);if(!r)throw new l(i("noVersionCommit"));let a=r?ln.prerelease(r.lines[0].to)?.[0]:void 0,c=e.distTag??e.preid??r?.distTag??String(a??"latest");await ye({...e,root:t,commit:r?.hash??e.commit,distTag:await Fe(e.distTag,c,e.yes)});return}await G(t);let n=await M(t);if(!e.force&&n&&await ie(t,e.baseline)===n.hash){let r=n.distTag??String(ln.prerelease(n.lines[0].to)?.[0]??"latest"),a=await ge({...e,root:t,distTag:r},!0,!0),c=await Ze(a);if(c.remoteError)throw new l(i("statusRemoteUnknown",{detail:c.remoteError}));if(a.steps.some((g)=>g.kind==="publish"||g.kind==="dist-tag")||c.entries.some((g)=>!g.localReady||!g.remoteReady)){let g=i("releasePending",{command:O({...e,root:t,distTag:r,commit:n.hash})});if(e.yes||!F())throw new l(g);console.log(S(g));while(!0){let d=await ee(i("pendingAction"),[{value:"resume",label:i("actionResume"),tone:"positive"},{value:"status",label:i("actionStatus")},{value:"exit",label:i("actionExit"),tone:"muted"},{value:"force",label:i("actionForceVersion"),tone:"caution"},{value:"repair",label:i("actionRetryUnpublished"),tone:"caution"}]);if(d==="exit")return;if(d==="force"){e={...e,force:!0};break}if(d==="status"){try{await oe({...e,root:t,commit:n.hash,distTag:r})}catch(f){console.error(H(f instanceof Error?f.message:String(f)))}continue}await ye({...e,root:t,commit:n.hash,distTag:r,retryUnpublished:d==="repair"});return}}}console.log(R(i("releaseVersionStage")));let s=e.distTag??e.preid??"latest",o=await Xe({...e,root:t,preparePublish:async({preid:r,config:a})=>{if(s=await Fe(e.distTag,r??"latest",e.yes),F()&&!e.yes){console.log(R(i("wizardExecutionPlan"))),console.log(i("wizardPlanContext",{tag:s,branch:a.publish.branch??i("statusNotConfigured")}));for(let[c,g]of Object.entries(a.publish.hooks))if(g)console.log(` ${c}: ${g}`)}return s}});if(e.dryRun){console.log(S(i("releasePreviewComplete")));return}if(!o)return;console.log(R(i("releasePublishStage"))),console.log(I(O({...e,root:t,distTag:s,commit:o}))),await ye({...e,root:t,distTag:s,commit:o})})}j();D();j();D();import mi from"node:fs";import ui from"node:path";Q();_();async function gn(e=process.cwd()){let t={root:e,versionsFile:"change-versions.yml",branchFromCli:!1,messageFromCli:!1};while(!0){let n=mi.existsSync(ui.resolve(e,t.versionsFile));if(!n)console.log(S(i("wizardNeedsConfig")));let s=n?[{value:"release",label:i("actionNewRelease")},{value:"resume",label:i("actionResume"),tone:"positive"},{value:"status",label:i("actionStatus")},{value:"exit",label:i("actionExit"),tone:"muted"}]:[{value:"init",label:i("actionInit")},{value:"exit",label:i("actionExit"),tone:"muted"}],o=await ee(i("wizardAction"),s);if(o==="exit")return;if(o==="init"){await Ke(t);continue}if(o==="status"){await oe({...t,distTag:await Te(e)});continue}await et({...t,resume:o==="resume"});return}}_();N();var Y=new di;Y.name("change-versions").version(JSON.parse(pi(new URL("../package.json",import.meta.url),"utf8")).version).description(i("cliDescription"));function ve(e){return e.option("--root <dir>",i("optRoot"),process.cwd()).option("--versions-file <file>",i("optVersionsFile"),"change-versions.yml").option("--packages <dir>",i("optPackages"))}Y.command("init").description(i("cmdInit")).option("--root <dir>",i("optRoot"),process.cwd()).option("--versions-file <file>",i("optVersionsFile"),"change-versions.yml").option("--yes",i("optYesInit")).action(async function(){let e=this.opts();await Ke(e)});ve(Y.command("test").description(i("cmdTest"))).option("--if-present",i("optTestIfPresent")).option("--base <rev>",i("optTestBase")).option("--all",i("optTestAll")).option("--dry-run",i("optTestDryRun")).option("--concurrency <number>",i("optTestConcurrency")).action(async function(){await _t(this.opts())});ve(Y.command("changed").description(i("cmdChanged"))).option("--baseline <rev>",i("optBaseline")).action(async function(){let e=this.opts();await Wt(e)});ve(Y.command("version").description(i("cmdVersion")).argument("[bump]",i("argBump"))).option("--force",i("optForceVersion")).option("--preid <id>",i("optPreid")).option("--yes",i("optYes")).option("--dry-run",i("optDryRunVersion")).option("--baseline <rev>",i("optBaseline")).option("--branch <name>",i("optBranch")).option("--commit-message <template>",i("optCommitMessage")).action(async function(e){let t=this.opts();await Xe({...t,bump:e,branchFromCli:this.getOptionValueSource("branch")==="cli",messageFromCli:this.getOptionValueSource("commitMessage")==="cli"})});ve(Y.command("publish").description(i("cmdPublish"))).option("--retry-unpublished",i("optRetryUnpublished")).option("--commit <rev>",i("optReleaseCommit")).option("--dist-tag <tag>",i("optDistTag")).option("--yes",i("optYes")).option("--dry-run",i("optDryRunPublish")).action(async function(){let e=this.opts();await ye({...e,distTag:await Fe(e.distTag,await Te(e.root,e.distTag,e.commit),e.yes)})});ve(Y.command("status").description(i("cmdStatus"))).option("--commit <rev>",i("optReleaseCommit")).option("--dist-tag <tag>",i("optDistTag")).action(async function(){let e=this.opts();await oe({...e,distTag:await Te(e.root,e.distTag,e.commit)})});ve(Y.command("release").description(i("cmdRelease")).argument("[bump]",i("argBump"))).option("--retry-unpublished",i("optRetryUnpublished")).option("--force",i("optForceVersion")).option("--preid <id>",i("optPreid")).option("--dist-tag <tag>",i("optDistTag")).option("--yes",i("optYes")).option("--dry-run",i("optDryRunRelease")).option("--baseline <rev>",i("optBaseline")).option("--branch <name>",i("optBranch")).option("--commit-message <template>",i("optCommitMessage")).option("--resume",i("optResume")).option("--commit <rev>",i("optReleaseCommit")).action(async function(e){await et({...this.opts(),bump:e,branchFromCli:this.getOptionValueSource("branch")==="cli",messageFromCli:this.getOptionValueSource("commitMessage")==="cli"})});async function hi(){if(process.argv.length<=2){if(F())await gn();else Y.outputHelp();return}await Y.parseAsync(process.argv)}hi().catch((e)=>{if(e instanceof J){console.log(C(e.message));return}if(e instanceof l)console.error(H(e.message));else console.error(e);process.exit(1)});
|
package/docs/commands.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# 命令
|
|
2
2
|
|
|
3
|
-
对标 Lerna 的 `changed`、`version`、`publish from-package
|
|
3
|
+
对标 Lerna 的 `changed`、`version`、`publish from-package`。另提供按变更范围执行的 `test`;不做通用的 `bootstrap`、`run`、`exec`、`add`、`link`。
|
|
4
4
|
|
|
5
5
|
`release` 编排升版和发布;`version`、`publish` 保留为独立阶段。
|
|
6
6
|
|
|
7
7
|
和 Lerna 差两处:
|
|
8
8
|
|
|
9
9
|
- 非正式档位不写 CHANGELOG,也不打 git tag。`prerelease`、`prepatch`、`preminor`、`premajor` 需要预发布标识;交互时可补填,非交互或 `--yes` 时必须带 `--preid`。`patch` 再带 `--preid` 不接受。
|
|
10
|
-
- 按需升版本。默认从带 `Co-authored-by: change-versions <publish@change-versions.com>` 标记的历史批次中,找出各包最近覆盖的源码提交,分别比较到 HEAD;修复批次按 `Change-Versions-Source` 记录比较。不属于最近批次的包继续使用自身的历史起点,不被恢复记录或其他包的升版提交掩盖。没有自身历史时采用旧式版本基线,若无旧式基线则回退到首条工具批次的父提交(无父提交则使用该批次)。较新的旧式 `v<数字>` 基线可统一推进起点;显式 `--baseline` 则所有包按指定起点比较。起点不取自 `commitMessage` 或 Git tag,提交作者仍是执行命令的人。命中 `include` 且没有命中 `exclude` 的文件,或去掉 `devDependencies` 后仍有差异的 `package.json`,才让该包入选。入选包的依赖方一并升版。没有入选包时默认退出;交互可选择仅对上一批包强制升版,或显式使用 `--force`。
|
|
10
|
+
- 按需升版本。默认从带 `Co-authored-by: change-versions <publish@change-versions.com>` 标记的历史批次中,找出各包最近覆盖的源码提交,分别比较到 HEAD;修复批次按 `Change-Versions-Source` 记录比较。不属于最近批次的包继续使用自身的历史起点,不被恢复记录或其他包的升版提交掩盖。没有自身历史时采用旧式版本基线,若无旧式基线则回退到首条工具批次的父提交(无父提交则使用该批次)。较新的旧式 `v<数字>` 基线可统一推进起点;显式 `--baseline` 则所有包按指定起点比较。起点不取自 `publish.commitMessage` 或 Git tag,提交作者仍是执行命令的人。命中 `publish.include` 且没有命中 `publish.exclude` 的文件,或去掉 `devDependencies` 后仍有差异的 `package.json`,才让该包入选。入选包的依赖方一并升版。没有入选包时默认退出;交互可选择仅对上一批包强制升版,或显式使用 `--force`。
|
|
11
11
|
|
|
12
12
|
正式版(`patch`、`minor`、`major`)写各包 CHANGELOG,并把 workspace 依赖订正成 `^` 当前版本,然后提交。publish 每个公开包成功后立即创建 `<name>@<version>` Git tag;配置 branch 时立即推送该 tag,再处理下一个包。tag 指向该包的源码提交,通常是 version 提交;修复补发时按恢复记录确定。失败包不创建 tag,afterPublish 失败也保留已成功包的 tag。
|
|
13
13
|
|
|
@@ -21,7 +21,7 @@ change-versions version prerelease --preid=alpha
|
|
|
21
21
|
change-versions publish --dist-tag=alpha
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
`changed` 只打印。`version` 改版本和 workspace 依赖范围,提交标题来自 `commitMessage`,例如 `bump: prerelease alpha`。`publish` 把这些版本发到 dist-tag `alpha`。registry 上已经有的版本跳过。
|
|
24
|
+
`changed` 只打印。`version` 改版本和 workspace 依赖范围,提交标题来自 `publish.commitMessage`,例如 `bump: prerelease alpha`。`publish` 把这些版本发到 dist-tag `alpha`。registry 上已经有的版本跳过。
|
|
25
25
|
|
|
26
26
|
正式版:
|
|
27
27
|
|
|
@@ -60,39 +60,55 @@ change-versions publish
|
|
|
60
60
|
|
|
61
61
|
文件里的 `#` 注释说明字段,读取时会跳过。
|
|
62
62
|
|
|
63
|
-
##
|
|
63
|
+
## 配置迁移(Breaking Change)
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
配置按用途分类。升级时需手动迁移旧字段;旧写法以及新旧混写均会报错,并列出字段的新位置,不会自动合并两套规则。
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
| 原字段 | 新字段 | 用途 |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `packagesDir` | `packagesDir` | 发布与测试共享的包目录 |
|
|
70
|
+
| `include` / `exclude` | `publish.include` / `publish.exclude` | 决定哪些文件变化触发升版与发布 |
|
|
71
|
+
| `hooks` | `publish.hooks` | 发布生命周期命令,不用于测试 |
|
|
72
|
+
| `commitMessage` | `publish.commitMessage` | 版本提交标题模板 |
|
|
73
|
+
| `branch` | `publish.branch` | 版本分支推送与发布标签推送策略 |
|
|
74
|
+
| `commitFiles` | `publish.commitFiles` | 允许随版本提交的额外文件 |
|
|
75
|
+
| `test.global` | `test.allPackagesInputs` | 命中后测试所有包的文件规则 |
|
|
71
76
|
|
|
72
|
-
hooks
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
`publish` 覆盖从变更识别、升版提交到上传的发布流程,供 `changed`、`version`、`release`、`publish` 和 `status` 使用。`test` 独立决定测试范围,既不继承 `publish.exclude`,也不执行 `publish.hooks`。例如 `src/__tests__` 可以被发布规则排除,同时被测试规则包含。
|
|
78
|
+
|
|
79
|
+
所有路径模式仍相对仓库根目录,嵌套到 `publish` 后不用修改路径内容。命令行 `--branch` / `--commit-message` 名称保持不变,分别覆盖 `publish.branch` / `publish.commitMessage`。
|
|
80
|
+
|
|
81
|
+
## 配置
|
|
75
82
|
|
|
76
|
-
include
|
|
77
|
-
- packages/*/src
|
|
78
|
-
- packages/*/bin
|
|
83
|
+
`change-versions.yml` 放在仓库根目录。不认识的字段会失败。发布路径写在 `publish.include` 和 `publish.exclude` 下;测试路径写在 `test.include` 和 `test.exclude` 下。顶层只接受共享的 `packagesDir`、`publish`、`test`。
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
```yaml
|
|
86
|
+
packagesDir: packages
|
|
87
|
+
publish:
|
|
88
|
+
include:
|
|
89
|
+
- packages/*/src
|
|
90
|
+
- packages/*/bin
|
|
91
|
+
exclude:
|
|
92
|
+
- packages/*/src/**/__tests__/**
|
|
93
|
+
hooks:
|
|
94
|
+
beforeCommit: bun install
|
|
95
|
+
beforePublish: bun run clean && bun run build
|
|
96
|
+
branch: v5
|
|
97
|
+
commitMessage: "bump: %s"
|
|
82
98
|
```
|
|
83
99
|
|
|
84
100
|
- `packagesDir` 默认 `packages`。
|
|
85
|
-
- `commitFiles`
|
|
86
|
-
- `commitMessage` 默认 `bump: %s`。只决定提交标题。`%s` 换成档位;有 `--preid` 时再空一格接上 preid。正文一行一个包:`<name> <old> => <new>`。`version` 在正文末尾追加 `Co-authored-by: change-versions <publish@change-versions.com>`,不覆盖提交作者。下次 `changed`、`version`、`publish` 按这行查找。
|
|
87
|
-
- `branch` 不写就不推送。配置了 branch 时,version 要求当前分支与其一致(包括 dry-run),避免升版提交留在别的分支却推送旧分支。`version` 只推分支,不推 tag。
|
|
88
|
-
- `include` 必填。`exclude` 可以不写。标准 YAML 列表。`*` 和 `?` 不跨过 `/`,`**` 可以。模式相对仓库根。命中目录时,目录里的文件也算命中。不支持 `!` 和方括号字符组。
|
|
101
|
+
- `publish.commitFiles` 可选,在 `publish` 下填写 `commitFiles: [release-note.txt]`,只接受仓库内普通文件的精确相对路径,拒绝通配符、越界路径和符号链接。工作区各包的 package.json、本次 CHANGELOG,以及根目录 bun.lock、bun.lockb、package-lock.json、npm-shrinkwrap.json、yarn.lock、pnpm-lock.yaml 自动允许,无需重复声明。
|
|
102
|
+
- `publish.commitMessage` 默认 `bump: %s`。只决定提交标题。`%s` 换成档位;有 `--preid` 时再空一格接上 preid。正文一行一个包:`<name> <old> => <new>`。`version` 在正文末尾追加 `Co-authored-by: change-versions <publish@change-versions.com>`,不覆盖提交作者。下次 `changed`、`version`、`publish` 按这行查找。
|
|
103
|
+
- `publish.branch` 不写就不推送。配置了 `publish.branch` 时,version 要求当前分支与其一致(包括 dry-run),避免升版提交留在别的分支却推送旧分支。`version` 只推分支,不推 tag。
|
|
104
|
+
- `publish.include` 必填。`publish.exclude` 可以不写。标准 YAML 列表。`*` 和 `?` 不跨过 `/`,`**` 可以。模式相对仓库根。命中目录时,目录里的文件也算命中。不支持 `!` 和方括号字符组。
|
|
89
105
|
- `package.json` 不要求写进路径。只有它变了时,去掉两边的 `devDependencies` 再比。
|
|
90
106
|
|
|
91
|
-
`hooks` 在仓库根用 shell 执行一次,不按包重复。
|
|
107
|
+
`publish.hooks` 在仓库根用 shell 执行一次,不按包重复。
|
|
92
108
|
|
|
93
|
-
- `beforeCommit`:版本、依赖范围和 CHANGELOG 写完之后、`git commit` 之前。常见根锁文件会随版本提交;其他生成文件使用 `commitFiles` 显式声明。hook 不应自行暂存或提交。失败时按下文的文件保护规则恢复。
|
|
94
|
-
- `beforePublish`:确认之后、第一个 `npm publish` 之前。只能改 git 忽略的构建产物。
|
|
95
|
-
- `afterPublish`:要发的包都已在 registry 上之后。不能把工作区弄脏。
|
|
109
|
+
- `publish.hooks.beforeCommit`:版本、依赖范围和 CHANGELOG 写完之后、`git commit` 之前。常见根锁文件会随版本提交;其他生成文件使用 `publish.commitFiles` 显式声明。hook 不应自行暂存或提交。失败时按下文的文件保护规则恢复。
|
|
110
|
+
- `publish.hooks.beforePublish`:确认之后、第一个 `npm publish` 之前。只能改 git 忽略的构建产物。
|
|
111
|
+
- `publish.hooks.afterPublish`:要发的包都已在 registry 上之后。不能把工作区弄脏。
|
|
96
112
|
|
|
97
113
|
发布 hooks(`beforePublish` / `afterPublish`)通过 `CHANGE_VERSIONS_PUBLISH_CONTEXT` 读取 JSON 上下文:
|
|
98
114
|
|
|
@@ -110,7 +126,7 @@ registry 也不进配置。每个包按这个顺序解析,查询、上传、di
|
|
|
110
126
|
2. 否则,`@scope/name` 用 `.npmrc` 里的 `@scope:registry`
|
|
111
127
|
3. 否则用 `npm config get registry`
|
|
112
128
|
|
|
113
|
-
`"private": true`
|
|
129
|
+
`"private": true` 的包仍参与变更检测、依赖方传播和入选升版,用版本批次记录其源码变化;不上传,也不创建 Git tag。`changed`、交互入选列表和版本预览会显示私有标记;升版预览还会说明“私有包仅升版,不上传,也不创建 Git tag”。
|
|
114
130
|
|
|
115
131
|
## 退出后下一步
|
|
116
132
|
|
|
@@ -120,7 +136,7 @@ flowchart TD
|
|
|
120
136
|
dirtyTree["工作区不干净"]
|
|
121
137
|
noBaseline["没有基线"]
|
|
122
138
|
badRev["baseline 解析失败"]
|
|
123
|
-
badYml["配置读不了或 include 为空"]
|
|
139
|
+
badYml["配置读不了或 publish.include 为空"]
|
|
124
140
|
noneChanged["没有要升的包"]
|
|
125
141
|
badBump["档位或 preid 不合法"]
|
|
126
142
|
userNo["确认时回答否"]
|
|
@@ -156,7 +172,7 @@ flowchart TD
|
|
|
156
172
|
dirtyTree --> hDirty["退出 1. 提交或还原后, 重跑刚才那条命令"]
|
|
157
173
|
noBaseline --> hBase["退出 1. changed 或 version 加上 --baseline"]
|
|
158
174
|
badRev --> hRev["退出 1. 换成 git rev-parse 能解析的 SHA 或 tag"]
|
|
159
|
-
badYml --> hYml["退出 1. 补上 include, 删掉不认识的字段"]
|
|
175
|
+
badYml --> hYml["退出 1. 补上 publish.include, 删掉不认识的字段"]
|
|
160
176
|
noneChanged --> hNone["退出 0. 不用做"]
|
|
161
177
|
badBump --> hBump["退出 1. pre 档位带 --preid, 正式版不要带"]
|
|
162
178
|
userNo --> hNo["还没写文件也没上传. 想继续就再跑同一条命令"]
|
|
@@ -180,7 +196,7 @@ flowchart TD
|
|
|
180
196
|
|
|
181
197
|
五个包里前三个发布成功、第四个失败时,正式版已立即创建前三个包的 Git tag,第四个和未执行的第五个没有 tag。重跑同一条 `publish` 会跳过已有版本、补齐缺失的标签,只上传剩余包。若上传成功但 Git tag 创建或推送失败,停止后续包,恢复时先补齐该包标签再继续,不重复上传。401、403、ENEEDAUTH 不重试,先 `npm login --registry=<url>`。其余网络错误每个包重试 2 次。只有 npm view 的 JSON 错误明确给出 E404 才视为目标版本不存在;认证失败、DNS/网络错误、无效响应不视为未发布。`npm publish` 和 `dist-tag add` 都算。
|
|
182
198
|
|
|
183
|
-
打 tag 如果停在半路,再跑时指向同一条 version 提交的 tag 跳过,指向别处则退出并且不覆盖。有 `branch` 时再推这些 tag,不用 `git push --tags`。远程已有同一个 tag 算成功。
|
|
199
|
+
打 tag 如果停在半路,再跑时指向同一条 version 提交的 tag 跳过,指向别处则退出并且不覆盖。有 `publish.branch` 时再推这些 tag,不用 `git push --tags`。远程已有同一个 tag 算成功。
|
|
184
200
|
|
|
185
201
|
## changed
|
|
186
202
|
|
|
@@ -265,7 +281,7 @@ change-versions publish --dist-tag=<tag>
|
|
|
265
281
|
|
|
266
282
|
上一次正式版是带这条 `Co-authored-by` 的提交里,正文每个新版本都没有预发布后缀的那一条。还没有这种提交时,认标题为 `v` 加 `主.次.修`、且不含预发布后缀的提交。都没有时,使用本轮比较基线(包括交互输入的首次基线)。
|
|
267
283
|
|
|
268
|
-
一个提交归给某个包,当它改过的文件命中该包的 `include` 且没有命中 `exclude`,或该包的 `package.json` 在去掉 `devDependencies` 后有差异。只因依赖方被升版的包,写一条依赖更新。`feat` 进 Features,`fix` 进 Bug Fixes,`BREAKING CHANGE` 进破坏性变更。`docs`、`style`、`chore`、`refactor`、`test`、`build`、`ci` 不出现。解析不了的提交跳过。比较链接写 tag 名字,此时 tag 还没打。
|
|
284
|
+
一个提交归给某个包,当它改过的文件命中该包的 `publish.include` 且没有命中 `publish.exclude`,或该包的 `package.json` 在去掉 `devDependencies` 后有差异。只因依赖方被升版的包,写一条依赖更新。`feat` 进 Features,`fix` 进 Bug Fixes,`BREAKING CHANGE` 进破坏性变更。`docs`、`style`、`chore`、`refactor`、`test`、`build`、`ci` 不出现。解析不了的提交跳过。比较链接写 tag 名字,此时 tag 还没打。
|
|
269
285
|
|
|
270
286
|
根目录的 CHANGELOG 不由这个工具改。
|
|
271
287
|
|
|
@@ -274,11 +290,12 @@ change-versions publish --dist-tag=<tag>
|
|
|
274
290
|
最小仓库:
|
|
275
291
|
|
|
276
292
|
```yaml
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
293
|
+
publish:
|
|
294
|
+
include:
|
|
295
|
+
- packages/*/src
|
|
296
|
+
hooks:
|
|
297
|
+
beforeCommit: npm install
|
|
298
|
+
beforePublish: npm run build
|
|
282
299
|
```
|
|
283
300
|
|
|
284
301
|
```bash
|
|
@@ -286,7 +303,7 @@ change-versions version prerelease --preid=alpha --baseline=<首次修订>
|
|
|
286
303
|
change-versions publish --dist-tag=alpha
|
|
287
304
|
```
|
|
288
305
|
|
|
289
|
-
minipack-cli 的日常预发布把不变的项放在仓库根的 `change-versions.yml`:`packagesDir` 为 `packages`,`commitMessage` 为 `bump: %s`,`branch` 为 `v5`,`beforeCommit` 是 `bun install`,`beforePublish` 是构建。命令只留下档位和 dist-tag。`@tuya-miniapp` 的私有 registry 写在各包的 `publishConfig.registry` 或 `.npmrc`,不写进这个工具。工具新版本发布并升级依赖后,可让 `make release` 直接执行 `change-versions release patch`,恢复入口执行 `change-versions release --resume`,替换仓库中的流程包装脚本。构建仍通过 `beforePublish` 和发布上下文接入,本工具不内置 Bun 构建策略。
|
|
306
|
+
minipack-cli 的日常预发布把不变的项放在仓库根的 `change-versions.yml`:`packagesDir` 为 `packages`,`publish.commitMessage` 为 `bump: %s`,`publish.branch` 为 `v5`,`beforeCommit` 是 `bun install`,`beforePublish` 是构建。命令只留下档位和 dist-tag。`@tuya-miniapp` 的私有 registry 写在各包的 `publishConfig.registry` 或 `.npmrc`,不写进这个工具。工具新版本发布并升级依赖后,可让 `make release` 直接执行 `change-versions release patch`,恢复入口执行 `change-versions release --resume`,替换仓库中的流程包装脚本。构建仍通过 `beforePublish` 和发布上下文接入,本工具不内置 Bun 构建策略。
|
|
290
307
|
|
|
291
308
|
|
|
292
309
|
## 工具自身的打包检查
|
|
@@ -294,3 +311,63 @@ minipack-cli 的日常预发布把不变的项放在仓库根的 `change-version
|
|
|
294
311
|
`bun run check` 依次执行 typecheck、所有测试和构建。`npm pack` / `npm publish` 会通过 prepack 自动检查类型并构建最新 CLI;只上传 dist、docs、README 和 package.json 等 npm 必要元数据。设置本包发布版本及实际上传由维护者执行。
|
|
295
312
|
|
|
296
313
|
运行安装包可用 `change-versions --version` 检查版本。命令说明与源码开发使用 Bun,安装后的 Node CLI 不要求用户安装 Bun。
|
|
314
|
+
|
|
315
|
+
## test
|
|
316
|
+
|
|
317
|
+
`test` 根据变更文件选择需要测试的包,不升版、不发布,也不使用发布基线。测试规则独立于 `publish.include/exclude`:测试文件变化可以触发测试,同时继续被发布规则排除。
|
|
318
|
+
|
|
319
|
+
```yaml
|
|
320
|
+
publish:
|
|
321
|
+
include:
|
|
322
|
+
- packages/*/src/**
|
|
323
|
+
exclude:
|
|
324
|
+
- packages/*/src/**/__tests__/**
|
|
325
|
+
- "**/*.test.ts"
|
|
326
|
+
test:
|
|
327
|
+
command: bun run test
|
|
328
|
+
include:
|
|
329
|
+
- packages/*/src/**
|
|
330
|
+
- packages/*/tests/**
|
|
331
|
+
- packages/*/package.json
|
|
332
|
+
- packages/*/tsconfig.json
|
|
333
|
+
exclude:
|
|
334
|
+
- "**/*.md"
|
|
335
|
+
dependentInputs:
|
|
336
|
+
- packages/*/src/**
|
|
337
|
+
- packages/*/package.json
|
|
338
|
+
dependents: true
|
|
339
|
+
concurrency: 4
|
|
340
|
+
allPackagesInputs:
|
|
341
|
+
- package.json
|
|
342
|
+
- bun.lock
|
|
343
|
+
- tsconfig.json
|
|
344
|
+
- test-utils/**
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
# 本地:暂存、未暂存和未跟踪文件,相对于 HEAD
|
|
349
|
+
change-versions test
|
|
350
|
+
|
|
351
|
+
# CI:目标分支与 HEAD 的 merge-base 到 HEAD,仅选择已提交变更
|
|
352
|
+
change-versions test --base origin/main
|
|
353
|
+
|
|
354
|
+
# 显示每个包的入选原因、未受影响或缺少测试脚本状态,不执行命令
|
|
355
|
+
change-versions test --base origin/main --dry-run
|
|
356
|
+
|
|
357
|
+
# 全量测试;串行适用于共享端口、数据库或输出目录的测试
|
|
358
|
+
change-versions test --all --concurrency 1
|
|
359
|
+
|
|
360
|
+
# 自定义命令也只在声明了 scripts.test 的包中执行
|
|
361
|
+
change-versions test --if-present
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
- 必须显式声明 `test`;`test: {}` 可启用默认配置。`publish.include` 仍须填写。包目录结构与 `changed` 一致,为 `packagesDir` 的直接子目录,私有包同样参与测试。`--root` 必须指向 Git 仓库根目录。
|
|
365
|
+
- `test.include` 默认 `<packagesDir>/*/**`,包含测试、快照、fixture、隐藏文件和包配置;`test.exclude` 默认空。匹配规则与发布路径规则相同,相对仓库根目录。默认不会排除快照或测试文件。
|
|
366
|
+
- `test.dependentInputs` 默认与 `test.include` 相同。只有文件先命中 `test.include` 且未命中 `test.exclude`,并命中 `test.dependentInputs`,才传播到依赖方。例子中的 `src/**` 包含源码目录内的测试,仍会传播;若希望这些测试仅触发本包,可改为枚举源码目录或源码入口。`test.exclude` 会让文件完全不触发测试,不适合用来实现“只测本包”。
|
|
367
|
+
- `test.dependents` 默认 `true`,递归选中直接和间接依赖方,覆盖 `dependencies`、`devDependencies`、`peerDependencies` 和 `optionalDependencies`。传播使用比较起点与当前状态的依赖关系并集;本地模式还检查暂存区。删除、重命名按新旧路径分别处理;已删除的包不再执行测试,但可触发其依赖方。
|
|
368
|
+
- `test.allPackagesInputs` 命中任一文件就测试所有包,不受包级 `test.include/exclude` 限制。默认包含根 `package.json`、`tsconfig.json`、`pnpm-workspace.yaml` 和常见锁文件:`bun.lock`、`bun.lockb`、`package-lock.json`、`npm-shrinkwrap.json`、`yarn.lock`、`pnpm-lock.yaml`。显式填写列表会替换默认值;配置文件自身始终触发全量测试。共享测试工具、公共 mock、fixture 目录应加入这里。
|
|
369
|
+
- 未配置 `test.command` 时,在各入选包目录执行 `npm run test`,缺少 `scripts.test` 的包明确显示跳过,不记为通过。配置 `test.command` 后默认始终执行该 shell 命令,命令不存在或脚本缺失会失败。加上 `--if-present` 可让自定义命令也跳过没有 `scripts.test` 的包;此选项只检查脚本是否声明,不会吞掉实际测试失败,也不会阻断受影响包的依赖传播。命令负责必要的依赖构建,工具不自动生成构建任务。
|
|
370
|
+
- `test.concurrency` 默认 `4`,`--concurrency` 可覆盖,均须为正整数。入选测试全部运行后汇总失败包,任一失败退出 `1`;无匹配变更退出 `0`。多个包的命令输出可能交错,工具打印各包的开始和结束状态。收到 SIGINT/SIGTERM 后停止调度并终止运行中的命令;POSIX 系统按进程组清理子进程。
|
|
371
|
+
- `--base` 不读取发布记录,不自动选择默认分支,也不包含未提交变更;实际命令始终运行在当前工作区,CI 应使用干净的检出。缺少提交或共同祖先时退出失败,请补齐 Git 历史后重试,或用 `--all`。`--all` 不需要比较基线,不能与 `--base` 同用。
|
|
372
|
+
|
|
373
|
+
支持选项:`--root`、`--versions-file`、`--packages`、`--base`、`--all`、`--dry-run`、`--concurrency`、`--if-present`。本功能不自动接入发布 hooks,发布前需要测试时可以由 CI 显式串联命令。
|
package/docs/release-flow.md
CHANGED
|
@@ -107,7 +107,7 @@ Git tag 证明该包对应的源码已发布,不代表整批全部成功,也
|
|
|
107
107
|
|
|
108
108
|
### 2. 共享输入如何影响选包:规则待定
|
|
109
109
|
|
|
110
|
-
根目录的构建脚本、共享配置和锁文件可能影响多个包的实际产物。当前变更归属按包目录分组,包目录外的文件不会仅因为命中 include 就自动映射到受影响包。
|
|
110
|
+
根目录的构建脚本、共享配置和锁文件可能影响多个包的实际产物。当前变更归属按包目录分组,包目录外的文件不会仅因为命中 publish.include 就自动映射到受影响包。
|
|
111
111
|
|
|
112
112
|
应先明确这些共享文件影响哪些包。否则会出现源码目录没变、产物实际改变,却没有入选包的情况。这与“只发布对应变动的包”直接相关,不宜用默认全量发布掩盖。
|
|
113
113
|
|