change-versions 1.4.2 → 2.0.1
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 +52 -4
- package/dist/cli.js +29 -28
- package/docs/commands.md +118 -35
- package/docs/release-flow.md +12 -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
|
|
|
@@ -66,7 +103,7 @@ CLI 回归通过临时 npm 替身验证发布顺序、部分失败续发、登
|
|
|
66
103
|
|
|
67
104
|
完成用绿色、失败用红色、预览和待处理事项用黄色、引导和命令用青色、跳过项及 registry 用弱化颜色;状态始终有文字标签。颜色沿用终端检测,支持 `NO_COLOR`;重定向日志仍保留完整文字,`FORCE_COLOR=1` 可强制显示颜色。
|
|
68
105
|
|
|
69
|
-
|
|
106
|
+
交互菜单的标题、序号和输入引导用青色,恢复或重试用绿色,强制升版与修复补发用黄色,退出用弱化颜色。文本输入的默认值用绿色,确认操作也使用选择列表,默认选中 `yes`,选择 `no` 直接退出;关闭颜色后仍保留原有文案和默认值提示。
|
|
70
107
|
|
|
71
108
|
发生部分上传、hook 或 Git 标签失败时,先修复提示中的原因,再使用输出的 publish 命令重试,无需再次 version。重试重新查询 registry,跳过已上传版本并补齐必要标签;配置的 hook 仍会执行。Git 标签阶段失败时,本地或远端可能已有部分标签,不能据此认定全部未创建。
|
|
72
109
|
|
|
@@ -91,7 +128,7 @@ change-versions release patch --dist-tag=next
|
|
|
91
128
|
change-versions release patch --yes # 脚本/CI,不提问
|
|
92
129
|
```
|
|
93
130
|
|
|
94
|
-
输入错误可重试;输入 q、Ctrl-C
|
|
131
|
+
输入错误可重试;输入 q、Ctrl-C 或输入结束可取消当前提问。取消停止后续步骤,已经创建的版本提交或已上传的包会保留。版本提交和实际上传分别确认;确认列表默认选中 `yes`,选择 `no` 直接退出,上下键选择 `yes` 后回车执行。发现上一批未完成时,可查看状态、恢复发布、仅对上一批包强制升版并发布,或选择“使用当前代码重试未发布的包(保留原版本号)”。发布失败后也提供修复补发入口;普通重试固定同一提交和标签,从失败菜单退出仍返回失败状态。
|
|
95
132
|
|
|
96
133
|
例如四个包中三个已经发布,第四个需要改代码:先提交第四个包的修复,再选择修复补发或运行 `publish --retry-unpublished` / `release --retry-unpublished`。工具重新查询 registry,仅尚未发布的公开包可采用修复代码,所有版本号保持不变,已上传版本不重复上传。会创建一条空的 Git 恢复记录提交,记录整批每个包对应的源码提交;已发布包的正式版 Git 标签仍对应原提交,修复包的标签对应修复提交。再次中断后,沿用输出的新 publish 命令或 `release --resume` 即可。若仍需继续修改未发布包,再提交修复并重复使用 `--retry-unpublished`。`--dry-run` 可先预览,已发布包或私有包的源码变更会阻止此操作。
|
|
97
134
|
|
|
@@ -99,11 +136,22 @@ change-versions release patch --yes # 脚本/CI,不提问
|
|
|
99
136
|
|
|
100
137
|
## 统一发布入口
|
|
101
138
|
|
|
139
|
+
可在项目配置中预设发布 tag,交互运行 `release` / `publish` 时直接选择:
|
|
140
|
+
|
|
141
|
+
```yaml
|
|
142
|
+
publish:
|
|
143
|
+
distTags: [latest, alpha, beta, next]
|
|
144
|
+
include:
|
|
145
|
+
- packages/*/src
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
未配置列表时提供 latest、alpha、beta、next。预发布版本标识也使用列表选择,提供自定义入口。列表会保留当前推导或恢复批次的 tag,并提供“自定义 tag”入口。`--dist-tag` 直接使用指定值;`--yes` 沿用原有推导规则,不会自动采用列表第一项。`init` 会询问并写入这份列表。这里的 tag 是 npm dist-tag;预发布版本标识仍由 `--preid` 或交互输入决定。
|
|
149
|
+
|
|
102
150
|
`release patch` 完成选包、升版、提交和发布;`release --resume` 只恢复发布。自定义 dist-tag 记录在版本提交中,恢复时自动沿用。`status` 只读查询 npm 版本、标签和 Git 标签,区分待上传、待更新标签与已就绪;它不能证明历史 hook 是否成功。
|
|
103
151
|
|
|
104
152
|
合并后丢失自动识别标记时,可使用 `publish --commit=<版本提交>` 或 `release --commit=<版本提交>`,版本与内容校验仍然生效。失败引导中的命令也固定 commit。
|
|
105
153
|
|
|
106
|
-
实际操作使用仓库锁避免并发发布。升版仅暂存本次允许的文件;失败仅恢复仍属于本次写入的内容,外部文件与意外 hook 提交会保留。额外的 hook 生成文件通过 YAML 的 `commitFiles` 声明。配置仍兼容现有 YAML,无需迁移 JS 配置,也不增加 Bun 运行时依赖。完整规则见 [命令文档](docs/commands.md)。
|
|
154
|
+
实际操作使用仓库锁避免并发发布。升版仅暂存本次允许的文件;失败仅恢复仍属于本次写入的内容,外部文件与意外 hook 提交会保留。额外的 hook 生成文件通过 YAML 的 `publish.commitFiles` 声明。配置仍兼容现有 YAML,无需迁移 JS 配置,也不增加 Bun 运行时依赖。完整规则见 [命令文档](docs/commands.md)。
|
|
107
155
|
|
|
108
156
|
使用项目需要在包含这些命令的新版本发布后升级依赖,再将现有流程包装换成 release;1.2.1 不包含这些入口。构建通过既有 beforePublish 和发布上下文接入,增量构建策略继续由项目决定。
|
|
109
157
|
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`),r=q("red",t,process.stderr);if(!n.length)return r;return[r,...n.map((s)=>q("yellow",s,process.stderr))].join(`
|
|
4
|
-
`)}function M(e){return q("green",e,process.stdout)}function k(e){return q("yellow",e,process.stdout)}function ze(e){return q("red",e,process.stdout)}var I=()=>{};function Ke(){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=Ke()){let r=on[e][n];if(t)for(let[s,o]of Object.entries(t))r=r.replaceAll(`{${s}}`,String(o));return r}var on;var D=G(()=>{on={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}
|
|
2
|
+
var Je=(e,t,n)=>()=>{if(e)try{t=e(e=0)}catch(r){n=[r]}if(n)throw n[0];return t};function Ke(){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=Ke()){let r=ln[e][n];if(t)for(let[o,s]of Object.entries(t))r=r.replaceAll(`{${o}}`,String(s));return r}var ln;var T=Je(()=>{ln={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."},confirmList:{zh:"请选择操作",en:"Confirm action"},confirmNo:{zh:"no",en:"no"},confirmYes:{zh:"yes",en:"yes"},choosePreidList:{zh:"选择预发布版本标识",en:"Choose a prerelease identifier"},customPreid:{zh:"自定义版本标识",en:"Custom prerelease identifier"},chooseDistTagList:{zh:"选择发布的 npm tag",en:"Choose the target npm tag"},customDistTag:{zh:"自定义 tag",en:"Custom tag"},initDistTags:{zh:"发布 tag 列表(publish.distTags),逗号分隔",en:"Publication tags (publish.distTags), comma-separated"},initNoteDistTags:{zh:"交互发布时可直接选择的 npm tag 列表;--dist-tag 可覆盖",en:"Selectable npm tags for interactive publication; override with --dist-tag"},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
3
|
持有者: {owner}
|
|
6
4
|
下一步:等待该进程结束;若进程已退出,确认没有其他发布操作后再删除该锁并重试。`,en:`Another release operation holds the repository lock: {file}
|
|
7
5
|
Owner: {owner}
|
|
@@ -23,16 +21,14 @@ For committed fixes to unpublished packages, append --retry-unpublished to keep
|
|
|
23
21
|
{detail}`,en:`Cannot read the npm registry:
|
|
24
22
|
{detail}`},npmFailed:{zh:"npm 失败",en:"npm failed"},npmViewFailed:{zh:"npm view 失败",en:"npm view failed"},hookFailed:{zh:`{name} 失败。下一步: 修好 hook 后重跑 publish。已上传的包及已创建的 Git 标签保留
|
|
25
23
|
{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 x(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 x(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(k(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(`${k("[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(k(i("confirmAnswer")))}}var J,Pn;var _=G(()=>{I();D();N();J=class J extends l{constructor(){super(i("interactiveCancelled"))}};Pn={positive:M,caution:k,muted:v}});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?k(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),C=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(v(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},C);return}console.log(v(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?` ${v(i("privateMark"))}`:m.has(g.folder)?` ${v(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,P=!1,b=(g)=>g==="summaryPublished"||g==="summaryRetagged"?M:g==="summaryFailed"?ze:g==="summaryPending"?k:v,R=()=>{console.log((P?M:ze)(i(P?"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?` — ${v(re)}`:""}`)}if(console.log([...g].map(([E,z])=>`${b(E)(i(E))}: ${Q(String(z))}`).join(", ")),P)console.log(M(i("publishReady"))),console.log(v(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(k(i("publishTagRecovery")));console.log(k(i("publishRetryTitle"))),console.log(S(i("publishRetry"))),console.log(v(i("publishResumeInfo"))),console.log(F(C))}},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(v(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),P=!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 x("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 C=Ne(a.lines);if(C)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 P=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:C,commit:a.hash,beforePublish:o.hooks.beforePublish,afterPublish:o.hooks.afterPublish});return{root:r,startHead:s,config:o,release:a,entries:d,formal:C,registryOf:f,published:w,distTagVersions:p,steps:P,changed:h}}function rt(e,t){for(let n of e.steps)if(console.log(`${k(i("plannedStep",{step:""}))}${Be(n)}`),n.kind==="tag"&&e.config.branch)console.log(k(i("plannedStep",{step:`git push origin refs/tags/${n.name}@${n.version}`})));if(!e.formal)console.log(v(i("noGitTag")));console.log(k(i("publishPreviewComplete"))),console.log(S(i("publishPreviewNext"))),console.log(F(t))}function Be(e){if(e.kind==="hook")return`${v(`${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 x("git",["rev-parse","--verify","HEAD"],e)).code!==0)return null;let r;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 m=c.stdout.trim();if((await x("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((m)=>m.startsWith("Change-Versions-Source:"))){let m=/^Change-Versions-Source: (\S+) ([a-f0-9]{40}|[a-f0-9]{64})$/.exec(c);if(!m||a.has(m[1])||!s.some((d)=>d.name===m[1]))throw new l(i("badReleaseCommit",{commit:r.hash}));a.set(m[1],m[2])}if(a.size&&a.size!==s.length)throw new l(i("badReleaseCommit",{commit:r.hash}));for(let c of new Set(a.values()))if((await x("git",["merge-base","--is-ancestor",c,r.hash],e)).code!==0)throw new l(i("badReleaseCommit",{commit:r.hash}));return{hash:r.hash,subject:r.subject,lines:s,distTag:o,sourceCommits:a}}async function Cn(e,t,n){await Me(e,n);let r=n.map((o)=>`${o.name}@${o.to}`);for(let o of n){let a=`${o.name}@${o.to}`;if((await x("git",["rev-parse","--verify",`refs/tags/${a}^{commit}`],e)).code===0)continue;await y("git",["tag",a,o.commit],e)}if(!t)return;let s=await x("git",["push","origin",...r.map((o)=>`refs/tags/${o}`)],e);if(s.code!==0)throw new l(i("tagPushFailed",{detail:s.stderr.trim()}))}async function Me(e,t){for(let n of t){let r=`${n.name}@${n.to}`,s=await x("git",["rev-parse","--verify",`refs/tags/${r}^{commit}`],e);if(s.code===0&&s.stdout.trim()!==n.commit)throw new l(i("tagPointsElsewhere",{tag:r}))}}async function Ae(e,t){if((await y("git",["rev-parse","HEAD"],e)).trim()!==t)throw new l(i("publishHeadChanged"))}async function It(e){let t="";for(let n=0;n<3;n++)try{await e();return}catch(r){if(r instanceof l)throw r;t=r instanceof Error?r.message:String(r)}throw new l(i("networkFailed",{detail:t}))}async function Nt(e,t,n){let r=await x("npm",t,e);if(r.code===0)return;let s=`${r.stderr}${r.stdout}`;if(process.stderr.write(r.stderr),ye(s))throw new l(i("notLoggedIn",{registry:n}),!0);throw Error(s.trim()||i("npmFailed"))}async function jt(e,t,n,r){let s=await x("npm",["view",`${t}@${n}`,"version","--json",`--registry=${r}`],e);if(s.code===0){let a;try{a=JSON.parse(s.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:r}))}let o=`${s.stderr}${s.stdout}`;if(ye(o))throw new l(i("notLoggedIn",{registry:r}),!0);if(ve(s.stdout)||ve(s.stderr))return!1;throw Error(o.trim()||i("npmViewFailed"))}async function Mt(e,t,n,r,s=!1){let o=await x("npm",["view",t,"dist-tags","--json",`--registry=${r}`],e);if(o.code!==0){let m=`${o.stderr}${o.stdout}`.trim();if(ye(m))throw new l(i("notLoggedIn",{registry:r}),!0);if(s&&(ve(o.stdout)||ve(o.stderr)))return;throw Error(m||i("npmViewFailed"))}let a;try{a=JSON.parse(o.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:r}));let c=Object.prototype.hasOwnProperty.call(a,n)?a[n]:void 0;if(c!==void 0&&(typeof c!=="string"||!je.valid(c)))throw Error(i("unexpectedRegistryTags",{name:t,tag:n,registry:r}));return c}function Ht(e,t,n,r){if(n&&je.gt(n,t))throw new l(i("distTagDowngrade",{name:e,version:t,current:n,tag:r}))}async function Sn(e,t){let n=await x("npm",["whoami",`--registry=${t}`],e);if(n.code===0)return;let r=`${n.stderr}${n.stdout}`;if(ye(r))throw new l(i("notLoggedIn",{registry:t}),!0);throw new l(i("registryUnreachable",{registry:t,detail:r.trim()}))}async function Rn(e,t){if(t.publishConfig?.registry)return t.publishConfig.registry.replace(/\/$/,"");if(t.name.startsWith("@")&&t.name.includes("/")){let r=t.name.slice(0,t.name.indexOf("/")),s=await Et(e,`${r}:registry`);if(s&&s!=="undefined"&&s!=="null")return s.replace(/\/$/,"")}let n=await Et(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 Et(e,t){let n=await x("npm",["config","get",t],e);if(n.code!==0){let r=n.stderr.trim()||n.stdout.trim()||`npm config get ${t}: exit ${n.code}`;throw new l(i("noNpmRegistry",{detail:r}))}return n.stdout.trim()}async function At(e,t,n,r){if(!t)return;console.log(S(i("executingStep",{step:`${n}: ${t}`})));try{await Ee("sh",["-c",t],e,r)}catch(s){let o=s instanceof Error?s.message:String(s);throw new l(i("hookFailed",{name:n,detail:o}))}}async function xe(e,t,n){return t??(await j(Ie.resolve(e),n))?.distTag??"latest"}var K=G(()=>{ke();I();D();N();W();_();Ft();ce()});import be from"node:fs";import Oe from"node:path";function he(e,t){let n=Oe.resolve(e,t);if(!be.existsSync(n))throw new l(i("configUnreadable",{file:t}));return Fe(be.readFileSync(n,"utf8"))}function ee(e,t){let n=Oe.join(e,t);if(!be.existsSync(n))throw new l(i("packagesDirMissing",{dir:t}));return be.readdirSync(n).filter((r)=>!r.startsWith(".")&&be.existsSync(Oe.join(n,r,"package.json")))}function A(e,t,n){let r=JSON.parse(be.readFileSync(Oe.join(e,t,n,"package.json"),"utf8"));if(!r.name||!r.version)throw new l(i("packageJsonIncomplete",{folder:n}));return r}function He(e,t){let n=[];for(let r of["dependencies","devDependencies"])for(let s of Object.keys(e[r]??{})){let o=t.get(s);if(o)n.push(o)}return n}async function V(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 Pe(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 Ve(e){return(await y("git",["log","--format=%H%x1f%s"],e)).split(`
|
|
30
|
-
`).filter(Boolean).map((n)=>{let[r,s]=n.split("\x1F");return{hash:r,subject:s??""}})}async function fe(e){let t=await y("git",["log","-F",`--grep=${ue}`,"--format=%H%x1f%s%x1f%b%x1e"],e),n=[];for(let r of t.split("\x1E")){let s=r.replace(/^\n/,"").trim();if(!s)continue;let[o,a,c]=s.split("\x1F");if(!o)continue;n.push({hash:o,subject:a??"",body:c??""})}return n}async function te(e,t){if(t){let o=await x("git",["rev-parse","--verify",`${t}^{commit}`],e);if(o.code!==0)throw new l(i("badBaseline",{rev:t}));return o.stdout.trim()}if((await x("git",["rev-parse","--verify","HEAD"],e)).code!==0)throw new l(i("noBaseline"));let r=await fe(e),s=(await Ve(e)).find((o)=>et(o.subject));if(r[0]){if(s&&s.hash!==r[0].hash&&(await x("git",["merge-base","--is-ancestor",r[0].hash,s.hash],e)).code===0)return s.hash;return r[0].hash}return s?.hash??Tn()}async function Ge(e,t,n,r=!n){let s=await te(e,n),o=ee(e,t.packagesDir),a=o.map((u)=>({...A(e,t.packagesDir,u),folder:u})),c=await zn(e,a,s,r),m=new Map,d=new Map;for(let u of o){let w=c.get(u);d.set(w,[...d.get(w)??[],u])}for(let[u,w]of d){let p=await y("git",["diff","--name-only",u,"HEAD","--",...w.map((P)=>`${t.packagesDir}/${P}`)],e);for(let[P,b]of ht(p,t.packagesDir,w))m.set(P,b)}let h=new Set;for(let[u,w]of m){let p=c.get(u),P=`${t.packagesDir}/${u}/package.json`;if(w.includes(P)&&await ot(e,p,"HEAD",P))h.add(u)}let{changed:C,dependents:f}=Ct({packages:a,filesByFolder:m,devOnly:h,include:t.include,exclude:t.exclude,packagesDir:t.packagesDir});return{baseline:s,changed:C,dependents:f}}async function zn(e,t,n,r){let s=new Map(t.map((f)=>[f.folder,n]));if(!r)return s;let o=await fe(e);if(!o.length||o[0].hash!==n)return s;let a=(await Ve(e)).find((f)=>et(f.subject))?.hash,c=o[o.length-1].hash,m=await x("git",["rev-parse","--verify",`${c}^`],e),d=a??(m.code===0?m.stdout.trim():c);for(let f of t)s.set(f.folder,d);let h=new Map(t.map((f)=>[f.name,f.folder]));await Promise.resolve().then(() => K());for(let f of o){if(!h.size)break;if(!de(f.body).some((w)=>h.has(w.name)))continue;let u=await j(e,f.hash);if(!u)continue;for(let w of u.lines){let p=h.get(w.name);if(!p)continue;let P=u.sourceCommits.get(w.name)??u.hash,b=a&&(await x("git",["merge-base","--is-ancestor",P,a],e)).code===0;s.set(p,b?a:P),h.delete(w.name)}}return s}async function ot(e,t,n,r){let s=await Ot(e,`${t}:${r}`),o=await Ot(e,`${n}:${r}`);if(s===null||o===null)return!1;try{return bt(JSON.parse(s),JSON.parse(o))}catch{return!1}}async function Ot(e,t){let n=await x("git",["show",t],e);return n.code===0?n.stdout:null}function Tn(){throw new l(i("noBaseline"))}var ce=G(()=>{D();N();W();St()});import{CommitParser as On}from"conventional-commits-parser";import{writeChangelogString as Vn}from"conventional-changelog-writer";function Kt(e){return e.find((t)=>Ne(de(t.body)))?.hash}function Yt(e,t,n,r,s,o){let a=`${n}/${t}/package.json`,c=`${n}/${t}/`,m=[];for(let d of e)if(d.files.some((C)=>C!==a&&C.startsWith(c)&&Ze(C,r,s))||d.files.includes(a)&&o.has(d.hash))m.push({hash:d.hash,body:d.body});return m}async function Qt(e){let t=new Date().toISOString().slice(0,10),n=[`## ${e.version} (${t})`];if(e.previousTag){let o=`../../compare/${encodeURIComponent(e.previousTag)}...${encodeURIComponent(e.currentTag)}`;n.push(`[${e.previousTag}...${e.currentTag}](${o})`)}let r=new On({noteKeywords:["BREAKING CHANGE","BREAKING-CHANGE"]}),s=[];for(let o of e.messages){let a;try{a=r.parse(o.body)}catch{continue}s.push({...a,hash:o.hash.slice(0,7),notes:a.notes??[],subject:a.subject})}if(s.length){let a=(await Vn(s,{version:e.version,date:t},{groupBy:"type",mainTemplate:Ln,transform(c){let d=(c.notes??[]).some((C)=>/breaking/i.test(C.title)),h=c.type??"";if(Gn.has(h)&&!d)return null;if(!Jt[h]&&!d)return null;return{type:Jt[h]??null}}})).trim();if(a)n.push("",a);else if(e.dependencies.length)n.push("",_t(e.dependencies))}else if(e.dependencies.length)n.push("",_t(e.dependencies).trimEnd());return`${n.join(`
|
|
24
|
+
{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:"↑/↓ to select, Enter to confirm. 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 lt from"picomatch";import we from"semver";import{YAMLParseError as gn,parse as dn}from"yaml";function Ye(e){return mt.has(e)}function ee(e){return Boolean(e)&&encodeURIComponent(e)===e&&!we.validRange(e)}function Ee(e,t){if(!gt.includes(e))throw new g(i("badBump",{bump:e,list:gt.join(", ")}));let n=e;if(mn.has(n)&&!t)throw new g(i("preidRequired",{kind:n}));if(mt.has(n)&&t)throw new g(i("preidForbidden"));return n}function Ne(e,t){let n=bn(e),r=Object.keys(n).filter((c)=>ut.has(c));if(ve(n.test)&&"global"in n.test)r.push("test.global");if(r.length)throw new g(i("configFieldsMoved",{fields:r.map((c)=>c==="test.global"?"test.global → test.allPackagesInputs":`${c} → publish.${c}`).join(", ")}));for(let c of Object.keys(n))if(!un.has(c))throw new g(i("unknownField",{name:c}));let o=t??(n.packagesDir===void 0?"packages":_e(n.packagesDir,"packagesDir")),s=hn(n.publish),a=n.test===void 0?void 0:fn(n.test,o);return{packagesDir:o,publish:s,...a?{test:a}:{}}}function hn(e){if(e===void 0)throw new g(i("includeRequired"));if(!ve(e))throw new g(i("publishConfigInvalid"));let t=e;for(let d of Object.keys(t))if(!ut.has(d))throw new g(i("unknownField",{name:`publish.${d}`}));let n=t.commitMessage===void 0?"bump: %s":_e(t.commitMessage,"publish.commitMessage"),r=t.branch===void 0?void 0:_e(t.branch,"publish.branch"),o=t.hooks==null?{}:wn(t.hooks),s=t.include==null?[]:be(t.include,"publish.include"),a=t.exclude==null?[]:be(t.exclude,"publish.exclude");if(!s.length)throw new g(i("includeRequired"));if(!n.includes("%s"))throw new g(i("commitMessageNeedsToken"));for(let d of[...s,...a]){if(d.includes("!"))throw new g(i("patternBang",{pattern:d}));if(/\[[^\]]+\]/.test(d))throw new g(i("patternClass",{pattern:d}))}let c=t.commitFiles==null?void 0:be(t.commitFiles,"publish.commitFiles"),l=t.distTags===void 0?void 0:be(t.distTags,"publish.distTags");if(l?.some((d)=>!ee(d)))throw new g(i("badDistTag"));return{commitMessage:n,branch:r,hooks:o,include:s,exclude:a,...l?{distTags:[...new Set(l)]}:{},...c?{commitFiles:c}:{}}}function fn(e,t){if(!ve(e))throw new g(i("testConfigInvalid",{field:"test"}));if("global"in e)throw new g(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 g(i("unknownField",{name:`test.${a}`}));let r=(a,c)=>{let l=e[a]===void 0?c:be(e[a],`test.${a}`);for(let d of l){if(d.includes("!"))throw new g(i("patternBang",{pattern:d}));if(/\[[^\]]+\]/.test(d))throw new g(i("patternClass",{pattern:d}))}return l},o=r("include",[`${t}/*/**`]);if(e.command!==void 0&&(typeof e.command!=="string"||!e.command.trim()))throw new g(i("testConfigInvalid",{field:"test.command"}));if(e.dependents!==void 0&&typeof e.dependents!=="boolean")throw new g(i("testConfigInvalid",{field:"test.dependents"}));let s=e.concurrency===void 0?4:e.concurrency;if(typeof s!=="number"||!Number.isSafeInteger(s)||s<1)throw new g(i("testConfigInvalid",{field:"test.concurrency"}));return{...e.command===void 0?{}:{command:e.command},include:o,exclude:r("exclude",[]),allPackagesInputs:r("allPackagesInputs",["package.json","bun.lock","bun.lockb","package-lock.json","npm-shrinkwrap.json","yarn.lock","pnpm-lock.yaml","pnpm-workspace.yaml","tsconfig.json"]),dependentInputs:r("dependentInputs",o),dependents:e.dependents===void 0?!0:e.dependents,concurrency:s}}function bn(e){try{let t=dn(e);if(t==null)return{};if(!ve(t))throw new g(i("badLine",{line:1}));return t}catch(t){if(t instanceof g)throw t;if(t instanceof gn)throw new g(i("badLine",{line:t.linePos?.[0]?.line??1}));throw t}}function wn(e){if(!ve(e))throw new g(i("badHook",{line:"hooks"}));let t={};for(let[n,r]of Object.entries(e)){if(!pn.has(n))throw new g(i("unknownHook",{name:n}));if(typeof r!=="string"||!r)throw new g(i("hookMissingCommand",{name:n}));t[n]=r}return t}function be(e,t){if(!Array.isArray(e))throw new g(i("badPatternLine",{line:t}));return e.map((n)=>{if(typeof n!=="string"||!n)throw new g(i("badPatternLine",{line:t}));return n})}function _e(e,t){if(typeof e!=="string"||!e)throw new g(i("missingValue",{name:t}));return e}function ve(e){return Boolean(e)&&typeof e==="object"&&!Array.isArray(e)}function ye(e,t){let n={dot:!0,flags:"s"};if(lt(t,n)(e))return!0;if(t.endsWith("/**"))return!1;return lt(`${t}/**`,n)(e)}function ke(e,t,n){return t.some((r)=>ye(e,r))&&!n.some((r)=>ye(e,r))}function pt(e,t,n){let r=new Set(n),o=`${t}/`,s=new Map;for(let a of e.split(`
|
|
25
|
+
`)){if(!a.startsWith(o))continue;let c=a.slice(o.length),l=c.split("/")[0];if(!l||!c.includes("/")||!r.has(l))continue;let d=s.get(l)??[];d.push(a),s.set(l,d)}return s}function ht(e,t,n,r,o,s){let a=[],c=[];for(let l of e){let d=t.get(l);if(!d?.length){c.push(l);continue}let p=`${s}/${l}/package.json`;if(d.some((b)=>b!==p&&ke(b,n,r))||d.includes(p)&&!o(l))a.push(l);else c.push(l)}return{changed:a,ignored:c}}function ft(e,t){return JSON.stringify(Fe(dt(e)))===JSON.stringify(Fe(dt(t)))}function dt(e){if(!e||typeof e!=="object"||Array.isArray(e))return e;let t={...e};return delete t.devDependencies,t}function Fe(e){if(Array.isArray(e))return e.map(Fe);if(e&&typeof e==="object"){let t=e;return Object.fromEntries(Object.keys(t).sort().map((n)=>[n,Fe(t[n])]))}return e}function bt(e,t,n){let r=n?we.inc(e,t,n):we.inc(e,t);if(!r)throw new g(i("badSemver",{version:e}));return r}function wt(e,t,n){let r=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 s=[...t];while(s.length){let a=s.pop();if(!a)break;for(let c of o.get(a)??[]){if(r.has(c))continue;r.add(c),s.push(c)}}return e.filter((a)=>r.has(a))}function yt(e,t,n){let r=e.replace("%s",t);return n?`${r} ${n}`:r}function Ae(e){return e.length>0&&e.every((t)=>Boolean(we.valid(t.to))&&we.prerelease(t.to)==null)}function Qe(e){return/^v\d/.test(e)}function vt(e){return/^v\d+\.\d+\.\d+$/.test(e)}function Be(e){let t=[];for(let n of e.split(`
|
|
26
|
+
`)){let r=/^(\S+) (\S+) => (\S+)$/.exec(n.trim());if(r)t.push({name:r[1],from:r[2],to:r[3]})}return t}function kt(e){let t=new Map(e.map((s)=>[s.folder,s])),n=new Set,r=[],o=(s,a)=>{if(n.has(s.folder))return;if(a.has(s.folder))throw new g(i("dependencyCycle",{name:s.name}));a.add(s.folder);for(let c of s.deps){let l=t.get(c);if(l)o(l,a)}a.delete(s.folder),n.add(s.folder),r.push(s)};for(let s of e)o(s,new Set);return r}function Pe(e){return/ENEEDAUTH|E401|E403|need auth|not logged in|401 Unauthorized|403 Forbidden/i.test(e)}function xe(e){try{return JSON.parse(e)?.error?.code==="E404"}catch{return!1}}var g,gt,mt,mn,un,ut,pn,yn="publish@change-versions.com",vn,ce;var D=Je(()=>{T();g=class g extends Error{auth;constructor(e,t=!1){super(e);this.name="Fail",this.auth=t}};gt=["patch","minor","major","prerelease","prepatch","preminor","premajor"],mt=new Set(["patch","minor","major"]),mn=new Set(["prerelease","prepatch","preminor","premajor"]);un=new Set(["packagesDir","publish","test"]),ut=new Set(["commitMessage","branch","hooks","include","exclude","commitFiles","distTags"]),pn=new Set(["beforeCommit","beforePublish","afterPublish"]);vn=`change-versions <${yn}>`,ce=`Co-authored-by: ${vn}`});import{CommitParser as Xn}from"conventional-commits-parser";import{writeChangelogString as Zn}from"conventional-changelog-writer";function Zt(e,t,n,r,o,s){let a=`${n}/${t}/package.json`,c=`${n}/${t}/`,l=[];for(let d of e)if(d.files.some((w)=>w!==a&&w.startsWith(c)&&ke(w,r,o))||d.files.includes(a)&&s.has(d.hash))l.push({hash:d.hash,body:d.body});return l}async function en(e){let t=new Date().toISOString().slice(0,10),n=[`## ${e.version} (${t})`];if(e.previousTag){let s=`../../compare/${encodeURIComponent(e.previousTag)}...${encodeURIComponent(e.currentTag)}`;n.push(`[${e.previousTag}...${e.currentTag}](${s})`)}let r=new Xn({noteKeywords:["BREAKING CHANGE","BREAKING-CHANGE"]}),o=[];for(let s of e.messages){let a;try{a=r.parse(s.body)}catch{continue}o.push({...a,hash:s.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((w)=>/breaking/i.test(w.title)),p=c.type??"";if(ei.has(p)&&!d)return null;if(!Qt[p]&&!d)return null;return{type:Qt[p]??null}}})).trim();if(a)n.push("",a);else if(e.dependencies.length)n.push("",Xt(e.dependencies))}else if(e.dependencies.length)n.push("",Xt(e.dependencies).trimEnd());return`${n.join(`
|
|
31
27
|
`).trim()}
|
|
32
|
-
`}function
|
|
28
|
+
`}function Xt(e){return`### Dependencies
|
|
33
29
|
|
|
34
30
|
* update dependencies ${e.map((t)=>`${t.name}@${t.version}`).join(", ")}
|
|
35
|
-
`}var
|
|
31
|
+
`}var ei,Qt,ti=`{{#each commitGroups}}
|
|
36
32
|
{{#if title}}
|
|
37
33
|
### {{title}}
|
|
38
34
|
|
|
@@ -50,23 +46,28 @@ For committed fixes to unpublished packages, append --retry-unpublished to keep
|
|
|
50
46
|
{{/each}}
|
|
51
47
|
|
|
52
48
|
{{/each}}
|
|
53
|
-
`;var
|
|
49
|
+
`;var tn=Je(()=>{D();ei=new Set(["docs","style","chore","refactor","test","build","ci"]),Qt={feat:"Features",feature:"Features",fix:"Bug Fixes",perf:"Performance Improvements",revert:"Reverts"}});T();D();import $t from"node:fs";import Sn from"node:path";T();D();import{spawn as Pt}from"node:child_process";import{AsyncLocalStorage as kn}from"node:async_hooks";var K=new kn,Ie=()=>new Promise(()=>{});async function le(e){if(K.getStore())return e();let t={interrupted:!1,children:new Set,groups:new Set,recover:new Set},n=()=>{if(t.interrupted)return;t.interrupted=!0,(async()=>{let r=[...t.children];for(let s of r)if(process.platform==="win32"&&s.pid)Pt("taskkill",["/pid",String(s.pid),"/T","/F"],{stdio:"ignore"}).on("error",()=>s.kill());let o=(s)=>{for(let a of t.groups)try{process.kill(-a,s)}catch{}};if(o("SIGTERM"),r.length)await new Promise((s)=>setTimeout(s,2000));o("SIGKILL"),await Promise.all(r.filter((s)=>s.exitCode===null&&s.signalCode===null).map((s)=>new Promise((a)=>{s.once("close",()=>a()),s.kill("SIGKILL")})));for(let s of[...t.recover].reverse())try{s()}catch(a){console.error(a)}process.exit(1)})()};process.on("SIGINT",n),process.on("SIGTERM",n);try{return await K.run(t,async()=>{try{let r=await e();return t.interrupted?await Ie():r}catch(r){if(t.interrupted)return await Ie();throw r}})}finally{process.off("SIGINT",n),process.off("SIGTERM",n)}}function ge(e){let t=K.getStore();if(!t)throw Error("A process scope is required for interrupt recovery");return t.recover.add(e),()=>{t.recover.delete(e)}}function xt(e,t,n,r,o,s=!1){let a=K.getStore(),c=Pt(e,t,{cwd:n,stdio:r,shell:s,env:{...process.env,...o},detached:Boolean(a)&&process.platform!=="win32"});if(a?.children.add(c),a&&process.platform!=="win32"&&c.pid)a.groups.add(c.pid);return c.once("close",()=>{if(a?.children.delete(c),a&&!a.interrupted&&c.pid)a.groups.delete(c.pid)}),c}async function P(e,t,n){if(K.getStore()?.interrupted)return Ie();return new Promise((r)=>{let o=K.getStore(),s=xt(e,t,n,["ignore","pipe","pipe"]),a="",c="";s.stdout.on("data",(l)=>{a+=l}),s.stderr.on("data",(l)=>{c+=l}),s.on("error",(l)=>{if(!o?.interrupted)r({code:1,stdout:a,stderr:`${c}${l.message}`})}),s.on("close",(l)=>{if(!o?.interrupted)r({code:l??1,stdout:a,stderr:c})})})}async function y(e,t,n){let r=await P(e,t,n);if(r.code!==0){let o=(r.stderr||r.stdout).trim();throw new g(o?i("commandFailed",{command:e,detail:o}):i("commandFailedShort",{command:e}))}return r.stdout}async function de(e,t,n,r,o=!1){if(K.getStore()?.interrupted)return Ie();return new Promise((s,a)=>{let c=K.getStore(),l=xt(e,t,n,"inherit",r,o);l.on("error",(d)=>{if(!c?.interrupted)a(d)}),l.on("close",(d)=>{if(c?.interrupted)return;if(d===0)s();else a(new g(i("commandExit",{command:e,code:d??1})))})})}D();import Pn from"node:path";import St from"semver";T();async function Xe(e){return(await y("git",["log","--format=%H%x1f%s"],e)).split(`
|
|
50
|
+
`).filter(Boolean).map((n)=>{let[r,o]=n.split("\x1F");return{hash:r,subject:o??""}})}async function Me(e){let t=await y("git",["log","-F",`--grep=${ce}`,"--format=%H%x1f%s%x1f%b%x1e"],e),n=[];for(let r of t.split("\x1E")){let o=r.replace(/^\n/,"").trim();if(!o)continue;let[s,a,c]=o.split("\x1F");if(!s)continue;n.push({hash:s,subject:a??"",body:c??""})}return n}async function te(e,t){if(t){let s=await P("git",["rev-parse","--verify",`${t}^{commit}`],e);if(s.code!==0)throw new g(i("badBaseline",{rev:t}));return s.stdout.trim()}if((await P("git",["rev-parse","--verify","HEAD"],e)).code!==0)throw new g(i("noBaseline"));let r=await Me(e),o=(await Xe(e)).find((s)=>Qe(s.subject));if(r[0]){if(o&&o.hash!==r[0].hash&&(await P("git",["merge-base","--is-ancestor",r[0].hash,o.hash],e)).code===0)return o.hash;return r[0].hash}if(o)return o.hash;throw new g(i("noBaseline"))}async function N(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 g(i("badReleaseCommit",{commit:t}));let l=c.stdout.trim();if((await P("git",["merge-base","--is-ancestor",l,"HEAD"],e)).code!==0)throw new g(i("badReleaseCommit",{commit:t}));r={hash:l,subject:(await y("git",["show","-s","--format=%s",l],e)).trim(),body:await y("git",["show","-s","--format=%b",l],e)}}else r=(await Me(e))[0];if(!r)return null;let o=Be(r.body);if(!o.length){if(t)throw new g(i("badReleaseCommit",{commit:t}));return null}if(new Set(o.map((c)=>c.name)).size!==o.length||o.some((c)=>!St.valid(c.from)||!St.valid(c.to)))throw new g(i("badReleaseCommit",{commit:r.hash}));let s=/^Change-Versions-Dist-Tag: (\S+)$/m.exec(r.body)?.[1],a=new Map;for(let c of r.body.split(`
|
|
51
|
+
`).filter((l)=>l.startsWith("Change-Versions-Source:"))){let l=/^Change-Versions-Source: (\S+) ([a-f0-9]{40}|[a-f0-9]{64})$/.exec(c);if(!l||a.has(l[1])||!o.some((d)=>d.name===l[1]))throw new g(i("badReleaseCommit",{commit:r.hash}));a.set(l[1],l[2])}if(a.size&&a.size!==o.length)throw new g(i("badReleaseCommit",{commit:r.hash}));for(let c of new Set(a.values()))if((await P("git",["merge-base","--is-ancestor",c,r.hash],e)).code!==0)throw new g(i("badReleaseCommit",{commit:r.hash}));return{hash:r.hash,subject:r.subject,lines:o,distTag:s,sourceCommits:a}}async function Ct(e,t,n,r=!n){let o=await te(e,n),s=await xn(e,t,o,r);return{baseline:o,baselines:s}}async function xn(e,t,n,r){let o=new Map(t.map((w)=>[w.folder,n]));if(!r)return o;let s=await Me(e);if(!s.length||s[0].hash!==n)return o;let a=(await Xe(e)).find((w)=>Qe(w.subject))?.hash,c=s[s.length-1].hash,l=await P("git",["rev-parse","--verify",`${c}^`],e),d=a??(l.code===0?l.stdout.trim():c);for(let w of t)o.set(w.folder,d);let p=new Map(t.map((w)=>[w.name,w.folder]));for(let w of s){if(!p.size)break;if(!Be(w.body).some((h)=>p.has(h.name)))continue;let b=await N(e,w.hash);if(!b)continue;for(let h of b.lines){let m=p.get(h.name);if(!m)continue;let v=b.sourceCommits.get(h.name)??b.hash,S=a&&(await P("git",["merge-base","--is-ancestor",v,a],e)).code===0;o.set(m,S?a:v),p.delete(h.name)}}return o}async function zt(e,t){let n=await Me(e),r=(await Xe(e)).find((o)=>vt(o.subject))?.hash;return n.find((o)=>Ae(Be(o.body)))?.hash??r??(t?await te(e,t):void 0)}function je(e,t,n){return e.map((r)=>`${r.name} ${r.from} => ${r.to}`).join(`
|
|
52
|
+
`)+(t?`
|
|
53
|
+
|
|
54
|
+
Change-Versions-Dist-Tag: ${t}`:"")+(n?`
|
|
55
|
+
`+e.map((r)=>`Change-Versions-Source: ${r.name} ${n.get(r.name)}`).join(`
|
|
56
|
+
`):"")}async function Se(e,t,n){return t??(await N(Pn.resolve(e),n))?.distTag??"latest"}D();T();import me from"node:fs";import Ce from"node:path";class Ze{packagesDir;source;packages;byName;byFolder;constructor(e,t,n){this.packagesDir=e;this.source=t;this.packages=n;this.byName=new Map(n.map((r)=>[r.name,r])),this.byFolder=new Map(n.map((r)=>[r.folder,r]))}named(e){return this.byName.get(e)}folder(e){return this.byFolder.get(e)}path(e){return`${this.packagesDir}/${e}`}dependencies(e,t){let n=this.folder(e);return t.flatMap((r)=>Object.keys(n?.[r]??{}).flatMap((o)=>{let s=this.named(o);return s?[s]:[]}))}}function Tt(e,t){let n=Ce.join(e,t);if(!me.existsSync(n))throw new g(i("packagesDirMissing",{dir:t}));return me.readdirSync(n).filter((r)=>!r.startsWith(".")&&me.existsSync(Ce.join(n,r,"package.json"))).map((r)=>({folder:r,text:me.readFileSync(Ce.join(n,r,"package.json"),"utf8")}))}function J(e,t){return new Ze(t,"worktree",Tt(e,t).map(({folder:n,text:r})=>{let o=JSON.parse(r);if(!o.name||!o.version)throw new g(i("packageJsonIncomplete",{folder:n}));return{...o,folder:n}}))}async function ze(e,t,n="worktree"){let r;if(n==="worktree"){r=Tt(e,t);for(let{folder:s}of r)if(!me.realpathSync(Ce.join(e,t,s)).startsWith(me.realpathSync(e)+Ce.sep))throw new g(i("testRootRequired"))}else{let s=(await y("git",n===":"?["ls-files","-z","--",t]:["ls-tree","-r","--name-only","-z",n,"--",t],e)).split("\x00").filter(Boolean);r=[];let a=`${t}/`;for(let c of s){if(!c.startsWith(a))continue;let l=c.slice(a.length).split("/");if(l.length!==2||l[1]!=="package.json"||l[0].startsWith("."))continue;r.push({folder:l[0],text:await y("git",["show",`${n===":"?"":n}:${c}`],e)})}}let o=r.map(({folder:s,text:a})=>{try{let c=JSON.parse(a);if(!c||typeof c.name!=="string"||!c.name)throw Error("name");for(let l of["dependencies","devDependencies","peerDependencies","optionalDependencies","scripts"]){let d=c[l];if(d!==void 0&&(!d||typeof d!=="object"||Array.isArray(d)||Object.values(d).some((p)=>typeof p!=="string")))throw Error(l)}return{...c,folder:s}}catch{throw new g(i("testManifestInvalid",{folder:s}))}});return new Ze(t,n,o)}T();D();function Rt(e){let t=e.workspace,n=t.packages.map((a)=>a.folder),{changed:r}=ht(n,e.filesByFolder,e.include,e.exclude,(a)=>e.devOnly.has(a),t.packagesDir),o=wt(n,r,(a)=>t.dependencies(a,["dependencies","devDependencies"]).map((c)=>c.folder));for(let a of o){let c=t.folder(a);if(!c||c.private)continue;for(let l of Object.keys(c.dependencies??{}))if(t.named(l)?.private)throw new g(i("publicDependsOnPrivate",{name:c.name,dep:l}))}let s=new Set(r);return{changed:r,dependents:o.filter((a)=>!s.has(a))}}function I(e,t,n){let r=Sn.resolve(e,t);if(!$t.existsSync(r))throw new g(i("configUnreadable",{file:t}));return Ne($t.readFileSync(r,"utf8"),n)}async function M(e){let t=await P("git",["status","--porcelain"],e);if(t.code!==0)throw new g(i("gitFailed",{detail:t.stderr.trim()}));if(t.stdout.trim())throw new g(i("dirtyTree"))}async function ue(e,t){let n=await P("git",["status","--porcelain"],e);if(n.code!==0)throw new g(i("gitFailed",{detail:n.stderr.trim()}));if(n.stdout.trim())throw new g(t)}async function He(e,t,n,r=!n){let o=J(e,t.packagesDir),s=o.packages,a=s.map((m)=>m.folder),{baseline:c,baselines:l}=await Ct(e,s,n,r),d=new Map,p=new Map;for(let m of a){let v=l.get(m);p.set(v,[...p.get(v)??[],m])}for(let[m,v]of p){let S=await y("git",["diff","--name-only",m,"HEAD","--",...v.map((u)=>o.path(u))],e);for(let[u,k]of pt(S,t.packagesDir,v))d.set(u,k)}let w=new Set;for(let[m,v]of d){let S=l.get(m),u=`${o.path(m)}/package.json`;if(v.includes(u)&&await et(e,S,"HEAD",u))w.add(m)}let{changed:b,dependents:h}=Rt({workspace:o,filesByFolder:d,devOnly:w,include:t.publish.include,exclude:t.publish.exclude});return{baseline:c,changed:b,dependents:h,workspace:o}}async function et(e,t,n,r){let o=await Dt(e,`${t}:${r}`),s=await Dt(e,`${n}:${r}`);if(o===null||s===null)return!1;try{return ft(JSON.parse(o),JSON.parse(s))}catch{return!1}}async function Dt(e,t){let n=await P("git",["show",t],e);return n.code===0?n.stdout:null}import{Command as fi}from"commander";import{readFileSync as bi}from"node:fs";import zn from"node:path";import{styleText as Cn}from"node:util";function U(e,t,n){return Cn(e,t,{stream:n})}function z(e){return U(["bold","cyan"],e,process.stdout)}function _(e){return U("bold",e,process.stdout)}function ne(e){return U("green",e,process.stdout)}function x(e){return U("dim",e,process.stdout)}function E(e){return U("cyan",e,process.stdout)}function Ft(e,t,n){return`${_(e)} ${x(t)} ${x("=>")} ${ne(n)}`}function j(e){let[t,...n]=e.split(`
|
|
57
|
+
`),r=U("red",t,process.stderr);if(!n.length)return r;return[r,...n.map((o)=>U("yellow",o,process.stderr))].join(`
|
|
58
|
+
`)}function A(e){return U("green",e,process.stdout)}function C(e){return U("yellow",e,process.stdout)}function Oe(e){return U("red",e,process.stdout)}T();async function Et(e){let t=zn.resolve(e.root),n=I(t,e.versionsFile);if(e.packages)n.packagesDir=e.packages;await M(t);let{baseline:r,changed:o,dependents:s,workspace:a}=await He(t,n,e.baseline);if(!o.length&&!s.length){let c=(await y("git",["rev-parse","--short",r],t)).trim();console.log(x(i("noPackagesSince",{rev:c})));return}console.log(z(i("changedHeader")));for(let c of o){let l=a.folder(c);console.log(` ${_(l.name)}${l.private?` ${x(i("privateMark"))}`:""}`)}if(s.length){console.log(z(i("dependentsHeader")));for(let c of s){let l=a.folder(c);console.log(` ${ne(l.name)}${l.private?` ${x(i("privateMark"))}`:""}`)}}}D();import Te from"node:path";T();var Le=(e)=>e.split("\x00").filter(Boolean);function Tn(e,t,n,r,o,s=!1){let a=new Map,c=(d,p)=>{let w=a.get(d)??new Set;w.add(p),a.set(d,w)},l=n.filter((d)=>r.allPackagesInputs.some((p)=>ye(d,p)));if(s||l.length)for(let d of e){if(s)c(d.folder,"--all");for(let p of l)c(d.folder,i("testAllPackagesReason",{file:p}))}else{let d=new Set([...t,...e].map((b)=>b.folder)),p=new Set,w=`${o}/`;for(let b of n){if(!b.startsWith(w)||!ke(b,r.include,r.exclude))continue;let h=b.slice(w.length).split("/")[0];if(!d.has(h))continue;if(c(h,b),r.dependentInputs.some((m)=>ye(b,m)))p.add(h)}if(r.dependents){let b=new Map;for(let S of[...t,...e]){let u=b.get(S.name)??new Set;u.add(S.folder),b.set(S.name,u)}let h=new Map;for(let S of[...t,...e]){let u={...S.dependencies,...S.devDependencies,...S.peerDependencies,...S.optionalDependencies};for(let k of Object.keys(u))for(let f of b.get(k)??[]){let R=h.get(f)??new Set;R.add(S.folder),h.set(f,R)}}let m=new Set(p),v=[...p];for(let S=0;S<v.length;S++){let u=v[S];for(let k of h.get(u)??[]){if(k===u)continue;if(c(k,i("testDependencyReason",{name:u})),!m.has(k))m.add(k),v.push(k)}}}}return new Map(e.filter((d)=>a.has(d.folder)).map((d)=>[d.folder,[...a.get(d.folder)]]))}async function Nt(e){return le(()=>Rn(e))}async function Rn(e){if(e.all&&e.base)throw new g(i("testAllBaseConflict"));let t=Te.resolve(e.root),n=I(t,e.versionsFile,e.packages),r=n.test;if(!r)throw new g(i("testConfigMissing"));let o=e.concurrency===void 0?r.concurrency:Number(e.concurrency);if(!Number.isSafeInteger(o)||o<1)throw new g(i("testConfigInvalid",{field:"--concurrency"}));if((await y("git",["rev-parse","--show-prefix"],t)).trim())throw new g(i("testRootRequired"));let a="HEAD";if(e.base){let h=(await y("git",["rev-parse","--verify","--end-of-options",`${e.base}^{commit}`],t)).trim(),m=await P("git",["merge-base",h,"HEAD"],t);if(m.code!==0)throw new g(i("testBaseMissing",{base:e.base}));a=m.stdout.trim()}let c=[],l=[];if(!e.all){if(e.base)c=Le(await y("git",["diff","--name-only","--no-renames","-z",a,"HEAD","--"],t));else c=[...Le(await y("git",["diff","--cached","--name-only","--no-renames","-z","HEAD","--"],t)),...Le(await y("git",["diff","--name-only","--no-renames","-z","--"],t)),...Le(await y("git",["ls-files","--others","--exclude-standard","-z"],t))];if(l=[...(await ze(t,n.packagesDir,a)).packages],e.base)l.push(...(await ze(t,n.packagesDir,"HEAD")).packages);else l.push(...(await ze(t,n.packagesDir,":")).packages)}let d=[...(await ze(t,n.packagesDir)).packages].sort((h,m)=>h.folder<m.folder?-1:h.folder>m.folder?1:0),p={...r,allPackagesInputs:[...r.allPackagesInputs,Te.relative(t,Te.resolve(t,e.versionsFile)).split(Te.sep).join("/")]},w=Tn(d,l,[...new Set(c)],p,n.packagesDir,e.all);console.log(i("testPlan",{count:w.size,base:e.all?"--all":a}));let b=[];for(let h of d){let m=w.get(h.folder);if(!m){console.log(i("testUnaffected",{name:h.name}));continue}if(console.log(` ${h.name}: ${m.join("; ")}`),(!r.command||e.ifPresent)&&!h.scripts?.test?.trim())console.log(i("testNoScript",{name:h.name}));else b.push(h)}if(e.dryRun){console.log(i("testDryRun"));return}await $n(t,n.packagesDir,b,r.command??"npm run test",o)}async function $n(e,t,n,r,o){let s=[],a=0,c=0,l=ge(()=>{console.error(i("testInterrupted"))});try{let d=async()=>{while(c<n.length){let p=n[c++];console.log(i("testRunning",{name:p.name,command:r}));try{await de(r,[],Te.join(e,t,p.folder),void 0,!0),console.log(i("testPassed",{name:p.name})),a++}catch(w){console.error(w instanceof Error?w.message:String(w)),console.log(i("testFailed",{name:p.name})),s.push(p.name)}}};await Promise.all(Array.from({length:Math.min(o,n.length)},d))}finally{l()}if(console.log(i("testSummary",{passed:a,failed:s.length})),s.length)throw new g(i("testFailures",{names:s.join(", ")}))}import V from"node:fs";import se from"node:path";T();D();import Dn from"node:readline/promises";import{stdin as B,stdout as ie}from"node:process";T();D();class q extends g{constructor(){super(i("interactiveCancelled"))}}var Fn={positive:A,caution:C,muted:x};async function En(e){let t=Dn.createInterface({input:B,output:ie}),n=new AbortController,r=()=>n.abort();t.once("SIGINT",r),t.once("close",r);try{return await t.question(e,{signal:n.signal})}catch(o){if(n.signal.aborted)throw new q;throw o}finally{t.off("SIGINT",r),t.off("close",r),t.close()}}function F(){return Boolean(B.isTTY&&ie.isTTY)}async function At(e,t){if(!F())throw new g(i("bumpRequired"));if(!t.length)throw new g(i("bumpRequired"));console.log(z(e));let n=0,r="",o="",s=(c=!1)=>{if(c)ie.write(`\x1B[${t.length}A\r`);t.forEach((l,d)=>{let p=l.tone?Fn[l.tone](l.label):l.label;ie.write(`\x1B[2K${d===n?E("❯"):" "} ${E(String(d+1))}) ${p}
|
|
59
|
+
`)}),ie.write(`\x1B[2K${E(i("chooseIndex"))}${r}`)},a=B.isRaw;return new Promise((c,l)=>{let d=()=>{B.off("data",b),B.off("end",p),B.off("error",w),B.setRawMode(a),B.pause(),ie.write(`
|
|
60
|
+
`)},p=()=>{d(),l(new q)},w=(h)=>{d(),l(h)},b=(h)=>{o+=h.toString();let m=o.match(/\x1b(?:\[|O)?$/)?.[0]??"",v=o.slice(0,o.length-m.length);o=m;let S=v.match(/\x1b(?:\[|O)[AB]|[\s\S]/g)??[];for(let u of S){if(u.toLowerCase()==="q"||u==="\x03"||u==="\x04"){p();return}if(/^\x1b(?:\[|O)[AB]$/.test(u))n=(n+(u.endsWith("A")?-1:1)+t.length)%t.length,r="";else if(/^\d$/.test(u))r+=u;else if(u===""||u==="\b")r=r.slice(0,-1);else if(u==="\r"||u===`
|
|
61
|
+
`){let k=r?Number(r)-1:n;if(k>=0&&k<t.length){d(),c(k);return}ie.write(`
|
|
62
|
+
`),console.log(C(i("invalidChoice",{answer:r}))),r="",s();return}}s(!0)};B.on("data",b),B.once("end",p),B.once("error",w),B.setRawMode(!0),B.resume(),s()})}async function Bt(e,t){return t[await At(e,t.map((n)=>({label:n})))]}async function L(e,t){return t[await At(e,t)].value}async function H(e,t=""){if(!F())throw new g(i("yesRequired"));let n=t?` ${A(`[${t}]`)}`:"",r=(await En(`${z(e)}${n} `)).trim();if(r.toLowerCase()==="q")throw new q;return r||t}async function re(e){if(!F())throw new g(i("yesRequired"));if(console.log(z(e)),await L(i("confirmList"),[{value:"yes",label:i("confirmYes"),tone:"positive"},{value:"no",label:i("confirmNo"),tone:"muted"}])==="no")throw new q;return!0}function Nn(e){let t=new Set,n=[];for(let r of[...Mn(e),...jn(e)]){if(t.has(r))continue;t.add(r),n.push(r)}return n}function An(e,t=Ke()){let n=(s,a="")=>i(s,void 0,t).split(`
|
|
54
63
|
`).map((c)=>`${a}# ${c}`).join(`
|
|
55
|
-
`),r=(
|
|
56
|
-
`),
|
|
64
|
+
`),r=(s,a,c,l)=>[n(l," "),a?` ${s}: ${Re(a)}`:` # ${s}: ${c}`].join(`
|
|
65
|
+
`),o=[n("initNotePackagesDir"),`packagesDir: ${Re(e.packagesDir)}`,"","publish:",n("initNoteDistTags"," "),` distTags: ${JSON.stringify(e.distTags??["latest","alpha","beta","next"])}`,"",n("initNoteCommitMessage"," "),' commitMessage: "bump: %s"',"",n("initNoteBranch"," "),e.branch?` branch: ${Re(e.branch)}`:" # branch: main",""," hooks:",r("beforeCommit",e.beforeCommit,"bun install","initNoteBeforeCommit"),r("beforePublish",e.beforePublish,"bun run build","initNoteBeforePublish"),r("afterPublish",void 0,"echo published","initNoteAfterPublish"),"",n("initNoteInclude"," ")," include:",...e.include.map((s)=>` - ${Re(s)}`),` # - ${e.packagesDir}/*/bin`,"",n("initNoteExclude"," ")];if(e.exclude.length)o.push(" exclude:",...e.exclude.map((s)=>` - ${Re(s)}`));else o.push(" # exclude:",` # - ${e.packagesDir}/*/src/**/__tests__/**`);return`${o.join(`
|
|
57
66
|
`)}
|
|
58
|
-
`}async function Le(e){let t=le.resolve(e.root),n=le.resolve(t,e.versionsFile);if(U.existsSync(n))throw new l(i("configExists",{file:e.versionsFile}));let r=Dn(t),s=r[0]??"packages",o=e.yes?Nn(s):await En(e.versionsFile,r,s);if(!o.include.length)throw new l(i("includeRequired"));let a=Fn(o);Fe(a),U.mkdirSync(le.dirname(n),{recursive:!0}),U.writeFileSync(n,a),console.log(oe(i("initWrote",{file:e.versionsFile})))}function Nn(e){return{packagesDir:e,include:[`${e}/*/src`],exclude:[`${e}/*/src/**/__tests__/**`]}}async function En(e,t,n){if(!T())throw new l(i("yesRequired"));if(console.log(S(i("initIntro",{file:e}))),t.length)console.log(v(i("initFoundDirs",{dirs:t.join(", ")})));let r=await L(i("initPackagesDir"),n),s=Gt(await L(i("initInclude"),`${r}/*/src`)),o=Gt(await L(i("initExclude"),`${r}/*/src/**/__tests__/**`)),a=await L(i("initBranch")),c=await L(i("initBeforeCommit")),m=await L(i("initBeforePublish"));return{packagesDir:r,branch:a||void 0,beforeCommit:c||void 0,beforePublish:m||void 0,include:s,exclude:o}}function Gt(e){return e.split(",").map((t)=>t.trim()).filter(Boolean)}function Ce(e){return/^[A-Za-z0-9_./@*+-]+$/.test(e)?e:JSON.stringify(e)}function An(e){return jn(e).flatMap((t)=>{let n=t.replace(/\/\*\*?$/,"");if(!n||n===t||n.includes("*"))return[];return U.existsSync(le.join(e,n))?[n]:[]})}function Bn(e){if(!U.existsSync(e))return[];let t=[];for(let n of U.readdirSync(e)){if(n.startsWith(".")||n==="node_modules")continue;let r=le.join(e,n);if(!U.statSync(r).isDirectory())continue;if(In(r))t.push(n)}return t}function In(e){return U.readdirSync(e).some((t)=>U.existsSync(le.join(e,t,"package.json")))}function jn(e){let t=le.join(e,"package.json");if(!U.existsSync(t))return[];try{let n=JSON.parse(U.readFileSync(t,"utf8")),r=Array.isArray(n.workspaces)?n.workspaces:n.workspaces?.packages;return Array.isArray(r)?r.filter((s)=>typeof s==="string"):[]}catch{return[]}}K();ke();K();import Xn from"node:path";import tn from"semver";ke();import ge from"node:fs";import qe from"node:path";W();N();I();D();import me from"node:fs";import ne from"node:path";import{spawnSync as Mn}from"node:child_process";function st(e){try{let t=me.lstatSync(e);if(!t.isFile())throw new l(i("unsafeCommitFile",{file:e}));return{bytes:me.readFileSync(e),mode:t.mode&511}}catch(t){if(t.code==="ENOENT")return;throw t}}function Hn(e,t){return e===t||Boolean(e&&t&&e.mode===t.mode&&e.bytes.equals(t.bytes))}async function Lt(e,t,n){let r=(await y("git",["rev-parse","HEAD"],e)).trim();if(n&&r!==n)throw new l(i("versionHeadChanged"));let s=new Map;for(let f of new Set(t)){let u=ne.resolve(e,f),w=ne.relative(e,u);if(!w||w.startsWith("..")||ne.isAbsolute(w)||/[*?\[\]\\]/.test(f)||w.split(ne.sep).includes(".git"))throw new l(i("unsafeCommitFile",{file:f}));for(let p=ne.dirname(u);p!==e;p=ne.dirname(p))if(me.existsSync(p)&&me.lstatSync(p).isSymbolicLink())throw new l(i("unsafeCommitFile",{file:f}));s.set(w,st(u))}let o=new Map(s),a,c=!0,m=()=>{o=new Map([...s.keys()].map((f)=>[f,st(ne.join(e,f))]))},d=(...f)=>Mn("git",f,{cwd:e,encoding:"utf8"}),h=()=>{if(!c)return;c=!1,process.off("SIGINT",C),process.off("SIGTERM",C);let f=d("rev-parse","HEAD");if(f.status!==0||f.stdout.trim()!==r){console.error(H(i("versionHeadChanged")));return}if(a!==void 0&&d("write-tree").stdout.trim()===a)d("restore","--staged","--",...s.keys());for(let[u,w]of s){let p=ne.join(e,u);try{if(!Hn(st(p),o.get(u)))continue;if(w)me.writeFileSync(p,w.bytes),me.chmodSync(p,w.mode);else me.rmSync(p,{force:!0})}catch{}}},C=()=>{h(),console.error(H(i("versionRecovery"))),process.exit(1)};return process.on("SIGINT",C),process.on("SIGTERM",C),{checkpoint:m,async stage(){if((await y("git",["rev-parse","HEAD"],e)).trim()!==r)throw new l(i("versionHeadChanged"));if(await y("git",["diff","--cached","--name-only","-z"],e))throw new l(i("versionUnexpectedIndex"));let u=(await y("git",["diff","--name-only","-z"],e)).split("\x00").filter(Boolean),w=(await y("git",["ls-files","--others","--exclude-standard","-z"],e)).split("\x00").filter(Boolean),p=[...u,...w].filter((R)=>!s.has(R));if(p.length)throw new l(i("versionUnexpectedFiles",{files:p.join(", ")}));m();let P=[...new Set([...u,...w])];if(!P.length)throw new l(i("versionUnexpectedIndex"));if(await y("git",["add","--",...P],e),a=(await y("git",["write-tree"],e)).trim(),(await y("git",["diff","--cached","--name-only","-z"],e)).split("\x00").filter(Boolean).some((R)=>!s.has(R)))throw new l(i("versionUnexpectedIndex"))},async verifyCommit(){let f=(await y("git",["rev-parse","HEAD^"],e)).trim(),u=(await y("git",["rev-parse","HEAD^{tree}"],e)).trim();if(f!==r||u!==a)throw new l(i("versionHeadChanged"))},disarm(){c=!1,process.off("SIGINT",C),process.off("SIGTERM",C)},fail(f){throw h(),f}}}I();D();K();N();W();_();I();D();N();_();ce();import Ut from"semver";async function qt(e){let{bump:t,preid:n}=e;if(!t){if(e.yes||!T())throw new l(i("bumpRequired"));let r=n?"pre":await Z(i("chooseReleaseKind"),[{value:"formal",label:i("releaseKindFormal")},{value:"pre",label:i("releaseKindPre")}]);t=await $t(i("chooseBump"),r==="pre"?["prerelease","prepatch","preminor","premajor"]:["patch","minor","major"])}if(!t.startsWith("pre")||n||!["prerelease","prepatch","preminor","premajor"].includes(t))return{bump:De(t,n),preid:n};if(e.yes||!T())return{bump:De(t,n),preid:n};while(!n){let r=await L(i("choosePreid"),"alpha");if(Ut.valid(`0.0.0-${r}.0`))n=r;else console.log(k(i("invalidPreid")))}return{bump:De(t,n),preid:n}}function Ue(e){return Boolean(e)&&encodeURIComponent(e)===e&&!Ut.validRange(e)}async function Se(e,t,n){if(e!==void 0||n||!T()){let r=e??t;if(!Ue(r))throw new l(i("badDistTag"));return r}while(!0){let r=await L(i("chooseDistTag"),t);if(Ue(r))return r;console.log(k(i("badDistTag")))}}async function Wt(e,t,n){try{return await te(e,t)}catch(r){if(t||n||!T()||!(r instanceof l)||r.message!==i("noBaseline"))throw r}console.log(k(i("firstBaseline")));while(!0){let r=await L(i("chooseBaseline"));if(!r)continue;try{return await te(e,r)}catch(s){if(!(s instanceof l))throw s;console.log(k(s.message))}}}ce();async function We(e){return X(e.root,Boolean(e.dryRun),()=>Un(e))}async function Un(e){let t=qe.resolve(e.root),n=he(t,e.versionsFile);if(e.packages)n.packagesDir=e.packages;if(e.messageFromCli&&e.commitMessage)n.commitMessage=e.commitMessage;if(e.branchFromCli)n.branch=e.branch||void 0;if(await V(t),n.branch){let u=await x("git",["symbolic-ref","--quiet","--short","HEAD"],t);if(u.code!==0||u.stdout.trim()!==n.branch)throw new l(i("wrongBranch",{branch:n.branch}))}let r=await Wt(t,e.baseline,e.yes),s=(await y("git",["rev-parse","HEAD"],t)).trim(),o=e.force?{baseline:r,changed:await Zt(t,n),dependents:[]}:await Ge(t,n,r,!e.baseline);if(!o.changed.length&&!o.dependents.length){let u=(await y("git",["rev-parse","--short",o.baseline],t)).trim();if(console.log(v(i("noPackagesSince",{rev:u}))),console.log(v(i("noVersionChangesNext"))),e.yes||!T()||!await j(t)||!await se(i("confirmForceVersion")))return;o.changed=await Zt(t,n)}if(T()&&!e.yes){console.log(S(i("wizardCandidates")));for(let u of[...o.changed,...o.dependents]){let w=A(t,n.packagesDir,u);console.log(` ${w.name}${w.private?` ${v(i("privateMark"))}`:""}`)}}let{bump:a,preid:c}=await qt(e),m=[...o.changed,...o.dependents].map((u)=>{let w=A(t,n.packagesDir,u);return{folder:u,name:w.name,private:w.private,from:w.version,to:wt(w.version,a,c)}});if(e.dryRun)console.log(k(i("versionPreview")));for(let u of m)console.log(`${ct(u.name,u.from,u.to)}${u.private?` ${v(i("privateMark"))}`:""}`);if(m.some((u)=>u.private))console.log(v(i("privateVersionHint")));let d=e.preparePublish?await e.preparePublish({preid:c,config:n}):e.publishTag;if(e.dryRun){console.log(S(i("versionPreviewComplete")));return}if(!e.yes&&!await se(i("confirmVersions")))throw new l(i("cancelledVersion"));if(await V(t),(await y("git",["rev-parse","HEAD"],t)).trim()!==s)throw new l(i("versionHeadChanged"));let h=ee(t,n.packagesDir).map((u)=>`${n.packagesDir}/${u}/package.json`);if(Xe(a))h.push(...m.map((u)=>`${n.packagesDir}/${u.folder}/CHANGELOG.md`));h.push("bun.lock","bun.lockb","package-lock.json","npm-shrinkwrap.json","yarn.lock","pnpm-lock.yaml",...n.commitFiles??[]);let C=await Lt(t,h,s);try{try{for(let p of m)_n(t,n.packagesDir,p.folder,p.to);if(Kn(t,n.packagesDir),Xe(a))await qn(t,n,m,r)}finally{C.checkpoint()}if(n.hooks.beforeCommit)await Ee("sh",["-c",n.hooks.beforeCommit],t);let u=vt(n.commitMessage,a,c),w=m.map((p)=>`${p.name} ${p.from} => ${p.to}`).join(`
|
|
59
|
-
`)+(d?`
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`)
|
|
64
|
-
`).filter(Boolean):[]})}return s}async function Jn(e,t,n){return!await ot(e,`${t}^`,t,n)}function _n(e,t,n,r){let s=qe.join(e,t,n,"package.json"),o=ge.readFileSync(s,"utf8"),a=JSON.parse(o);if(typeof a.version!=="string")throw new l(i("missingVersionField",{folder:n}));a.version=r;let c=/\n([\t ]+)"/.exec(o)?.[1]??" ";ge.writeFileSync(s,JSON.stringify(a,null,c)+`
|
|
65
|
-
`)}function Kn(e,t){let n=ee(e,t),r=new Map(n.map((s)=>[A(e,t,s).name,A(e,t,s)]));for(let s of n){let o=qe.join(e,t,s,"package.json"),a=ge.readFileSync(o,"utf8"),c=JSON.parse(a),m=!1;for(let d of["dependencies","devDependencies"])for(let h of Object.keys(c[d]??{})){let C=r.get(h);if(!C)continue;let f=`^${C.version}`;if(c[d][h]!==f)c[d][h]=f,m=!0}if(m){let d=/\n([\t ]+)"/.exec(a)?.[1]??" ";ge.writeFileSync(o,JSON.stringify(c,null,d)+`
|
|
66
|
-
`)}}}ce();W();async function Je(e){let t=[],n=new Map,r;if(!e.formal||e.entries.every((s)=>s.pkg.private))return{entries:t,remoteError:r};if(e.config.branch){let s=e.entries.filter((a)=>!a.pkg.private).flatMap((a)=>[`refs/tags/${a.name}@${a.to}`,`refs/tags/${a.name}@${a.to}^{}`]),o=await x("git",["ls-remote","--tags","origin",...s],e.root);if(o.code!==0)r=o.stderr.trim()||o.stdout.trim()||`git ls-remote: exit ${o.code}`;else for(let a of o.stdout.trim().split(`
|
|
67
|
-
`).filter(Boolean)){let[c,m]=a.split(/\s+/);n.set(m,c)}}for(let s of e.entries){if(s.pkg.private)continue;let o=`${s.name}@${s.to}`,a=await x("git",["rev-parse","--verify",`refs/tags/${o}^{commit}`],e.root),c=n.get(`refs/tags/${o}^{}`)??n.get(`refs/tags/${o}`);t.push({tag:o,localReady:a.code===0&&a.stdout.trim()===s.commit,remoteReady:!e.config.branch||!r&&c===s.commit})}return{entries:t,remoteError:r}}N();D();I();I();N();D();K();_();I();D();K();import Yn from"node:path";W();async function ie(e){let t=Yn.resolve(e.root);if(await y("git",["rev-parse","--show-toplevel"],t),!await j(t,e.commit)){console.log(v(i("statusNoBatch")));return}console.log(S(i("statusHeading")));let n=await ae(e,!0);console.log(i("publishSummaryContext",{commit:n.release.hash,tag:e.distTag}));let r=!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")r=!1;console.log(` ${(c==="statusUpload"||c==="statusRetag"?k:v)(i(c))}: ${a.name}@${a.to}`)}let s=await Je(n);if(s.remoteError)r=!1,console.log(k(i("statusRemoteUnknown",{detail:s.remoteError})));for(let{tag:a,localReady:c,remoteReady:m}of s.entries){if(!c||!m)r=!1;console.log((c&&m?v:k)(i("statusGitTag",{tag:a,local:c?i("statusReady"):i("statusMissing"),remote:n.config.branch?m?i("statusReady"):i("statusMissing"):i("statusNotConfigured")})))}let o=await x("git",["status","--porcelain"],t);if(o.code!==0||o.stdout.trim())r=!1,console.log(k(i("dirtyTree")));console.log((r?M:k)(i(r?"statusExternalReady":"statusPending"))),console.log(v(i("statusHooksUnknown"))),console.log(F(O({...e,root:t,commit:n.release.hash})))}ke();I();D();N();W();_();K();ce();async function en(e,t){console.log(S(i("repairPublication")));let n=await ae(e,!1,!0),{root:r,startHead:s,config:o,release:a,entries:c,changed:m,published:d}=n;for(let p of c){if(!m.has(p.folder))continue;if(d.has(p.folder)||p.pkg.private)throw new l(i("repairPublishedChanged",{name:p.name,version:p.to}))}if(!m.size)return a.hash;if(o.branch){let p=await x("git",["symbolic-ref","--quiet","--short","HEAD"],r);if(p.code!==0||p.stdout.trim()!==o.branch)throw new l(i("wrongBranch",{branch:o.branch}))}let h=c.map((p)=>({...p,commit:m.has(p.folder)?s:p.commit}));if(n.formal)await Me(r,h.filter((p)=>!p.pkg.private));console.log(k(i("repairPlan")));for(let p of h)console.log(` ${p.name}@${p.to}: ${p.commit}${m.has(p.folder)?` ${i("repairCurrentCode")}`:""}`);if(e.dryRun){console.log(k(i("publishPreview"))),console.log(k(i("plannedStep",{step:i("confirmRepairPublication")}))),console.log(i("publishPlanSummary",{uploads:n.steps.filter((P)=>P.kind==="publish").length,retags:n.steps.filter((P)=>P.kind==="dist-tag").length,tags:n.steps.filter((P)=>P.kind==="tag").length}));let p=new Map(h.map((P)=>[P.name,P.commit]));rt({...n,entries:h,steps:n.steps.map((P)=>P.kind==="tag"?{...P,commit:p.get(P.name)}:P)},O({...e,root:r,commit:a.hash}));return}if(!e.yes&&!await se(i("confirmRepairPublication")))throw new l(i("cancelledPublish"));if(await V(r),(await y("git",["rev-parse","HEAD"],r)).trim()!==s)throw new l(i("versionHeadChanged"));let C=a.lines.map((p)=>`${p.name} ${p.from} => ${p.to}`).join(`
|
|
68
|
-
`)+`
|
|
69
|
-
|
|
70
|
-
Change-Versions-Dist-Tag: ${e.distTag}
|
|
71
|
-
`+h.map((p)=>`Change-Versions-Source: ${p.name} ${p.commit}`).join(`
|
|
72
|
-
`);await y("git",["commit","--allow-empty","-m","chore: record repaired publication","-m",C,"-m",ue],r);let f=(await y("git",["rev-parse","HEAD"],r)).trim(),u=(await y("git",["rev-parse","HEAD^"],r)).trim(),w=(await y("git",["rev-parse","HEAD^{tree}"],r)).trim();if(u!==s||w!==(await y("git",["rev-parse",`${s}^{tree}`],r)).trim())throw new l(i("versionHeadChanged"));if(await V(r),t?.(f),console.log(M(i("repairRecorded",{commit:f}))),console.log(F(O({...e,root:r,commit:f,retryUnpublished:!1}))),o.branch){let p=await x("git",["push","origin",o.branch],r);if(p.code!==0)throw new l(i("branchPushFailed",{branch:o.branch,detail:p.stderr.trim()}))}return console.log(v(i("publishResumeInfo"))),f}async function we(e){return X(e.root,Boolean(e.dryRun),()=>Qn(e))}async function Qn(e){let t=await j(e.root,e.commit),n={...e,commit:t?.hash??e.commit};while(!0)try{if(n.retryUnpublished){let r=await en(n,(s)=>{n.commit=s,n.retryUnpublished=!1});if(!r)return;n.commit=r,n.retryUnpublished=!1}await Bt(n);return}catch(r){if(r instanceof J||r instanceof l&&r.message===i("cancelledPublish")||e.yes||e.dryRun||!T()||!n.commit)throw r;console.error(H(r instanceof Error?r.message:String(r)));while(!0){let s;try{s=await Z(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(o){if(o instanceof J)throw r;throw o}if(s==="exit")throw r;if(s==="retry")break;if(s==="repair"){n.retryUnpublished=!0;break}try{await ie(n)}catch(o){console.error(H(o instanceof Error?o.message:String(o)))}}}}_();async function _e(e){let t=Xn.resolve(e.root);if(e.distTag!==void 0&&!Ue(e.distTag))throw new l(i("badDistTag"));await X(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 o=await j(t,e.commit);if(!o)throw new l(i("noVersionCommit"));let a=o?tn.prerelease(o.lines[0].to)?.[0]:void 0,c=e.distTag??e.preid??o?.distTag??String(a??"latest");await we({...e,root:t,commit:o?.hash??e.commit,distTag:await Se(e.distTag,c,e.yes)});return}await V(t);let n=await j(t);if(!e.force&&n&&await te(t,e.baseline)===n.hash){let o=n.distTag??String(tn.prerelease(n.lines[0].to)?.[0]??"latest"),a=await ae({...e,root:t,distTag:o},!0,!0),c=await Je(a);if(c.remoteError)throw new l(i("statusRemoteUnknown",{detail:c.remoteError}));if(a.steps.some((m)=>m.kind==="publish"||m.kind==="dist-tag")||c.entries.some((m)=>!m.localReady||!m.remoteReady)){let m=i("releasePending",{command:O({...e,root:t,distTag:o,commit:n.hash})});if(e.yes||!T())throw new l(m);console.log(k(m));while(!0){let d=await Z(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 ie({...e,root:t,commit:n.hash,distTag:o})}catch(h){console.error(H(h instanceof Error?h.message:String(h)))}continue}await we({...e,root:t,commit:n.hash,distTag:o,retryUnpublished:d==="repair"});return}}}console.log(S(i("releaseVersionStage")));let r=e.distTag??e.preid??"latest",s=await We({...e,root:t,preparePublish:async({preid:o,config:a})=>{if(r=await Se(e.distTag,o??"latest",e.yes),T()&&!e.yes){console.log(S(i("wizardExecutionPlan"))),console.log(i("wizardPlanContext",{tag:r,branch:a.branch??i("statusNotConfigured")}));for(let[c,m]of Object.entries(a.hooks))if(m)console.log(` ${c}: ${m}`)}return r}});if(e.dryRun){console.log(k(i("releasePreviewComplete")));return}if(!s)return;console.log(S(i("releasePublishStage"))),console.log(F(O({...e,root:t,distTag:r,commit:s}))),await we({...e,root:t,distTag:r,commit:s})})}I();D();I();D();import Zn from"node:fs";import ei from"node:path";K();_();async function nn(e=process.cwd()){let t={root:e,versionsFile:"change-versions.yml",branchFromCli:!1,messageFromCli:!1};while(!0){let n=Zn.existsSync(ei.resolve(e,t.versionsFile));if(!n)console.log(k(i("wizardNeedsConfig")));let r=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"}],s=await Z(i("wizardAction"),r);if(s==="exit")return;if(s==="init"){await Le(t);continue}if(s==="status"){await ie({...t,distTag:await xe(e)});continue}await _e({...t,resume:s==="resume"});return}}_();N();var Y=new ti;Y.name("change-versions").version(JSON.parse(ni(new URL("../package.json",import.meta.url),"utf8")).version).description(i("cliDescription"));function Re(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 Le(e)});Re(Y.command("changed").description(i("cmdChanged"))).option("--baseline <rev>",i("optBaseline")).action(async function(){let e=this.opts();await Vt(e)});Re(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 We({...t,bump:e,branchFromCli:this.getOptionValueSource("branch")==="cli",messageFromCli:this.getOptionValueSource("commitMessage")==="cli"})});Re(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 we({...e,distTag:await Se(e.distTag,await xe(e.root,e.distTag,e.commit),e.yes)})});Re(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 ie({...e,distTag:await xe(e.root,e.distTag,e.commit)})});Re(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 _e({...this.opts(),bump:e,branchFromCli:this.getOptionValueSource("branch")==="cli",messageFromCli:this.getOptionValueSource("commitMessage")==="cli"})});async function ii(){if(process.argv.length<=2){if(T())await nn();else Y.outputHelp();return}await Y.parseAsync(process.argv)}ii().catch((e)=>{if(e instanceof J){console.log(v(e.message));return}if(e instanceof l)console.error(H(e.message));else console.error(e);process.exit(1)});
|
|
67
|
+
`}async function Ve(e){let t=se.resolve(e.root),n=se.resolve(t,e.versionsFile);if(V.existsSync(n))throw new g(i("configExists",{file:e.versionsFile}));let r=Nn(t),o=r[0]??"packages",s=e.yes?Bn(o):await In(e.versionsFile,r,o);if(!s.include.length)throw new g(i("includeRequired"));let a=An(s);Ne(a),V.mkdirSync(se.dirname(n),{recursive:!0}),V.writeFileSync(n,a),console.log(ne(i("initWrote",{file:e.versionsFile})))}function Bn(e){return{packagesDir:e,include:[`${e}/*/src`],exclude:[`${e}/*/src/**/__tests__/**`]}}async function In(e,t,n){if(!F())throw new g(i("yesRequired"));if(console.log(z(i("initIntro",{file:e}))),t.length)console.log(x(i("initFoundDirs",{dirs:t.join(", ")})));let r=await H(i("initPackagesDir"),n),o=tt(await H(i("initInclude"),`${r}/*/src`)),s=tt(await H(i("initExclude"),`${r}/*/src/**/__tests__/**`)),a=tt(await H(i("initDistTags"),"latest, alpha, beta, next")),c=await H(i("initBranch")),l=await H(i("initBeforeCommit")),d=await H(i("initBeforePublish"));return{packagesDir:r,branch:c||void 0,distTags:a,beforeCommit:l||void 0,beforePublish:d||void 0,include:o,exclude:s}}function tt(e){return e.split(",").map((t)=>t.trim()).filter(Boolean)}function Re(e){return/^[A-Za-z0-9_./@*+-]+$/.test(e)?e:JSON.stringify(e)}function Mn(e){return On(e).flatMap((t)=>{let n=t.replace(/\/\*\*?$/,"");if(!n||n===t||n.includes("*"))return[];return V.existsSync(se.join(e,n))?[n]:[]})}function jn(e){if(!V.existsSync(e))return[];let t=[];for(let n of V.readdirSync(e)){if(n.startsWith(".")||n==="node_modules")continue;let r=se.join(e,n);if(!V.statSync(r).isDirectory())continue;if(Hn(r))t.push(n)}return t}function Hn(e){return V.readdirSync(e).some((t)=>V.existsSync(se.join(e,t,"package.json")))}function On(e){let t=se.join(e,"package.json");if(!V.existsSync(t))return[];try{let n=JSON.parse(V.readFileSync(t,"utf8")),r=Array.isArray(n.workspaces)?n.workspaces:n.workspaces?.packages;return Array.isArray(r)?r.filter((o)=>typeof o==="string"):[]}catch{return[]}}import ui from"node:path";import an from"semver";import pe from"node:fs";import nt from"node:path";import Ln from"node:os";import{randomUUID as Vn}from"node:crypto";import{AsyncLocalStorage as Gn}from"node:async_hooks";D();T();var it=new Gn;async function Y(e,t,n){return le(()=>Un(e,t,n))}async function Un(e,t,n){if(t)return n();e=nt.resolve(e);let r=nt.resolve(e,(await y("git",["rev-parse","--git-common-dir"],e)).trim()),o=nt.join(r,"change-versions.lock");if(it.getStore()?.has(o))return n();let s={pid:process.pid,host:Ln.hostname(),token:Vn()},a;try{a=pe.openSync(o,"wx",384)}catch(l){if(l.code!=="EEXIST")throw l;let d="";try{d=pe.readFileSync(o,"utf8")}catch{}throw new g(i("repositoryLocked",{file:o,owner:d.trim()}))}let c=()=>{try{if(JSON.parse(pe.readFileSync(o,"utf8")).token===s.token)pe.unlinkSync(o)}catch{}};try{pe.writeFileSync(a,JSON.stringify(s))}finally{pe.closeSync(a)}process.on("exit",c);try{return await it.run(new Set([...it.getStore()??[],o]),n)}finally{process.off("exit",c),c()}}import Ht from"node:path";D();import Ot from"semver";T();D();function It(e){let t=[];if(e.beforePublish)t.push({kind:"hook",name:"beforePublish",command:e.beforePublish});for(let n of kt(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}async function Q(e,t="publish"){let n=t==="status"||t==="guard",r=t==="guard"||t==="repair",o=Ht.resolve(e.root);if(!n)await M(o);let s=(await y("git",["rev-parse","HEAD"],o)).trim(),a=I(o,e.versionsFile);if(e.packages)a.packagesDir=e.packages;let c=await N(o,e.commit);if(!c?.lines.length)throw new g(i("noVersionCommit"));let l=J(o,a.packagesDir),d=c.lines.map((u)=>{let k=l.named(u.name);if(!k)throw new g(i("packageNotInDir",{dir:a.packagesDir,name:u.name}));let f=k.folder;if(k.version!==u.to)throw new g(i("releaseVersionMismatch",{name:u.name,expected:u.to,actual:k.version,commit:c.hash}));return{...u,folder:f,pkg:k,commit:c.sourceCommits.get(u.name)??c.hash,deps:l.dependencies(f,["dependencies","devDependencies"]).map((R)=>R.folder)}}),p=new Set;for(let u of d){let k=await P("git",["diff","--quiet",u.commit,s,"--",`${a.packagesDir}/${u.folder}`],o);if(k.code===1)p.add(u.folder);if(k.code!==0&&k.code!==1)throw new g(i("gitFailed",{detail:k.stderr.trim()}))}if(p.size&&!r)throw new g(i("releaseContentsChanged"));if(!n)console.log(i("publishSummaryContext",{commit:c.hash,tag:e.distTag}));let w=Ae(c.lines);if(w)await $e(o,d.filter((u)=>!u.pkg.private));let b=new Map;for(let u of d){if(u.pkg.private)continue;let k=await Jn(o,u.pkg);if(b.set(u.folder,k),!n)console.log(i("checkingRegistry",{name:u.name,version:u.to,registry:k}))}let h=new Set;for(let u of d){let k=b.get(u.folder);if(!k||h.has(k))continue;if(h.add(k),!n)await Wn(o,k)}let m=new Set,v=new Map;for(let u of d){if(u.pkg.private)continue;let k=b.get(u.folder);if(!k)continue;await Gt(async()=>{let f=await Ut(o,u.name,u.to,k);if(f)m.add(u.folder);let R=await qt(o,u.name,e.distTag,k,!f);if(!n)Wt(u.name,u.to,R,e.distTag);v.set(u.folder,R)})}let S=It({packages:d.map((u)=>({name:u.name,version:u.to,folder:u.folder,private:u.pkg.private,deps:u.deps,registry:b.get(u.folder),published:m.has(u.folder),distTagVersion:v.get(u.folder),commit:u.commit})),distTag:e.distTag,formal:w,commit:c.hash,beforePublish:a.publish.hooks.beforePublish,afterPublish:a.publish.hooks.afterPublish});return{root:o,startHead:s,config:a,release:c,formal:w,steps:S,entries:d.map((u)=>({...u,registry:b.get(u.folder),changed:p.has(u.folder),status:u.pkg.private?"private":!m.has(u.folder)?"upload":v.get(u.folder)!==u.to?"retag":"ready"}))}}async function Ge(e){let t=await Kn(e);return{ready:e.entries.every((n)=>n.status==="private"||n.status==="ready")&&!t.remoteError&&t.entries.every((n)=>n.localReady&&n.remoteReady),tags:t}}async function Lt(e,t,n){let r="skipped";return await Gt(async()=>{let o=await Ut(e.root,t.name,t.version,t.registry);if(!o&&t.kind==="dist-tag")throw new g(i("registryVersionDisappeared",{name:t.name,version:t.version}));let s=await qt(e.root,t.name,t.tag,t.registry,!o);if(Wt(t.name,t.version,s,t.tag),await ue(e.root,i("publishSourceDirty")),await qn(e.root,e.startHead),o&&s===t.version)n("skipped"),r="skipped";else if(o)n("dist-tag"),await Mt(e.root,["dist-tag","add",`${t.name}@${t.version}`,t.tag,`--registry=${t.registry}`],t.registry),r="retagged";else n("publish"),await Mt(Ht.join(e.root,e.config.packagesDir,t.folder),["publish",`--tag=${t.tag}`,`--registry=${t.registry}`],t.registry),r="published"}),r}function Vt(e){for(let t of e.entries)if(t.changed&&(t.status!=="upload"||t.pkg.private))throw new g(i("repairPublishedChanged",{name:t.name,version:t.to}));return{changed:e.entries.some((t)=>t.changed),entries:e.entries.map((t)=>({...t,commit:t.changed?e.startHead:t.commit}))}}async function $e(e,t){for(let n of t){let r=`${n.name}@${n.to}`,o=await P("git",["rev-parse","--verify",`refs/tags/${r}^{commit}`],e);if(o.code===0&&o.stdout.trim()!==n.commit)throw new g(i("tagPointsElsewhere",{tag:r}))}}async function qn(e,t){if((await y("git",["rev-parse","HEAD"],e)).trim()!==t)throw new g(i("publishHeadChanged"))}async function Gt(e){let t="";for(let n=0;n<3;n++)try{await e();return}catch(r){if(r instanceof g)throw r;t=r instanceof Error?r.message:String(r)}throw new g(i("networkFailed",{detail:t}))}async function Mt(e,t,n){let r=await P("npm",t,e);if(r.code===0)return;let o=`${r.stderr}${r.stdout}`;if(process.stderr.write(r.stderr),Pe(o))throw new g(i("notLoggedIn",{registry:n}),!0);throw Error(o.trim()||i("npmFailed"))}async function Ut(e,t,n,r){let o=await P("npm",["view",`${t}@${n}`,"version","--json",`--registry=${r}`],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:r}))}let s=`${o.stderr}${o.stdout}`;if(Pe(s))throw new g(i("notLoggedIn",{registry:r}),!0);if(xe(o.stdout)||xe(o.stderr))return!1;throw Error(s.trim()||i("npmViewFailed"))}async function qt(e,t,n,r,o=!1){let s=await P("npm",["view",t,"dist-tags","--json",`--registry=${r}`],e);if(s.code!==0){let l=`${s.stderr}${s.stdout}`.trim();if(Pe(l))throw new g(i("notLoggedIn",{registry:r}),!0);if(o&&(xe(s.stdout)||xe(s.stderr)))return;throw Error(l||i("npmViewFailed"))}let a;try{a=JSON.parse(s.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:r}));let c=Object.prototype.hasOwnProperty.call(a,n)?a[n]:void 0;if(c!==void 0&&(typeof c!=="string"||!Ot.valid(c)))throw Error(i("unexpectedRegistryTags",{name:t,tag:n,registry:r}));return c}function Wt(e,t,n,r){if(n&&Ot.gt(n,t))throw new g(i("distTagDowngrade",{name:e,version:t,current:n,tag:r}))}async function Wn(e,t){let n=await P("npm",["whoami",`--registry=${t}`],e);if(n.code===0)return;let r=`${n.stderr}${n.stdout}`;if(Pe(r))throw new g(i("notLoggedIn",{registry:t}),!0);throw new g(i("registryUnreachable",{registry:t,detail:r.trim()}))}async function Jn(e,t){if(t.publishConfig?.registry)return t.publishConfig.registry.replace(/\/$/,"");if(t.name.startsWith("@")&&t.name.includes("/")){let r=t.name.slice(0,t.name.indexOf("/")),o=await jt(e,`${r}:registry`);if(o&&o!=="undefined"&&o!=="null")return o.replace(/\/$/,"")}let n=await jt(e,"registry");if(!n||n==="undefined"||n==="null")throw new g(i("noNpmRegistry",{detail:"npm config get registry: empty value"}));return n.replace(/\/$/,"")}async function jt(e,t){let n=await P("npm",["config","get",t],e);if(n.code!==0){let r=n.stderr.trim()||n.stdout.trim()||`npm config get ${t}: exit ${n.code}`;throw new g(i("noNpmRegistry",{detail:r}))}return n.stdout.trim()}async function Kn(e){let t=[],n=new Map,r;if(!e.formal||e.entries.every((o)=>o.pkg.private))return{entries:t,remoteError:r};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}^{}`]),s=await P("git",["ls-remote","--tags","origin",...o],e.root);if(s.code!==0)r=s.stderr.trim()||s.stdout.trim()||`git ls-remote: exit ${s.code}`;else for(let a of s.stdout.trim().split(`
|
|
68
|
+
`).filter(Boolean)){let[c,l]=a.split(/\s+/);n.set(l,c)}}for(let o of e.entries){if(o.pkg.private)continue;let s=`${o.name}@${o.to}`,a=await P("git",["rev-parse","--verify",`refs/tags/${s}^{commit}`],e.root),c=n.get(`refs/tags/${s}^{}`)??n.get(`refs/tags/${s}`);t.push({tag:s,localReady:a.code===0&&a.stdout.trim()===o.commit,remoteReady:!e.config.publish.branch||!r&&c===o.commit})}return{entries:t,remoteError:r}}import ae from"node:fs";import Ue from"node:path";import oe from"node:fs";import X from"node:path";import{spawnSync as _n}from"node:child_process";D();T();function rt(e){try{let t=oe.lstatSync(e);if(!t.isFile())throw new g(i("unsafeCommitFile",{file:e}));return{bytes:oe.readFileSync(e),mode:t.mode&511}}catch(t){if(t.code==="ENOENT")return;throw t}}function Yn(e,t){return e===t||Boolean(e&&t&&e.mode===t.mode&&e.bytes.equals(t.bytes))}async function Jt(e,t,n){let r=(await y("git",["rev-parse","HEAD"],e)).trim();if(n&&r!==n)throw new g(i("versionHeadChanged"));let o=new Map;for(let b of new Set(t)){let h=X.resolve(e,b),m=X.relative(e,h);if(!m||m.startsWith("..")||X.isAbsolute(m)||/[*?\[\]\\]/.test(b)||m.split(X.sep).includes(".git"))throw new g(i("unsafeCommitFile",{file:b}));for(let v=X.dirname(h);v!==e;v=X.dirname(v))if(oe.existsSync(v)&&oe.lstatSync(v).isSymbolicLink())throw new g(i("unsafeCommitFile",{file:b}));o.set(m,rt(h))}let s=new Map(o),a,c=!0,l=()=>{s=new Map([...o.keys()].map((b)=>[b,rt(X.join(e,b))]))},d=(...b)=>_n("git",b,{cwd:e,encoding:"utf8"}),p=()=>{if(!c)return;c=!1,w();let b=d("rev-parse","HEAD");if(b.status!==0||b.stdout.trim()!==r){console.error(j(i("versionHeadChanged")));return}if(a!==void 0&&d("write-tree").stdout.trim()===a)d("restore","--staged","--",...o.keys());for(let[h,m]of o){let v=X.join(e,h);try{if(!Yn(rt(v),s.get(h)))continue;if(m)oe.writeFileSync(v,m.bytes),oe.chmodSync(v,m.mode);else oe.rmSync(v,{force:!0})}catch{}}},w=ge(()=>{p(),console.error(j(i("versionRecovery")))});return{checkpoint:l,async stage(){if((await y("git",["rev-parse","HEAD"],e)).trim()!==r)throw new g(i("versionHeadChanged"));if(await y("git",["diff","--cached","--name-only","-z"],e))throw new g(i("versionUnexpectedIndex"));let h=(await y("git",["diff","--name-only","-z"],e)).split("\x00").filter(Boolean),m=(await y("git",["ls-files","--others","--exclude-standard","-z"],e)).split("\x00").filter(Boolean),v=[...h,...m].filter((k)=>!o.has(k));if(v.length)throw new g(i("versionUnexpectedFiles",{files:v.join(", ")}));l();let S=[...new Set([...h,...m])];if(!S.length)throw new g(i("versionUnexpectedIndex"));if(await y("git",["add","--",...S],e),a=(await y("git",["write-tree"],e)).trim(),(await y("git",["diff","--cached","--name-only","-z"],e)).split("\x00").filter(Boolean).some((k)=>!o.has(k)))throw new g(i("versionUnexpectedIndex"))},async verifyCommit(){let b=(await y("git",["rev-parse","HEAD^"],e)).trim(),h=(await y("git",["rev-parse","HEAD^{tree}"],e)).trim();if(b!==r||h!==a)throw new g(i("versionHeadChanged"))},disarm(){c=!1,w()},fail(b){throw p(),b}}}T();function Qn(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(Qn).join(" ")}D();import Kt from"semver";T();D();D();async function _t(e,t){let{bump:n,preid:r}=e;if(!n){if(e.yes||!F())throw new g(i("bumpRequired"));let s=r?"pre":await L(i("chooseReleaseKind"),[{value:"formal",label:i("releaseKindFormal")},{value:"pre",label:i("releaseKindPre")}]);n=await Bt(i("chooseBump"),s==="pre"?["prerelease","prepatch","preminor","premajor"]:["patch","minor","major"])}if(!n.startsWith("pre")||r||!["prerelease","prepatch","preminor","premajor"].includes(n))return{bump:Ee(n,r),preid:r};if(e.yes||!F())return{bump:Ee(n,r),preid:r};let o=[...new Set(((t?.length)?t:["alpha","beta","rc"]).filter((s)=>s!=="latest"&&Boolean(Kt.valid(`0.0.0-${s}.0`))))];r=await L(i("choosePreidList"),[...o.map((s)=>({value:s,label:s})),{value:"",label:i("customPreid")}]);while(!r){let s=await H(i("choosePreid"),"alpha");if(Kt.valid(`0.0.0-${s}.0`))r=s;else console.log(C(i("invalidPreid")))}return{bump:Ee(n,r),preid:r}}async function De(e,t,n,r=["latest","alpha","beta","next"]){if(e!==void 0||n||!F()){let a=e??t;if(!ee(a))throw new g(i("badDistTag"));return a}let o=[...new Set([...r,t])],s=await L(i("chooseDistTagList"),[...o.map((a)=>({value:a,label:a})),{value:"",label:i("customDistTag")}]);if(s)return s;while(!0){let a=await H(i("chooseDistTag"),t);if(ee(a))return a;console.log(C(i("badDistTag")))}}async function Yt(e,t,n){try{return await te(e,t)}catch(r){if(t||n||!F()||!(r instanceof g)||r.message!==i("noBaseline"))throw r}console.log(C(i("firstBaseline")));while(!0){let r=await H(i("chooseBaseline"));if(!r)continue;try{return await te(e,r)}catch(o){if(!(o instanceof g))throw o;console.log(C(o.message))}}}async function qe(e){return Y(e.root,Boolean(e.dryRun),()=>ni(e))}async function ni(e){let t=Ue.resolve(e.root),n=I(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 M(t),n.publish.branch){let m=await P("git",["symbolic-ref","--quiet","--short","HEAD"],t);if(m.code!==0||m.stdout.trim()!==n.publish.branch)throw new g(i("wrongBranch",{branch:n.publish.branch}))}let r=await Yt(t,e.baseline,e.yes),o=(await y("git",["rev-parse","HEAD"],t)).trim(),s=e.force?{baseline:r,changed:await nn(t,n),dependents:[]}:await He(t,n,r,!e.baseline);if(!s.changed.length&&!s.dependents.length){let m=(await y("git",["rev-parse","--short",s.baseline],t)).trim();if(console.log(x(i("noPackagesSince",{rev:m}))),console.log(x(i("noVersionChangesNext"))),e.yes||!F()||!await N(t)||!await re(i("confirmForceVersion")))return;s.changed=await nn(t,n)}let a="workspace"in s?s.workspace:J(t,n.packagesDir);if(F()&&!e.yes){console.log(z(i("wizardCandidates")));for(let m of[...s.changed,...s.dependents]){let v=a.folder(m);console.log(` ${v.name}${v.private?` ${x(i("privateMark"))}`:""}`)}}let{bump:c,preid:l}=await _t(e,n.publish.distTags),d=[...s.changed,...s.dependents].map((m)=>{let v=a.folder(m);return{folder:m,name:v.name,private:v.private,from:v.version,to:bt(v.version,c,l)}});if(e.dryRun)console.log(C(i("versionPreview")));for(let m of d)console.log(`${Ft(m.name,m.from,m.to)}${m.private?` ${x(i("privateMark"))}`:""}`);if(d.some((m)=>m.private))console.log(x(i("privateVersionHint")));let p=e.preparePublish?await e.preparePublish({preid:l,config:n}):e.publishTag;if(e.dryRun){console.log(z(i("versionPreviewComplete")));return}if(!e.yes&&!await re(i("confirmVersions")))throw new g(i("cancelledVersion"));if(await M(t),(await y("git",["rev-parse","HEAD"],t)).trim()!==o)throw new g(i("versionHeadChanged"));let w=a.packages.map(({folder:m})=>`${a.path(m)}/package.json`);if(Ye(c))w.push(...d.map((m)=>`${n.packagesDir}/${m.folder}/CHANGELOG.md`));w.push("bun.lock","bun.lockb","package-lock.json","npm-shrinkwrap.json","yarn.lock","pnpm-lock.yaml",...n.publish.commitFiles??[]);let b=await Jt(t,w,o);try{try{for(let S of d)oi(t,n.packagesDir,S.folder,S.to);if(ai(t,n.packagesDir),Ye(c))await ii(t,n,d,r)}finally{b.checkpoint()}if(n.publish.hooks.beforeCommit)await de("sh",["-c",n.publish.hooks.beforeCommit],t);let m=yt(n.publish.commitMessage,c,l),v=je(d,p);await b.stage(),await y("git",["commit","-m",m,"-m",v,"-m",ce],t),await b.verifyCommit(),b.disarm()}catch(m){await b.fail(m)}let h=(await y("git",["rev-parse","HEAD"],t)).trim();if(console.log(A(i("versionCommitted",{commit:h}))),console.log(C(i("versionTrailerHint"))),n.publish.branch){let m=await P("git",["push","origin",n.publish.branch],t);if(m.code!==0)throw new g(i("branchPushFailed",{branch:n.publish.branch,detail:m.stderr.trim()}))}return console.log(z(i("versionPublishNext"))),console.log(E(O({...e,root:t,commit:h,distTag:p??l??"latest"}))),h}async function nn(e,t){let n=await N(e);if(!n)throw new g(i("noVersionCommit"));let r=J(e,t.packagesDir);return console.log(C(i("forcePreviousBatch",{commit:n.hash}))),n.lines.map((o)=>{let s=r.named(o.name);if(!s)throw new g(i("packageNotInDir",{dir:t.packagesDir,name:o.name}));if(s.version!==o.to)throw new g(i("releaseVersionMismatch",{name:o.name,expected:o.to,actual:s.version,commit:n.hash}));if(!s.private){for(let a of Object.keys(s.dependencies??{}))if(r.named(a)?.private)throw new g(i("publicDependsOnPrivate",{name:s.name,dep:a}))}return s.folder})}async function ii(e,t,n,r){await Promise.resolve().then(() => tn());let a=await zt(e,r),c=await ri(e,a),l=new Set(n.map((p)=>p.folder)),d=J(e,t.packagesDir);for(let p of n){let w=`${t.packagesDir}/${p.folder}/package.json`,b=new Set;for(let f of c)if(f.files.includes(w)&&await si(e,f.hash,w))b.add(f.hash);let h=Zt(c,p.folder,t.packagesDir,t.publish.include,t.publish.exclude,b),m=d.folder(p.folder),v=h.length?[]:d.dependencies(m.folder,["dependencies","devDependencies"]).filter((f)=>l.has(f.folder)).map((f)=>({name:f.name,version:f.version})),S=await en({messages:h,version:p.to,previousTag:`${p.name}@${p.from}`,currentTag:`${p.name}@${p.to}`,dependencies:v}),u=Ue.join(e,t.packagesDir,p.folder,"CHANGELOG.md"),k=ae.existsSync(u)?ae.readFileSync(u,"utf8"):"";ae.writeFileSync(u,k?`${S}
|
|
69
|
+
${k}`:S)}}async function ri(e,t){let n=t?["rev-list","--reverse",`${t}..HEAD`]:["rev-list","--reverse","HEAD"],r=(await y("git",n,e)).split(`
|
|
70
|
+
`).filter(Boolean),o=[];for(let s of r){let a=await y("git",["log","-1","--format=%B",s],e),c=await P("git",["diff-tree","--no-commit-id","--name-only","-r","--root",s],e);o.push({hash:s,body:a,files:c.code===0?c.stdout.split(`
|
|
71
|
+
`).filter(Boolean):[]})}return o}async function si(e,t,n){return!await et(e,`${t}^`,t,n)}function oi(e,t,n,r){let o=Ue.join(e,t,n,"package.json"),s=ae.readFileSync(o,"utf8"),a=JSON.parse(s);if(typeof a.version!=="string")throw new g(i("missingVersionField",{folder:n}));a.version=r;let c=/\n([\t ]+)"/.exec(s)?.[1]??" ";ae.writeFileSync(o,JSON.stringify(a,null,c)+`
|
|
72
|
+
`)}function ai(e,t){let n=J(e,t);for(let{folder:r}of n.packages){let o=Ue.join(e,t,r,"package.json"),s=ae.readFileSync(o,"utf8"),a=JSON.parse(s),c=!1;for(let l of["dependencies","devDependencies"])for(let d of Object.keys(a[l]??{})){let p=n.named(d);if(!p)continue;let w=`^${p.version}`;if(a[l][d]!==w)a[l][d]=w,c=!0}if(c){let l=/\n([\t ]+)"/.exec(s)?.[1]??" ";ae.writeFileSync(o,JSON.stringify(a,null,l)+`
|
|
73
|
+
`)}}}D();T();D();T();import ci from"node:path";T();D();async function sn(e){return Y(e.root,Boolean(e.dryRun),()=>li(e))}async function li(e){console.log(e.dryRun?C(i("publishPreview")):z(i("publishPreparing")));let t=await Q(e),{root:n,startHead:r,config:o,release:s,entries:a,formal:c,steps:l}=t,d=O({...e,root:n,commit:s.hash}),p={CHANGE_VERSIONS_PUBLISH_CONTEXT:JSON.stringify({schemaVersion:1,root:n,packagesDir:o.packagesDir,commit:s.hash,distTag:e.distTag,packages:l.flatMap((f)=>f.kind==="publish"?[{name:f.name,version:f.version,path:ci.join(o.packagesDir,f.folder)}]:[])})};for(let f of a)if(f.status==="ready")console.log(x(i("alreadyTagged",{name:f.name,version:f.to,tag:e.distTag,registry:f.registry||""})));if(console.log(i("publishPlanSummary",{uploads:l.filter((f)=>f.kind==="publish").length,retags:l.filter((f)=>f.kind==="dist-tag").length,tags:l.filter((f)=>f.kind==="tag").length})),e.dryRun){at({steps:l,formal:c,config:o,entries:a},d);return}console.log(x(i("publishResumeInfo"))),console.log(z(i("willPublish")));for(let f of a){if(f.status==="ready")continue;let R=f.pkg.private?` ${x(i("privateMark"))}`:f.status==="retag"?` ${x(i("distTagOnlyMark"))}`:"";console.log(` ${_(f.name)} ${ne(f.to)}${R}`)}if(!e.yes&&!await re(i("confirmPublish")))throw new g(i("cancelledPublish"));let w=new Map(a.map((f)=>[f.folder,f.status==="private"?"summaryPrivate":f.status==="ready"?"summarySkipped":"summaryPending"])),b="beforePublish",h,m=!1,v=(f)=>f==="summaryPublished"||f==="summaryRetagged"?A:f==="summaryFailed"?Oe:f==="summaryPending"?C:x,S=()=>{console.log((m?A:Oe)(i(m?"publishComplete":"publishIncomplete"))),console.log(i("publishSummaryContext",{commit:s.hash,tag:e.distTag}));let f=new Map;for(let R of a){let G=w.get(R.folder);f.set(G,(f.get(G)??0)+1);let ct=R.registry;console.log(` ${v(G)(i(G))}: ${_(R.name)}@${_(R.to)}${ct?` — ${x(ct)}`:""}`)}if(console.log([...f].map(([R,G])=>`${v(R)(i(R))}: ${_(String(G))}`).join(", ")),m)console.log(A(i("publishReady"))),console.log(x(i(l.some((R)=>R.kind==="tag")?o.publish.branch?"gitTagsPushed":"gitTagsLocal":"noGitTag")));else{if(console.log(Oe(i("publishFailedStage",{stage:b}))),b==="git tag / git push")console.log(C(i("publishTagRecovery")));console.log(C(i("publishRetryTitle"))),console.log(z(i("publishRetry"))),console.log(x(i("publishResumeInfo"))),console.log(E(d))}},k=ge(()=>{if(h)w.set(h,"summaryFailed");S(),console.error(j(i("publishInterrupted")))});try{await rn(n,o.publish.hooks.beforePublish,"beforePublish",p),await ue(n,i("beforePublishDirty")),await st(n,r);for(let f of l){if(f.kind==="tag"){b="git tag / git push",console.log(z(i("executingStep",{step:ot(f)}))),await gi(n,o.publish.branch,[{name:f.name,to:f.version,commit:f.commit}]);continue}if(f.kind!=="publish"&&f.kind!=="dist-tag")continue;b=`${f.kind==="publish"?"npm publish":"npm dist-tag add"} ${f.name}@${f.version}`,h=f.folder;let R=await Lt(t,f,(G)=>{if(G==="skipped")console.log(x(i("alreadyTagged",{name:f.name,version:f.version,tag:f.tag,registry:f.registry})));else b=`${G==="publish"?"npm publish":"npm dist-tag add"} ${f.name}@${f.version}`,console.log(z(i("executingStep",{step:ot({...f,kind:G})})))});w.set(f.folder,R==="published"?"summaryPublished":R==="retagged"?"summaryRetagged":"summarySkipped"),h=void 0,await ue(n,i("publishSourceDirty")),await st(n,r)}b="afterPublish",await rn(n,o.publish.hooks.afterPublish,"afterPublish",p),await ue(n,i("afterPublishDirty")),await st(n,r),m=!0}catch(f){if(h)w.set(h,"summaryFailed");throw f}finally{k(),S()}}function at(e,t){for(let n of e.steps)if(console.log(`${C(i("plannedStep",{step:""}))}${ot(n)}`),n.kind==="tag"&&e.config.publish.branch)console.log(C(i("plannedStep",{step:`git push origin refs/tags/${n.name}@${n.version}`})));if(!e.formal)console.log(x(i("noGitTag")));console.log(C(i("publishPreviewComplete"))),console.log(z(i("publishPreviewNext"))),console.log(E(t))}function ot(e){if(e.kind==="hook")return`${x(`${e.name}:`)} ${E(e.command)}`;if(e.kind==="tag")return E(`git tag ${e.name}@${e.version} ${e.commit}`);if(e.kind==="dist-tag")return E(`npm dist-tag add ${e.name}@${e.version} ${e.tag} --registry=${e.registry}`);return E(`npm publish --tag=${e.tag} --registry=${e.registry} ${e.name}@${e.version}`)}async function gi(e,t,n){await $e(e,n);let r=n.map((s)=>`${s.name}@${s.to}`);for(let s of n){let a=`${s.name}@${s.to}`;if((await P("git",["rev-parse","--verify",`refs/tags/${a}^{commit}`],e)).code===0)continue;await y("git",["tag",a,s.commit],e)}if(!t)return;let o=await P("git",["push","origin",...r.map((s)=>`refs/tags/${s}`)],e);if(o.code!==0)throw new g(i("tagPushFailed",{detail:o.stderr.trim()}))}async function rn(e,t,n,r){if(!t)return;console.log(z(i("executingStep",{step:`${n}: ${t}`})));try{await de("sh",["-c",t],e,r)}catch(o){let s=o instanceof Error?o.message:String(o);throw new g(i("hookFailed",{name:n,detail:s}))}}async function st(e,t){if((await y("git",["rev-parse","HEAD"],e)).trim()!==t)throw new g(i("publishHeadChanged"))}import di from"node:path";T();async function Z(e){let t=di.resolve(e.root);if(await y("git",["rev-parse","--show-toplevel"],t),!await N(t,e.commit)){console.log(x(i("statusNoBatch")));return}console.log(z(i("statusHeading")));let n=await Q(e,"status"),r=await Ge(n);console.log(i("publishSummaryContext",{commit:n.release.hash,tag:e.distTag}));let o=r.ready;for(let c of n.entries){let l=c.status==="private"?"summaryPrivate":c.status==="upload"?"statusUpload":c.status==="retag"?"statusRetag":"summarySkipped";console.log(` ${(l==="statusUpload"||l==="statusRetag"?C:x)(i(l))}: ${c.name}@${c.to}`)}let s=r.tags;if(s.remoteError)console.log(C(i("statusRemoteUnknown",{detail:s.remoteError})));for(let{tag:c,localReady:l,remoteReady:d}of s.entries)console.log((l&&d?x:C)(i("statusGitTag",{tag:c,local:l?i("statusReady"):i("statusMissing"),remote:n.config.publish.branch?d?i("statusReady"):i("statusMissing"):i("statusNotConfigured")})));let a=await P("git",["status","--porcelain"],t);if(a.code!==0||a.stdout.trim())o=!1,console.log(C(i("dirtyTree")));console.log((o?A:C)(i(o?"statusExternalReady":"statusPending"))),console.log(x(i("statusHooksUnknown"))),console.log(E(O({...e,root:t,commit:n.release.hash})))}T();D();async function on(e,t){console.log(z(i("repairPublication")));let n=await Q(e,"repair"),{root:r,startHead:o,config:s,release:a}=n,c=Vt(n);if(!c.changed)return a.hash;if(s.publish.branch){let h=await P("git",["symbolic-ref","--quiet","--short","HEAD"],r);if(h.code!==0||h.stdout.trim()!==s.publish.branch)throw new g(i("wrongBranch",{branch:s.publish.branch}))}let l=c.entries;if(n.formal)await $e(r,l.filter((h)=>!h.pkg.private));console.log(C(i("repairPlan")));for(let h of l)console.log(` ${h.name}@${h.to}: ${h.commit}${h.changed?` ${i("repairCurrentCode")}`:""}`);if(e.dryRun){console.log(C(i("publishPreview"))),console.log(C(i("plannedStep",{step:i("confirmRepairPublication")}))),console.log(i("publishPlanSummary",{uploads:n.steps.filter((m)=>m.kind==="publish").length,retags:n.steps.filter((m)=>m.kind==="dist-tag").length,tags:n.steps.filter((m)=>m.kind==="tag").length}));let h=new Map(l.map((m)=>[m.name,m.commit]));at({...n,entries:l,steps:n.steps.map((m)=>m.kind==="tag"?{...m,commit:h.get(m.name)}:m)},O({...e,root:r,commit:a.hash}));return}if(!e.yes&&!await re(i("confirmRepairPublication")))throw new g(i("cancelledPublish"));if(await M(r),(await y("git",["rev-parse","HEAD"],r)).trim()!==o)throw new g(i("versionHeadChanged"));let d=je(a.lines,e.distTag,new Map(l.map((h)=>[h.name,h.commit])));await y("git",["commit","--allow-empty","-m","chore: record repaired publication","-m",d,"-m",ce],r);let p=(await y("git",["rev-parse","HEAD"],r)).trim(),w=(await y("git",["rev-parse","HEAD^"],r)).trim(),b=(await y("git",["rev-parse","HEAD^{tree}"],r)).trim();if(w!==o||b!==(await y("git",["rev-parse",`${o}^{tree}`],r)).trim())throw new g(i("versionHeadChanged"));if(await M(r),t?.(p),console.log(A(i("repairRecorded",{commit:p}))),console.log(E(O({...e,root:r,commit:p,retryUnpublished:!1}))),s.publish.branch){let h=await P("git",["push","origin",s.publish.branch],r);if(h.code!==0)throw new g(i("branchPushFailed",{branch:s.publish.branch,detail:h.stderr.trim()}))}return console.log(x(i("publishResumeInfo"))),p}async function he(e){return Y(e.root,Boolean(e.dryRun),()=>mi(e))}async function mi(e){let t=await N(e.root,e.commit),n={...e,commit:t?.hash??e.commit};while(!0)try{if(n.retryUnpublished){let r=await on(n,(o)=>{n.commit=o,n.retryUnpublished=!1});if(!r)return;n.commit=r,n.retryUnpublished=!1}await sn(n);return}catch(r){if(r instanceof q||r instanceof g&&r.message===i("cancelledPublish")||e.yes||e.dryRun||!F()||!n.commit)throw r;console.error(j(r instanceof Error?r.message:String(r)));while(!0){let o;try{o=await L(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(s){if(s instanceof q)throw r;throw s}if(o==="exit")throw r;if(o==="retry")break;if(o==="repair"){n.retryUnpublished=!0;break}try{await Z(n)}catch(s){console.error(j(s instanceof Error?s.message:String(s)))}}}}async function We(e){let t=ui.resolve(e.root);if(e.distTag!==void 0&&!ee(e.distTag))throw new g(i("badDistTag"));await Y(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 g(i("releaseResumeOptions"));let s=await N(t,e.commit);if(!s)throw new g(i("noVersionCommit"));let a=s?an.prerelease(s.lines[0].to)?.[0]:void 0,c=e.distTag??e.preid??s?.distTag??String(a??"latest");await he({...e,root:t,commit:s?.hash??e.commit,distTag:await De(e.distTag,c,e.yes,I(t,e.versionsFile).publish.distTags)});return}await M(t);let n=await N(t);if(!e.force&&n&&await te(t,e.baseline)===n.hash){let s=n.distTag??String(an.prerelease(n.lines[0].to)?.[0]??"latest"),a=await Q({...e,root:t,distTag:s},"guard"),c=await Ge(a),l=c.tags;if(l.remoteError)throw new g(i("statusRemoteUnknown",{detail:l.remoteError}));if(!c.ready){let d=i("releasePending",{command:O({...e,root:t,distTag:s,commit:n.hash})});if(e.yes||!F())throw new g(d);console.log(C(d));while(!0){let p=await L(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(p==="exit")return;if(p==="force"){e={...e,force:!0};break}if(p==="status"){try{await Z({...e,root:t,commit:n.hash,distTag:s})}catch(w){console.error(j(w instanceof Error?w.message:String(w)))}continue}await he({...e,root:t,commit:n.hash,distTag:s,retryUnpublished:p==="repair"});return}}}console.log(z(i("releaseVersionStage")));let r=e.distTag??e.preid??"latest",o=await qe({...e,root:t,preparePublish:async({preid:s,config:a})=>{if(r=await De(e.distTag,s??"latest",e.yes,a.publish.distTags),F()&&!e.yes){console.log(z(i("wizardExecutionPlan"))),console.log(i("wizardPlanContext",{tag:r,branch:a.publish.branch??i("statusNotConfigured")}));for(let[c,l]of Object.entries(a.publish.hooks))if(l)console.log(` ${c}: ${l}`)}return r}});if(e.dryRun){console.log(C(i("releasePreviewComplete")));return}if(!o)return;console.log(z(i("releasePublishStage"))),console.log(E(O({...e,root:t,distTag:r,commit:o}))),await he({...e,root:t,distTag:r,commit:o})})}T();import pi from"node:fs";import hi from"node:path";T();async function cn(e=process.cwd()){let t={root:e,versionsFile:"change-versions.yml",branchFromCli:!1,messageFromCli:!1};while(!0){let n=pi.existsSync(hi.resolve(e,t.versionsFile));if(!n)console.log(C(i("wizardNeedsConfig")));let r=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 L(i("wizardAction"),r);if(o==="exit")return;if(o==="init"){await Ve(t);continue}if(o==="status"){await Z({...t,distTag:await Se(e)});continue}await We({...t,resume:o==="resume"});return}}D();var W=new fi;W.name("change-versions").version(JSON.parse(bi(new URL("../package.json",import.meta.url),"utf8")).version).description(i("cliDescription"));function fe(e){return e.option("--root <dir>",i("optRoot"),process.cwd()).option("--versions-file <file>",i("optVersionsFile"),"change-versions.yml").option("--packages <dir>",i("optPackages"))}W.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 Ve(e)});fe(W.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 Nt(this.opts())});fe(W.command("changed").description(i("cmdChanged"))).option("--baseline <rev>",i("optBaseline")).action(async function(){let e=this.opts();await Et(e)});fe(W.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 qe({...t,bump:e,branchFromCli:this.getOptionValueSource("branch")==="cli",messageFromCli:this.getOptionValueSource("commitMessage")==="cli"})});fe(W.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 he({...e,distTag:await De(e.distTag,await Se(e.root,e.distTag,e.commit),e.yes,I(e.root,e.versionsFile).publish.distTags)})});fe(W.command("status").description(i("cmdStatus"))).option("--commit <rev>",i("optReleaseCommit")).option("--dist-tag <tag>",i("optDistTag")).action(async function(){let e=this.opts();await Z({...e,distTag:await Se(e.root,e.distTag,e.commit)})});fe(W.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 We({...this.opts(),bump:e,branchFromCli:this.getOptionValueSource("branch")==="cli",messageFromCli:this.getOptionValueSource("commitMessage")==="cli"})});async function wi(){if(process.argv.length<=2){if(F())await cn();else W.outputHelp();return}await W.parseAsync(process.argv)}le(wi).catch((e)=>{if(e instanceof q){console.log(x(e.message));return}if(e instanceof g)console.error(j(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
|
-
beforeCommit: bun install
|
|
74
|
-
beforePublish: bun run clean && bun run build
|
|
77
|
+
`publish` 覆盖从变更识别、升版提交到上传的发布流程,供 `changed`、`version`、`release`、`publish` 和 `status` 使用。`test` 独立决定测试范围,既不继承 `publish.exclude`,也不执行 `publish.hooks`。例如 `src/__tests__` 可以被发布规则排除,同时被测试规则包含。
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
- packages/*/src
|
|
78
|
-
- packages/*/bin
|
|
79
|
+
所有路径模式仍相对仓库根目录,嵌套到 `publish` 后不用修改路径内容。命令行 `--branch` / `--commit-message` 名称保持不变,分别覆盖 `publish.branch` / `publish.commitMessage`。
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
## 配置
|
|
82
|
+
|
|
83
|
+
`change-versions.yml` 放在仓库根目录。不认识的字段会失败。发布路径写在 `publish.include` 和 `publish.exclude` 下;测试路径写在 `test.include` 和 `test.exclude` 下。顶层只接受共享的 `packagesDir`、`publish`、`test`。
|
|
84
|
+
|
|
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
|
|
|
@@ -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,69 @@ 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 显式串联命令。
|
|
374
|
+
|
|
375
|
+
### 发布 tag 列表
|
|
376
|
+
|
|
377
|
+
在 `publish.distTags` 配置常用 npm 标签,例如 `[latest, alpha, beta, next]`。交互运行 `release`、`release --resume` 和 `publish` 时,按配置顺序展示标签并默认选中第一项,当前推导标签若不在配置中则追加到末尾,也可自定义输入。显式 `--dist-tag` 跳过选择;非交互及 `--yes` 继续使用原有默认标签规则。此列表不改变预发布版本号的 `preid`。
|
|
378
|
+
|
|
379
|
+
所有选择与确认统一使用列表:上下键选择、回车确认,也支持输入数字后回车,q/Ctrl-C 退出。确认列表默认选中 `yes`,选择 `no` 直接退出。
|
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
|
|
|
@@ -141,3 +141,14 @@ beforePublish、afterPublish 在恢复时可能再次执行,因此需要可重
|
|
|
141
141
|
- CHANGELOG 的修复更新与恢复记录一起保存,失败后可继续恢复。
|
|
142
142
|
|
|
143
143
|
在确定方案前,保持现有行为,不把上述建议写成已实现功能。
|
|
144
|
+
|
|
145
|
+
## 源码职责
|
|
146
|
+
|
|
147
|
+
发布与测试保持各自的规则,底层读取和执行知识按以下职责集中:
|
|
148
|
+
|
|
149
|
+
- `history.ts`:解释版本批次、修复补发的源码归属、逐包基线和正式版历史窗口,并统一生成批次正文。入选不再通过发布执行模块读取历史。
|
|
150
|
+
- `publicationState.ts`:查询和解释发布状态、检查修复资格、结合 Git 标签判定外部就绪。实际上传及重试前仍重新查询 registry,初始观察不替代执行前检查。
|
|
151
|
+
- `proc.ts`:持有子进程生命周期及进程信号,先终止子进程再执行领域恢复回调。`transaction.ts` 负责恢复仍属于本轮的文件,`publish.ts` 负责发布结果汇总,`operation.ts` 负责仓库锁。
|
|
152
|
+
- `packages.ts`:读取工作区快照,集中包名、目录和声明索引。提交、暂存区与当前文件是不同快照;写入版本后重新读取,不沿用写入前的快照。
|
|
153
|
+
|
|
154
|
+
发布入选只按既有 dependencies / devDependencies 规则传播;测试继续使用历史与当前依赖关系的并集,并考虑 peerDependencies / optionalDependencies。共享读取不合并这两种策略。
|