@vitarx/release-cli 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-present 朱冲林
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # @vitarx/release-cli
2
+
3
+ 一个用于自动化发布 npm 包的 CLI 工具,支持 workspace 模式和多种包管理器。
4
+
5
+ ## 功能特性
6
+
7
+ - ✅ 自动化版本管理和发布
8
+ - ✅ 支持 npm、pnpm、yarn 包管理器检测
9
+ - ✅ 支持 workspace 多包项目管理
10
+ - ✅ 支持指定包名发布 (--package 选项)
11
+ - ✅ 交互式版本类型选择
12
+ - ✅ 预发布版本支持(alpha、beta、rc)
13
+ - ✅ 自动生成 changelog (workspace多包项目时仅在发布的包名同根包名时在根目录生成 changelog)
14
+ - ✅ dry-run 模式测试
15
+ - ✅ 错误回滚机制
16
+
17
+ ## 安装
18
+
19
+ ### 全局安装
20
+
21
+ ```bash
22
+ # 使用 npm
23
+ npm install -g @vitarx/release-cli
24
+
25
+ # 使用 pnpm
26
+ pnpm install -g @vitarx/release-cli
27
+
28
+ # 使用 yarn
29
+ yarn global add @vitarx/release-cli
30
+ ```
31
+
32
+ ### 临时使用 (无需安装)
33
+
34
+ ```bash
35
+ # 使用 npx
36
+ npx @vitarx/release-cli
37
+
38
+ # 使用 pnpm
39
+ pnpm dlx @vitarx/release-cli
40
+
41
+ # 使用 yarn
42
+ yarn dlx @vitarx/release-cli
43
+ ```
44
+
45
+ ## 使用方法
46
+
47
+ ### 基本使用
48
+
49
+ ```bash
50
+ # 发布当前包 (自动检测包管理器)
51
+ @vitarx/release-cli # 如果是 workspace 项目,会以对话形式选择要发布的包
52
+
53
+ # 指定包名发布 (workspace 项目)
54
+ @vitarx/release-cli --package <包名>
55
+
56
+ # 使用 dry-run 模式测试
57
+ @vitarx/release-cli --dry-run
58
+
59
+ # 指定版本类型
60
+ @vitarx/release-cli patch
61
+ @vitarx/release-cli minor
62
+ @vitarx/release-cli major
63
+ # 自定义版本号
64
+ @vitarx/release-cli 1.0.1
65
+ ```
66
+
67
+ ### 高级用法
68
+
69
+ ```bash
70
+ # 组合使用选项
71
+ @vitarx/release-cli patch --dry-run --package my-package
72
+ @vitarx/release-cli minor --verbose
73
+
74
+ # 预发布版本
75
+ @vitarx/release-cli prerelease --preid alpha
76
+ @vitarx/release-cli prerelease --preid beta
77
+ @vitarx/release-cli prerelease --preid rc
78
+
79
+ # 从预发布升级到正式版本
80
+ @vitarx/release-cli release
81
+
82
+ # changelog 相关选项
83
+ @vitarx/release-cli patch --preset conventionalcommits --infile HISTORY.md
84
+ @vitarx/release-cli minor --no-same-file --output-unreleased
85
+ @vitarx/release-cli major --tag-prefix "" --release-count 5
86
+ ```
87
+
88
+ ### 支持的版本类型
89
+
90
+ - `patch` - 修复 bug 或兼容性更新
91
+ - `minor` - 新增功能(兼容旧版本)
92
+ - `major` - 重大变更(不兼容旧版本)
93
+ - `prepatch` - 预发布补丁版本
94
+ - `preminor` - 预发布功能版本
95
+ - `premajor` - 预发布主版本
96
+ - `prerelease` - 预发布版本(递增 preid)
97
+ - `release` - 从预发布版本升级为正式版本
98
+ - `custom` - 自定义版本号
99
+
100
+ ### 命令行选项参考
101
+
102
+ | 选项 | 缩写 | 描述 | 默认值 |
103
+ |-----------------------|------|--------------------------|----------------|
104
+ | `--help` | `-h` | 显示帮助信息 | - |
105
+ | `--dry-run` | `-d` | 试运行模式,不实际执行操作 | `false` |
106
+ | `--preid` | - | 预发布版本标识符(alpha/beta/rc) | - |
107
+ | `--preset` | - | conventional-changelog预设 | `angular` |
108
+ | `--infile` | - | changelog输入文件名 | `CHANGELOG.md` |
109
+ | `--outfile` | `-o` | changelog输出文件名 | 同输入文件 |
110
+ | `--same-file` | `-s` | 追加到现有changelog文件 | `true` |
111
+ | `--no-same-file` | - | 禁用追加到现有changelog文件 | - |
112
+ | `--append` | `-a` | 将新版本追加到旧版本后面 | - |
113
+ | `--first-release` | `-f` | 首次生成changelog | - |
114
+ | `--skip-unstable` | - | 跳过不稳定版本(alpha/beta/rc) | - |
115
+ | `--output-unreleased` | `-u` | 输出未发布的变更日志 | - |
116
+ | `--config` | `-n` | 自定义配置文件路径 | - |
117
+ | `--context` | `-c` | 模板变量上下文文件 | - |
118
+ | `--commit-path` | - | 只生成指定目录的提交日志 | - |
119
+ | `--tag-prefix` | - | Git 标签前缀 | `v` |
120
+ | `--verbose` | `-v` | 详细输出模式 | `false` |
121
+ | `--release-count` | - | 生成的发布记录数量 | `0` |
122
+ | `--package` | - | 指定要发布的包名 | - |
123
+
124
+ ### Workspace 支持
125
+
126
+ 如果你的项目是 workspace 项目(包含 `workspaces` 配置),工具会自动检测并让你选择要发布的包。
127
+
128
+ 你可以使用 `--package` 选项直接指定要发布的包名,避免交互式选择:
129
+
130
+ ```bash
131
+ @vitarx/release-cli --package my-package
132
+ @vitarx/release-cli patch --package my-package --dry-run
133
+ ```
134
+
135
+ ## 发布流程
136
+
137
+ 1. **Git 状态检查** - 确保工作目录干净
138
+ 2. **版本类型选择** - 交互式选择版本升级类型
139
+ 3. **版本号更新** - 自动更新 package.json 版本号
140
+ 4. **构建项目** - 执行构建命令(如果存在)
141
+ 5. **Changelog 生成** - 如果是主包发布则使用 conventional-changelog 生成变更日志
142
+ 6. **Git 提交** - 提交版本变更和 changelog
143
+ 7. **NPM 发布** - 发布到 npm 仓库
144
+ 8. **Git 标签** - 创建版本标签
145
+ 9. **Git 推送** - 推送代码和标签到远程仓库
146
+
147
+ > 从第2个步骤过后发生异常,都会回滚所有更改。
148
+
149
+ ## 配置要求
150
+
151
+ ### 项目配置
152
+
153
+ 确保你的项目包含:
154
+
155
+ - `package.json` - 包含正确的包信息
156
+ - 构建脚本 - 在 `package.json` 的 `scripts.build` 中定义
157
+ - Git 仓库 - 已初始化并配置远程仓库
158
+ - 多包项目 - 在 `package.json` 中指定 `workspaces`和`name`(用于生成 changelog)
159
+
160
+ ### Conventional Changelog
161
+
162
+ 1. 工具使用 `conventional-changelog-cli` 生成
163
+ changelog,确保你的提交信息符合 [Conventional Commits](https://www.conventionalcommits.org/) 规范。
164
+ 2. 如果是多包项目,请确保 根`package.json` 中包含 `name` 字段,如果发布的包名同根包名,则生成根目录的 changelog。
165
+
166
+ ## 开发
167
+
168
+ ### 安装依赖
169
+
170
+ ```bash
171
+ pnpm install
172
+ ```
173
+
174
+ ### 构建项目
175
+
176
+ ```bash
177
+ pnpm run build
178
+ ```
179
+
180
+ ### 本地测试
181
+
182
+ ```bash
183
+ # 使用 dry-run 模式测试
184
+ node dist/index.js --dry-run
185
+
186
+ # 测试特定版本类型
187
+ node dist/index.js patch --dry-run
188
+
189
+ # 测试指定包名发布
190
+ node dist/index.js patch --dry-run --package test-package
191
+ ```
192
+
193
+ ## 许可证
194
+
195
+ MIT License
@@ -0,0 +1,12 @@
1
+ export declare const VERSION_TYPE_DESC: {
2
+ readonly patch: "修复 bug 或兼容性更新";
3
+ readonly minor: "新增功能(兼容旧版本)";
4
+ readonly major: "重大变更(不兼容旧版本)";
5
+ readonly prepatch: "预发布补丁版本(补丁测试版本)";
6
+ readonly preminor: "预发布功能版本(新功能测试版本)";
7
+ readonly premajor: "预发布主版本(重大变更测试版本)";
8
+ readonly prerelease: "预发布版本(递增 preid)";
9
+ readonly release: "测试结束,发布正式版本";
10
+ };
11
+ export type VersionType = keyof typeof VERSION_TYPE_DESC;
12
+ export declare const VERSION_TYPE: VersionType[];
@@ -0,0 +1,11 @@
1
+ export const VERSION_TYPE_DESC = {
2
+ patch: '修复 bug 或兼容性更新',
3
+ minor: '新增功能(兼容旧版本)',
4
+ major: '重大变更(不兼容旧版本)',
5
+ prepatch: '预发布补丁版本(补丁测试版本)',
6
+ preminor: '预发布功能版本(新功能测试版本)',
7
+ premajor: '预发布主版本(重大变更测试版本)',
8
+ prerelease: '预发布版本(递增 preid)',
9
+ release: '测试结束,发布正式版本'
10
+ };
11
+ export const VERSION_TYPE = Object.keys(VERSION_TYPE_DESC);
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import process from 'node:process';
3
+ import { main } from './release.js';
4
+ // 主入口
5
+ main().catch(err => {
6
+ console.error(err);
7
+ process.exit(1);
8
+ });
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ts-node
2
+ /**
3
+ * 主函数,用于执行包发布流程
4
+ * 该函数会检查Git状态,检测包管理器,判断是否为workspace项目,
5
+ * 并根据项目类型执行相应的发布逻辑
6
+ *
7
+ * @returns {Promise<void>} 返回一个Promise,解析为void
8
+ */
9
+ export declare function main(): Promise<void>;
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env ts-node
2
+ import chalk from 'chalk';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import process from 'node:process';
6
+ import { checkGitStatus, detectPackageManager, formatVersionNumber, getWorkspacePackages, isWorkspaceProject, log, prompt, rollbackChanges, runCommand, selectVersion, updateVersion } from './utils.js';
7
+ /**
8
+ * 解析命令行参数
9
+ * @returns 包含版本类型、预发布标识符、dry-run模式等参数的对象
10
+ */
11
+ function parseArgs() {
12
+ // 获取命令行参数,排除前两个元素(node和脚本路径)
13
+ const args = process.argv.slice(2);
14
+ // 初始化结果对象,设置默认值
15
+ const result = {
16
+ isDryRun: false, // 是否为试运行模式
17
+ showHelp: false, // 是否显示帮助信息
18
+ changelogPreset: 'angular', // 更改日志预设模板
19
+ changelogInfile: 'CHANGELOG.md', // 更改日志输入文件
20
+ changelogSameFile: true, // 是否使用同一文件
21
+ changelogReleaseCount: 0, // 发布版本数量
22
+ changelogTagPrefix: 'v' // 版本标签前缀
23
+ };
24
+ // 遍历所有命令行参数
25
+ for (let i = 0; i < args.length; i++) {
26
+ const arg = args[i];
27
+ if (arg === '--help' || arg === '-h') {
28
+ result.showHelp = true;
29
+ }
30
+ else if (arg === '--dry-run' || arg === '-d') {
31
+ result.isDryRun = true;
32
+ }
33
+ else if (arg === '--preid' && args[i + 1]) {
34
+ result.preid = args[++i];
35
+ }
36
+ else if (arg === '--preset' && args[i + 1]) {
37
+ result.changelogPreset = args[++i];
38
+ }
39
+ else if (arg === '--infile' && args[i + 1]) {
40
+ result.changelogInfile = args[++i];
41
+ }
42
+ else if (arg === '--same-file' || arg === '-s') {
43
+ result.changelogSameFile = true;
44
+ }
45
+ else if (arg === '--no-same-file') {
46
+ result.changelogSameFile = false;
47
+ }
48
+ else if (arg === '--release-count' && args[i + 1]) {
49
+ result.changelogReleaseCount = parseInt(args[++i], 10);
50
+ }
51
+ else if (arg === '--outfile' && args[i + 1]) {
52
+ result.changelogOutfile = args[++i];
53
+ }
54
+ else if (arg === '--append' || arg === '-a') {
55
+ result.changelogAppend = true;
56
+ }
57
+ else if (arg === '--first-release' || arg === '-f') {
58
+ result.changelogFirstRelease = true;
59
+ }
60
+ else if (arg === '--skip-unstable') {
61
+ result.changelogSkipUnstable = true;
62
+ }
63
+ else if (arg === '--output-unreleased' || arg === '-u') {
64
+ result.changelogOutputUnreleased = true;
65
+ }
66
+ else if (arg === '--config' && args[i + 1]) {
67
+ result.changelogConfig = args[++i];
68
+ }
69
+ else if (arg === '--context' && args[i + 1]) {
70
+ result.changelogContext = args[++i];
71
+ }
72
+ else if (arg === '--commit-path' && args[i + 1]) {
73
+ result.changelogCommitPath = args[++i];
74
+ }
75
+ else if (arg === '--tag-prefix' && args[i + 1]) {
76
+ result.changelogTagPrefix = args[++i];
77
+ }
78
+ else if (arg === '--verbose' || arg === '-v') {
79
+ result.changelogVerbose = true;
80
+ }
81
+ else if (arg === '--package' && args[i + 1]) {
82
+ result.packageName = args[++i];
83
+ }
84
+ else if (!arg.startsWith('--')) {
85
+ result.versionType = arg;
86
+ }
87
+ }
88
+ return result;
89
+ }
90
+ /**
91
+ * 发布包到 npm 的主函数
92
+ * @param pkgDir - 包的目录路径
93
+ * @param packageManager - 检测到的包管理器类型
94
+ * @param isDryRun - 是否为试运行模式,默认为 false
95
+ * @param args - 命令行参数对象
96
+ */
97
+ async function publishPackage(pkgDir, packageManager, isDryRun = false, args) {
98
+ // 获取 package.json 的完整路径
99
+ const pkgPath = path.join(pkgDir, 'package.json');
100
+ // 读取并解析 package.json 文件
101
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
102
+ // 获取当前版本号
103
+ const currentVersion = pkg.version;
104
+ // 新版本号
105
+ let newVersion = '';
106
+ try {
107
+ if (args.versionType) {
108
+ newVersion = await formatVersionNumber(currentVersion, args.versionType, args.preid);
109
+ }
110
+ else {
111
+ newVersion = await selectVersion(currentVersion);
112
+ }
113
+ }
114
+ catch (e) {
115
+ log.error(e.message);
116
+ process.exit(1);
117
+ }
118
+ // 版本标签
119
+ let tagName;
120
+ try {
121
+ // -------------------- Step 3: 更新版本号 --------------------
122
+ updateVersion(pkgPath, newVersion, isDryRun);
123
+ // 打印发布信息
124
+ console.log(`\n📦 新版本: ${chalk.green(pkg.name)}@${chalk.cyan(newVersion)}`);
125
+ // 确认是否发布
126
+ const confirm = await prompt(`是否确认发布 ${pkg.name}@${newVersion}? (y/N) `);
127
+ if (confirm && confirm.toLowerCase() !== 'y') {
128
+ log.warn('已取消发布');
129
+ process.exit(0);
130
+ }
131
+ // -------------------- Step 4: 构建和测试项目 --------------------
132
+ log.info(`📦 Building package: ${pkg.name}...`);
133
+ runCommand(`npm run build --prefix ${pkgDir}`);
134
+ // -------------------- Step 5: 生成日志 --------------------
135
+ // 是否为根包
136
+ const isRootPackage = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8')).name === pkg.name;
137
+ if (isRootPackage) {
138
+ // 生成 changelog
139
+ try {
140
+ log.info(`📝 正在生成 CHANGELOG.md...`);
141
+ const changelogArgs = [
142
+ '-p', args.changelogPreset || 'angular',
143
+ '-i', args.changelogInfile || 'CHANGELOG.md',
144
+ args.changelogOutfile ? ['-o', args.changelogOutfile] : '',
145
+ args.changelogSameFile ? '-s' : '',
146
+ args.changelogAppend ? '-a' : '',
147
+ args.changelogFirstRelease ? '-f' : '',
148
+ args.changelogSkipUnstable ? '--skip-unstable' : '',
149
+ args.changelogOutputUnreleased ? '-u' : '',
150
+ args.changelogConfig ? ['-n', args.changelogConfig] : '',
151
+ args.changelogContext ? ['-c', args.changelogContext] : '',
152
+ args.changelogCommitPath ? ['--commit-path', args.changelogCommitPath] : '',
153
+ args.changelogTagPrefix ? ['-t', args.changelogTagPrefix] : '',
154
+ args.changelogVerbose ? '-v' : '',
155
+ '-r', args.changelogReleaseCount ? args.changelogReleaseCount.toString() : '0'
156
+ ].flat().filter(arg => arg !== '');
157
+ runCommand(`conventional-changelog ${changelogArgs.join(' ')}`, isDryRun);
158
+ }
159
+ catch {
160
+ const confirm = await prompt(chalk.yellow('⚠️ 生成 changelog 失败,要继续发布流程吗? (y/N) '));
161
+ if (confirm.toLowerCase() !== 'y') {
162
+ log.warn('已取消发布');
163
+ process.exit(0);
164
+ }
165
+ }
166
+ }
167
+ // -------------------- Step 6: 提交更改到 git --------------------
168
+ log.info(`📤 Committing changes...`);
169
+ runCommand(`git add ${pkgPath}${isRootPackage ? ` ${args.changelogInfile || 'CHANGELOG.md'}` : ''}`, isDryRun);
170
+ runCommand(`git commit -m "build(${pkg.name}): release ${args.changelogTagPrefix + newVersion}"`, isDryRun);
171
+ if (isRootPackage) {
172
+ log.info(`🏷 Tagging: ${args.changelogTagPrefix + newVersion}`);
173
+ runCommand(`git tag -a ${args.changelogTagPrefix + newVersion} -m "Release ${newVersion}"`, isDryRun);
174
+ // 发布成功后把标签赋值给 tagName
175
+ tagName = `${args.changelogTagPrefix + newVersion}`;
176
+ }
177
+ // -------------------- Step 7: 发布到 npm --------------------
178
+ log.info(`🚀 Publishing ${pkg.name}@${newVersion}...`);
179
+ const publishCmd = packageManager === 'pnpm'
180
+ ? `pnpm publish ${pkgDir} --access public --registry https://registry.npmjs.org/`
181
+ : `npm publish ${pkgDir} --access public --registry https://registry.npmjs.org/`;
182
+ // 执行发布命令
183
+ runCommand(publishCmd, isDryRun);
184
+ // -------------------- Step 8: 推送代码和标签到远程仓库 --------------------
185
+ log.info('⬆️ Pushing to remote...');
186
+ try {
187
+ runCommand(`git push${tagName ? '&& git push --tags' : ''}`, isDryRun);
188
+ }
189
+ catch {
190
+ log.warn('包已发布到pnm,但推送代码和标签到远程仓库意外失败,请仔细核查npm仓库和远程仓库状态。');
191
+ }
192
+ // 打印发布成功信息
193
+ log.success(`发布完成 ${pkg.name}@${newVersion}` + (isDryRun ? ' (dry-run)' : ''));
194
+ }
195
+ catch (err) {
196
+ // 错误处理
197
+ log.error(err.message);
198
+ // 回滚更改
199
+ rollbackChanges(tagName, isDryRun);
200
+ process.exit(1);
201
+ }
202
+ }
203
+ /**
204
+ * 主函数,用于执行包发布流程
205
+ * 该函数会检查Git状态,检测包管理器,判断是否为workspace项目,
206
+ * 并根据项目类型执行相应的发布逻辑
207
+ *
208
+ * @returns {Promise<void>} 返回一个Promise,解析为void
209
+ */
210
+ export async function main() {
211
+ const args = parseArgs();
212
+ if (args.showHelp) {
213
+ console.log(chalk.cyan('📦 Release CLI - NPM包发布工具'));
214
+ console.log('');
215
+ console.log(chalk.yellow('使用方法:'));
216
+ console.log(' release-cli [版本类型] [选项]');
217
+ console.log('');
218
+ console.log(chalk.yellow('版本类型:'));
219
+ console.log(' patch - 修复 bug 或兼容性更新');
220
+ console.log(' minor - 新增功能(兼容旧版本)');
221
+ console.log(' major - 重大变更(不兼容旧版本)');
222
+ console.log(' prepatch - 预发布补丁版本');
223
+ console.log(' preminor - 预发布功能版本');
224
+ console.log(' premajor - 预发布主版本');
225
+ console.log(' prerelease - 预发布版本(递增 preid)');
226
+ console.log(' release - 测试结束,发布正式版本');
227
+ console.log(' custom - 自定义版本号');
228
+ console.log('');
229
+ console.log(chalk.yellow('选项:'));
230
+ console.log(' -h, --help - 显示帮助信息');
231
+ console.log(' -d, --dry-run - 试运行模式,不实际执行操作,(默认false)');
232
+ console.log(' --preid <标识符> - 预发布版本标识符(alpha/beta/rc)');
233
+ console.log(' --preset <预设> - conventional-changelog预设(默认: angular)');
234
+ console.log(' --infile <文件名> - changelog输入文件名(默认: CHANGELOG.md)');
235
+ console.log(' -o, --outfile <文件名> - changelog输出文件名(默认: 同输入文件)');
236
+ console.log(' -s, --same-file - 追加到现有changelog文件(默认启用)');
237
+ console.log(' --no-same-file - 禁用追加到现有changelog文件');
238
+ console.log(' -a, --append - 将新版本追加到旧版本后面');
239
+ console.log(' -f, --first-release - 首次生成changelog');
240
+ console.log(' --skip-unstable - 跳过不稳定版本(alpha/beta/rc)');
241
+ console.log(' -u, --output-unreleased - 输出未发布的变更日志');
242
+ console.log(' -n, --config <文件> - 自定义配置文件路径');
243
+ console.log(' -c, --context <文件> - 模板变量上下文文件');
244
+ console.log(' --commit-path <目录> - 只生成指定目录的提交日志');
245
+ console.log(' --tag-prefix <前缀> - Git 标签前缀(默认: v)');
246
+ console.log(' -v, --verbose - 详细输出模式');
247
+ console.log(' --release-count <数量> - 生成的发布记录数量(默认: 0)');
248
+ console.log(' --package <包名> - 指定要发布的包名');
249
+ console.log('');
250
+ console.log(chalk.yellow('示例:'));
251
+ console.log(' release-cli patch --dry-run');
252
+ console.log(' release-cli minor --preid beta');
253
+ console.log(' release-cli --preset angular --infile CHANGELOG.md');
254
+ process.exit(0);
255
+ }
256
+ const isDryRun = args.isDryRun;
257
+ // 如果有未提交的更改,则报错退出
258
+ if (!isDryRun && !checkGitStatus()) {
259
+ log.error('请先提交或暂存当前更改再发布。');
260
+ process.exit(1);
261
+ }
262
+ const packageManager = detectPackageManager();
263
+ const isWorkspace = isWorkspaceProject();
264
+ log.info(`当前包管理器: ${packageManager}`);
265
+ log.info(`当前工作目录: ${process.cwd()}`);
266
+ log.info(`当前运行模式: ${isDryRun ? 'dry-run' : 'publish'}`);
267
+ if (isWorkspace) {
268
+ log.info('检测到 workspace 项目');
269
+ const packages = getWorkspacePackages();
270
+ if (packages.length === 0) {
271
+ log.error('未找到 workspace 包');
272
+ process.exit(1);
273
+ }
274
+ let selectedPackage;
275
+ if (args.packageName) {
276
+ // 如果指定了包名,查找对应的包
277
+ selectedPackage = packages.find(pkg => pkg.name === args.packageName);
278
+ if (!selectedPackage) {
279
+ log.error(`未找到包名为 ${args.packageName} 的包`);
280
+ process.exit(1);
281
+ }
282
+ log.info(`已选择包: ${selectedPackage.name}`);
283
+ }
284
+ else {
285
+ // 未指定包名,显示选择界面
286
+ console.log(chalk.cyan('\n请选择要发布的包:'));
287
+ packages.forEach((pkg, i) => {
288
+ console.log(`${chalk.yellow(i + 1 + '.')}${chalk.green(pkg.name)}:${chalk.cyan(pkg.version)} (${pkg.path})`);
289
+ });
290
+ const answer = await prompt('\n输入序号选择包: ');
291
+ const index = parseInt(answer, 10);
292
+ if (isNaN(index) || index < 1 || index > packages.length) {
293
+ log.error('无效的选择');
294
+ process.exit(1);
295
+ }
296
+ selectedPackage = packages[index - 1];
297
+ }
298
+ await publishPackage(selectedPackage.path, packageManager, isDryRun, args);
299
+ }
300
+ else {
301
+ // 单包项目,发布根目录
302
+ await publishPackage(process.cwd(), packageManager, isDryRun, args);
303
+ }
304
+ }
305
+ // 执行主函数
306
+ if (import.meta.url === `file://${process.argv[1]}`) {
307
+ main().catch((err) => {
308
+ log.error(err.message);
309
+ process.exit(1);
310
+ });
311
+ }
package/dist/type.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Package.json 文件结构接口
3
+ */
4
+ export interface PackageJson {
5
+ /** 包名称 */
6
+ name: string;
7
+ /** 包版本号 */
8
+ version: string;
9
+ /** 生产环境依赖 */
10
+ dependencies?: Record<string, string>;
11
+ /** 开发环境依赖 */
12
+ devDependencies?: Record<string, string>;
13
+ /** 对等依赖 */
14
+ peerDependencies?: Record<string, string>;
15
+ /** 可选依赖 */
16
+ optionalDependencies?: Record<string, string>;
17
+ /** 其他自定义字段 */
18
+ [key: string]: unknown;
19
+ }
20
+ /**
21
+ * 发布配置接口
22
+ */
23
+ export interface ReleaseConfig {
24
+ /** 版本类型 (major, minor, patch, premajor, preminor, prepatch, prerelease) */
25
+ versionType?: string;
26
+ /** 预发布标识 (如 alpha, beta, rc) */
27
+ preid?: string;
28
+ /** 要发布的包名 */
29
+ packageName?: string;
30
+ /** 是否为干运行模式,不实际执行操作 */
31
+ isDryRun: boolean;
32
+ /** changelog 预设配置 */
33
+ changelogPreset?: string;
34
+ /** changelog 输入文件名 */
35
+ changelogInfile?: string;
36
+ /** changelog 输出文件名 */
37
+ changelogOutfile?: string;
38
+ /** 是否在同一文件中更新 changelog */
39
+ changelogSameFile?: boolean;
40
+ /** 要包含的发布版本数量 */
41
+ changelogReleaseCount?: number;
42
+ /** 是否追加到现有 changelog 文件 */
43
+ changelogAppend?: boolean;
44
+ /** 是否为首次发布 */
45
+ changelogFirstRelease?: boolean;
46
+ /** 是否跳过不稳定版本 */
47
+ changelogSkipUnstable?: boolean;
48
+ /** 是否输出未发布的变更 */
49
+ changelogOutputUnreleased?: boolean;
50
+ /** changelog 配置文件路径 */
51
+ changelogConfig?: string;
52
+ /** changelog 上下文配置 */
53
+ changelogContext?: string;
54
+ /** 只生成指定目录的提交日志 */
55
+ changelogCommitPath?: string;
56
+ /** Git 标签前缀 */
57
+ changelogTagPrefix?: string;
58
+ /** 是否显示详细输出 */
59
+ changelogVerbose?: boolean;
60
+ /** 是否显示帮助信息 */
61
+ showHelp: boolean;
62
+ }
package/dist/type.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,95 @@
1
+ /**
2
+ * 创建一个命令行提示函数,用于向用户提问并获取用户输入
3
+ * @param question - 要向用户显示的问题字符串
4
+ * @returns {Promise<string>} 返回一个Promise,解析为用户输入的答案(去除首尾空格)
5
+ */
6
+ export declare function prompt(question: string): Promise<string>;
7
+ /**
8
+ * 检查Git工作目录的状态
9
+ * 该函数通过执行git status命令并检查输出结果来确定工作目录是否干净
10
+ *
11
+ * @returns {boolean} 如果工作目录干净(没有未跟踪的文件、没有修改的文件)则返回true,否则返回false
12
+ */
13
+ export declare function checkGitStatus(): boolean;
14
+ /**
15
+ * 日志输出
16
+ *
17
+ * @property info - 信息日志
18
+ * @property success - 成功日志
19
+ * @property error - 错误日志
20
+ * @property warn - 警告日志
21
+ */
22
+ export declare const log: {
23
+ readonly info: (message: string) => void;
24
+ readonly success: (message: string) => void;
25
+ readonly error: (message: string) => void;
26
+ readonly warn: (message: string) => void;
27
+ };
28
+ /**
29
+ * 选择版本升级类型
30
+ * @param currentVersion 当前版本号
31
+ * @returns {Promise<string>} 返回格式化后的新版本号
32
+ */
33
+ export declare function selectVersion(currentVersion: string): Promise<string>;
34
+ /**
35
+ * 获取预发布标识符(preid)的函数
36
+ * @param currentVersion 当前版本号
37
+ * @param isPreRelease 是否为预发布版本
38
+ * @returns {Promise<string>} 返回预发布标识符
39
+ */
40
+ export declare function getPreid(currentVersion: string, isPreRelease: boolean): Promise<string>;
41
+ /**
42
+ * 检测当前项目使用的包管理器
43
+ * @returns 返回 'npm' | 'pnpm' | 'yarn' 之一,表示当前项目使用的包管理器类型
44
+ */
45
+ export declare function detectPackageManager(): 'npm' | 'pnpm' | 'yarn';
46
+ /**
47
+ * 判断当前项目是否为工作空间项目
48
+ * 通过检查package.json中是否存在workspaces字段来判断
49
+ * @returns {boolean} 如果是工作空间项目返回true,否则返回false
50
+ */
51
+ export declare function isWorkspaceProject(): boolean;
52
+ /**
53
+ * 获取工作空间中的所有包信息
54
+ * @returns {Array<{ name: string; path: string; version: string }>} 返回一个包含包名、路径和版本的数组,如果不是工作空间项目则返回空数组
55
+ */
56
+ export declare function getWorkspacePackages(): Array<{
57
+ name: string;
58
+ path: string;
59
+ version: string;
60
+ }>;
61
+ /**
62
+ * 格式化版本号函数
63
+ * 根据当前版本号和下一个版本类型或自定义版本号生成新的版本号
64
+ * @param currentVersion 当前版本号
65
+ * @param nextVersion 下一个版本类型或自定义版本号
66
+ * @param preid 预发布标识符(可选)
67
+ * @returns {Promise<string>} 返回格式化后的新版本号
68
+ * @throws Error 如果输入的版本类型/版本号无效,则抛出错误
69
+ */
70
+ export declare function formatVersionNumber(currentVersion: string, // 当前版本号,字符串类型
71
+ nextVersion: string, // 下一个版本类型或自定义版本号,字符串类型
72
+ preid: string | undefined): Promise<string>;
73
+ /**
74
+ * 执行命令函数
75
+ * @param cmd - 需要执行的命令字符串
76
+ * @param isDryRun - 是否为试运行模式,默认为false
77
+ * 当isDryRun为true时,仅打印命令而不实际执行
78
+ * 当isDryRun为false时,打印命令并实际执行
79
+ */
80
+ export declare function runCommand(cmd: string, isDryRun?: boolean): void;
81
+ /**
82
+ * 回滚代码变更的函数
83
+ * @param tagName - 可选的标签名称,用于删除创建的标签
84
+ * @param isDryRun - 是否为试运行模式,默认为false
85
+ */
86
+ export declare function rollbackChanges(tagName?: string, isDryRun?: boolean): void;
87
+ /**
88
+ * 更新指定包的版本号
89
+ * @param pkgPath - 包的文件路径
90
+ * @param newVersion - 新的版本号
91
+ * @param isDryRun - 是否为试运行模式,默认为false
92
+ */
93
+ export declare function updateVersion(pkgPath: string, // 包的文件路径
94
+ newVersion: string, // 新的版本号
95
+ isDryRun?: boolean): void;
package/dist/utils.js ADDED
@@ -0,0 +1,337 @@
1
+ import chalk from 'chalk';
2
+ import { execSync } from 'node:child_process';
3
+ import * as console from 'node:console';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import process from 'node:process';
7
+ import readline from 'node:readline';
8
+ import semver from 'semver';
9
+ import { VERSION_TYPE, VERSION_TYPE_DESC } from './constant.js';
10
+ /**
11
+ * 创建一个命令行提示函数,用于向用户提问并获取用户输入
12
+ * @param question - 要向用户显示的问题字符串
13
+ * @returns {Promise<string>} 返回一个Promise,解析为用户输入的答案(去除首尾空格)
14
+ */
15
+ export function prompt(question) {
16
+ // 使用readline模块创建一个接口,用于处理用户输入和输出
17
+ const rl = readline.createInterface({
18
+ input: process.stdin, // 设置输入流为标准输入
19
+ output: process.stdout // 设置输出流为标准输出
20
+ });
21
+ // 返回一个新的Promise,用于异步处理用户输入
22
+ return new Promise(resolve =>
23
+ // 使用rl.question方法向用户提问,并在收到答案后执行回调
24
+ rl.question(question, ans => {
25
+ rl.close(); // 关闭readline接口
26
+ resolve(ans.trim()); // 解析Promise,返回去除首尾空格的答案
27
+ }));
28
+ }
29
+ /**
30
+ * 检查Git工作目录的状态
31
+ * 该函数通过执行git status命令并检查输出结果来确定工作目录是否干净
32
+ *
33
+ * @returns {boolean} 如果工作目录干净(没有未跟踪的文件、没有修改的文件)则返回true,否则返回false
34
+ */
35
+ export function checkGitStatus() {
36
+ // 执行git status --porcelain命令,获取简短的git状态输出
37
+ // --porcelain选项会以机器可读的格式显示状态信息
38
+ const gitStatus = execSync('git status --porcelain').toString().trim();
39
+ // 检查状态输出是否为空字符串
40
+ // 如果为空,表示工作目录干净,没有未提交的更改
41
+ return gitStatus === '';
42
+ }
43
+ /**
44
+ * 日志输出
45
+ *
46
+ * @property info - 信息日志
47
+ * @property success - 成功日志
48
+ * @property error - 错误日志
49
+ * @property warn - 警告日志
50
+ */
51
+ export const log = {
52
+ info: (message) => {
53
+ console.log(chalk.cyan(`Info:${message}`));
54
+ },
55
+ success: (message) => {
56
+ console.log(chalk.green(`Success:✅ ${message}`));
57
+ },
58
+ error: (message) => {
59
+ console.log(chalk.red(`Error:❌ ${message}`));
60
+ },
61
+ warn: (message) => {
62
+ console.log(chalk.yellow(`Warn:⚠️ ${message}`));
63
+ }
64
+ };
65
+ /**
66
+ * 选择版本升级类型
67
+ * @param currentVersion 当前版本号
68
+ * @returns {Promise<string>} 返回格式化后的新版本号
69
+ */
70
+ export async function selectVersion(currentVersion) {
71
+ console.log(chalk.cyan('\n请选择版本升级类型:')); // 打印提示信息
72
+ const items = [];
73
+ // 解析预发布版本标识符和版本号
74
+ const prerelease = semver.prerelease(currentVersion);
75
+ // 如果已经是预发布版本,且存在标识符,则返回标识符
76
+ const defaultPreid = prerelease && prerelease.length === 2 ? prerelease[0] : 'alpha';
77
+ VERSION_TYPE.forEach((t) => {
78
+ const next = semver.inc(currentVersion, t, t.startsWith('pre') ? defaultPreid : ''); // 计算下一个版本号
79
+ if (next) {
80
+ items.push(t);
81
+ console.log(`${chalk.yellow(items.length + '.')}${chalk.green(t)}:${VERSION_TYPE_DESC[t]}${chalk.cyan(`(${currentVersion} → ${next})`)}`);
82
+ }
83
+ });
84
+ console.log(`${chalk.yellow('&.')}${chalk.green('custom')}:输入一个自定义版本号`);
85
+ // 如果已经是预发布版本,且存在标识符,则默认更新预发布版本
86
+ const defaultType = prerelease && prerelease.length === 2 ? 'prepatch' : 'patch';
87
+ const answer = await prompt(`
88
+ 输入序号选择类型 (默认 ${defaultType}): `); // 获取用户输入
89
+ // 自定义版本号,则返回输入的版本号
90
+ if (answer.length > 1) {
91
+ return await formatVersionNumber(currentVersion, answer, undefined);
92
+ }
93
+ const index = parseInt(answer, 10); // 将输入转换为数字
94
+ const type = items[index - 1] || defaultType;
95
+ return await formatVersionNumber(currentVersion, type, undefined);
96
+ }
97
+ /**
98
+ * 获取预发布标识符(preid)的函数
99
+ * @param currentVersion 当前版本号
100
+ * @param isPreRelease 是否为预发布版本
101
+ * @returns {Promise<string>} 返回预发布标识符
102
+ */
103
+ export async function getPreid(currentVersion, isPreRelease) {
104
+ // 解析预发布版本标识符和版本号
105
+ const prerelease = semver.prerelease(currentVersion);
106
+ // 如果已经是预发布版本,且存在标识符,则返回标识符
107
+ const defaultPreid = prerelease?.length === 2 ? prerelease[0] : 'alpha';
108
+ if (isPreRelease && prerelease?.length === 2)
109
+ return defaultPreid;
110
+ // 打印提示信息,让用户选择预发布标识
111
+ log.info('请选择预发布标识 preid:');
112
+ // 定义预发布标识选项列表
113
+ const preids = {
114
+ alpha: '内测版本',
115
+ beta: '公测版本',
116
+ rc: '候选版本'
117
+ };
118
+ const items = Object.keys(preids);
119
+ // 打印所有预发布标识选项
120
+ items.forEach((p, i) => console.log(`${chalk.yellow(i + 1 + '.')}${chalk.green(p)}:${preids[p]}`));
121
+ console.log(`${chalk.yellow('&.')}${chalk.green('custom')}:输入一个自定义标识符`);
122
+ // 提示用户输入序号选择preid,默认为alpha
123
+ const answer = await prompt(`输入序号选择 preid (默认 ${defaultPreid}): `);
124
+ // 如果未输入,则返回默认值
125
+ if (!answer)
126
+ return defaultPreid;
127
+ // 将输入转换为数字
128
+ const index = parseInt(answer, 10);
129
+ // 自定义版本号,则返回输入的版本号
130
+ if (isNaN(index))
131
+ return answer;
132
+ // 返回用户选择的preid,如果未选择则返回默认值
133
+ return items[index - 1] || defaultPreid;
134
+ }
135
+ /**
136
+ * 检测当前项目使用的包管理器
137
+ * @returns 返回 'npm' | 'pnpm' | 'yarn' 之一,表示当前项目使用的包管理器类型
138
+ */
139
+ export function detectPackageManager() {
140
+ try {
141
+ // 检查是否存在 pnpm-lock.yaml 或 yarn.lock
142
+ if (fs.existsSync('pnpm-lock.yaml')) {
143
+ return 'pnpm';
144
+ }
145
+ else if (fs.existsSync('yarn.lock')) {
146
+ return 'yarn';
147
+ }
148
+ else if (fs.existsSync('package-lock.json')) {
149
+ return 'npm';
150
+ }
151
+ // 如果没有lock文件,尝试执行命令检测
152
+ try {
153
+ execSync('pnpm --version', { stdio: 'ignore' });
154
+ return 'pnpm';
155
+ }
156
+ catch {
157
+ try {
158
+ execSync('yarn --version', { stdio: 'ignore' });
159
+ return 'yarn';
160
+ }
161
+ catch {
162
+ return 'npm';
163
+ }
164
+ }
165
+ }
166
+ catch {
167
+ return 'npm'; // 默认使用 npm
168
+ }
169
+ }
170
+ /**
171
+ * 判断当前项目是否为工作空间项目
172
+ * 通过检查package.json中是否存在workspaces字段来判断
173
+ * @returns {boolean} 如果是工作空间项目返回true,否则返回false
174
+ */
175
+ export function isWorkspaceProject() {
176
+ try {
177
+ // 读取并解析package.json文件
178
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
179
+ // 检查是否存在workspaces字段,并转换为布尔值返回
180
+ return !!pkg.workspaces;
181
+ }
182
+ catch {
183
+ // 如果读取或解析package.json时发生错误,返回false
184
+ return false;
185
+ }
186
+ }
187
+ /**
188
+ * 获取工作空间中的所有包信息
189
+ * @returns {Array<{ name: string; path: string; version: string }>} 返回一个包含包名、路径和版本的数组,如果不是工作空间项目则返回空数组
190
+ */
191
+ export function getWorkspacePackages() {
192
+ // 首先检查是否为工作空间项目,如果不是则直接返回空数组
193
+ if (!isWorkspaceProject())
194
+ return [];
195
+ // 用于存储所有包信息的数组
196
+ const packages = [];
197
+ try {
198
+ // 读取根目录的package.json文件
199
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
200
+ // 获取workspaces配置,如果没有则为空数组
201
+ const workspaces = pkg.workspaces || [];
202
+ // 遍历所有workspace模式
203
+ for (const workspacePattern of workspaces) {
204
+ // 获取所有目录
205
+ const workspaceDirs = fs
206
+ .readdirSync('.', { withFileTypes: true })
207
+ .filter((dirent) => dirent.isDirectory())
208
+ .map((dirent) => dirent.name)
209
+ .filter((dir) => {
210
+ // 简单的模式匹配(支持通配符)
211
+ if (workspacePattern.includes('*')) {
212
+ const pattern = workspacePattern.replace(/\*/g, '.*');
213
+ return new RegExp(`^${pattern}$`).test(dir);
214
+ }
215
+ return dir === workspacePattern;
216
+ });
217
+ for (const dir of workspaceDirs) {
218
+ const pkgPath = path.join(dir, 'package.json');
219
+ if (fs.existsSync(pkgPath)) {
220
+ const pkgContent = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
221
+ packages.push({
222
+ name: pkgContent.name,
223
+ path: dir,
224
+ version: pkgContent.version
225
+ });
226
+ }
227
+ }
228
+ }
229
+ }
230
+ catch (error) {
231
+ log.warn(`获取 workspace 包信息失败 - ${error}`);
232
+ }
233
+ return packages;
234
+ }
235
+ /**
236
+ * 格式化版本号函数
237
+ * 根据当前版本号和下一个版本类型或自定义版本号生成新的版本号
238
+ * @param currentVersion 当前版本号
239
+ * @param nextVersion 下一个版本类型或自定义版本号
240
+ * @param preid 预发布标识符(可选)
241
+ * @returns {Promise<string>} 返回格式化后的新版本号
242
+ * @throws Error 如果输入的版本类型/版本号无效,则抛出错误
243
+ */
244
+ export async function formatVersionNumber(currentVersion, // 当前版本号,字符串类型
245
+ nextVersion, // 下一个版本类型或自定义版本号,字符串类型
246
+ preid // 预发布标识符,可选字符串类型
247
+ ) {
248
+ let newVersion; // 声明新版本号变量
249
+ // 检查nextVersion是否是预定义的版本类型之一
250
+ if (VERSION_TYPE.includes(nextVersion)) {
251
+ // 如果是预发布版本则获取预发布标识符
252
+ if (!preid && nextVersion.startsWith('pre')) {
253
+ preid = await getPreid(currentVersion, nextVersion === 'prerelease');
254
+ }
255
+ newVersion = semver.inc(currentVersion, nextVersion, preid);
256
+ if (!newVersion)
257
+ throw new Error(`输入的版本类型 '${nextVersion}' 无法完成版本号更新,请检查当前版本号${currentVersion}是否符合此类型更新要求`);
258
+ }
259
+ else {
260
+ newVersion = nextVersion;
261
+ }
262
+ if (!semver.valid(newVersion)) {
263
+ throw new Error(`输入无效的版本类型/版本号 '${nextVersion}',支持有效的版本类型 (${VERSION_TYPE.join(',')}) 或有效的自定义版本号`);
264
+ }
265
+ if (semver.lte(newVersion, currentVersion)) {
266
+ throw new Error(`输入的版本号 '${newVersion}' 必须大于当前版本号 (${currentVersion})`);
267
+ }
268
+ return newVersion;
269
+ }
270
+ /**
271
+ * 执行命令函数
272
+ * @param cmd - 需要执行的命令字符串
273
+ * @param isDryRun - 是否为试运行模式,默认为false
274
+ * 当isDryRun为true时,仅打印命令而不实际执行
275
+ * 当isDryRun为false时,打印命令并实际执行
276
+ */
277
+ export function runCommand(cmd, isDryRun = false) {
278
+ // 打印要执行的命令信息
279
+ console.log(`${chalk.blue(`${isDryRun ? '模拟' : ''}执行命令`)}: ${cmd}`);
280
+ // 如果不是试运行模式,则实际执行命令
281
+ if (!isDryRun) {
282
+ // 使用execSync执行命令,stdio: 'inherit'表示子进程的stdio与父进程共享
283
+ execSync(cmd, { stdio: 'inherit' });
284
+ }
285
+ }
286
+ /**
287
+ * 回滚代码变更的函数
288
+ * @param tagName - 可选的标签名称,用于删除创建的标签
289
+ * @param isDryRun - 是否为试运行模式,默认为false
290
+ */
291
+ export function rollbackChanges(tagName, isDryRun = false) {
292
+ // 输出警告信息,提示用户正在回滚
293
+ log.warn('发生错误,正在回滚...');
294
+ try {
295
+ // 执行git命令,回退到上一个提交
296
+ runCommand('git reset --hard HEAD~1', isDryRun);
297
+ }
298
+ catch {
299
+ // 捕获并忽略执行过程中的错误
300
+ log.error('回滚失败,请手动处理。');
301
+ }
302
+ // 如果提供了标签名称,则尝试删除该标签
303
+ if (tagName) {
304
+ try {
305
+ runCommand(`git tag -d ${tagName}`, isDryRun);
306
+ }
307
+ catch {
308
+ // 捕获并忽略执行过程中的错误
309
+ log.error('删除标签失败,请手动处理。');
310
+ }
311
+ }
312
+ // 输出错误信息,告知用户已回滚完成
313
+ log.error('已回滚到初始状态。');
314
+ }
315
+ /**
316
+ * 更新指定包的版本号
317
+ * @param pkgPath - 包的文件路径
318
+ * @param newVersion - 新的版本号
319
+ * @param isDryRun - 是否为试运行模式,默认为false
320
+ */
321
+ export function updateVersion(pkgPath, // 包的文件路径
322
+ newVersion, // 新的版本号
323
+ isDryRun = false // 是否为试运行模式,默认为false
324
+ ) {
325
+ // 如果不是试运行模式,则执行实际的版本更新操作
326
+ if (!isDryRun) {
327
+ // 读取并解析包文件
328
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
329
+ // 更新版本号
330
+ pkg.version = newVersion;
331
+ // 将更新后的包内容写回文件,并添加格式化和换行
332
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
333
+ }
334
+ else {
335
+ log.info(`dry-run 模式下跳过写入新版本号${newVersion}`);
336
+ }
337
+ }
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@vitarx/release-cli",
3
+ "version": "1.0.0-alpha.0",
4
+ "description": "A CLI tool for automated npm package release with workspace support",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "author": "zhuchonglin <8210856@qq.com>",
8
+ "license": "MIT",
9
+ "bin": {
10
+ "release-cli": "dist/index.js"
11
+ },
12
+ "devDependencies": {
13
+ "@commitlint/cli": "^19.8.1",
14
+ "@commitlint/config-conventional": "^19.8.1",
15
+ "@commitlint/types": "^19.8.1",
16
+ "@types/node": "^24.3.1",
17
+ "@types/semver": "^7.7.1",
18
+ "husky": "^9.1.7",
19
+ "typescript": "^5.9.2"
20
+ },
21
+ "dependencies": {
22
+ "chalk": "^5.6.2",
23
+ "conventional-changelog": "^7.1.1",
24
+ "conventional-changelog-angular": "^8.0.0",
25
+ "semver": "^7.7.2"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "scripts": {
33
+ "build": "rimraf dist && tsc",
34
+ "test": "node dist/index.js --dry-run"
35
+ }
36
+ }