chandao4 0.1.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/.env.example +10 -0
- package/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/dist/commands/bug.d.ts +3 -0
- package/dist/commands/bug.js +373 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +38 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +83 -0
- package/dist/commands/product.d.ts +3 -0
- package/dist/commands/product.js +41 -0
- package/dist/commands/project.d.ts +3 -0
- package/dist/commands/project.js +70 -0
- package/dist/commands/task.d.ts +3 -0
- package/dist/commands/task.js +445 -0
- package/dist/config/config.d.ts +17 -0
- package/dist/config/config.js +216 -0
- package/dist/config/defaults.d.ts +5 -0
- package/dist/config/defaults.js +23 -0
- package/dist/core/api-client.d.ts +20 -0
- package/dist/core/api-client.js +127 -0
- package/dist/core/auth.d.ts +44 -0
- package/dist/core/auth.js +244 -0
- package/dist/core/errors.d.ts +17 -0
- package/dist/core/errors.js +61 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +125 -0
- package/dist/services/bug.service.d.ts +59 -0
- package/dist/services/bug.service.js +232 -0
- package/dist/services/product.service.d.ts +12 -0
- package/dist/services/product.service.js +43 -0
- package/dist/services/project.service.d.ts +18 -0
- package/dist/services/project.service.js +35 -0
- package/dist/services/task.service.d.ts +55 -0
- package/dist/services/task.service.js +254 -0
- package/dist/types/api.d.ts +31 -0
- package/dist/types/api.js +3 -0
- package/dist/types/config.d.ts +18 -0
- package/dist/types/config.js +3 -0
- package/dist/types/models.d.ts +65 -0
- package/dist/types/models.js +33 -0
- package/dist/utils/format.d.ts +38 -0
- package/dist/utils/format.js +201 -0
- package/dist/utils/prompt.d.ts +12 -0
- package/dist/utils/prompt.js +154 -0
- package/dist/utils/validators.d.ts +14 -0
- package/dist/utils/validators.js +42 -0
- package/package.json +63 -0
package/.env.example
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
所有显著改动按版本记录于此。
|
|
4
|
+
|
|
5
|
+
格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-06-25
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- 查询命令:`bug my/list/show`、`task my/list/show`、`project list/show`、`product list`、`status`
|
|
14
|
+
- 写入命令:`bug create/update/delete`、`task create/update/delete`
|
|
15
|
+
- 登录管理:`login` / `logout`,凭据保存到 `~/.chandao4/config.json`
|
|
16
|
+
- 配置管理:`config show/set`
|
|
17
|
+
- 支持禅道企业版 4.1.3+,通过 Session Cookie 认证,无需管理员后台权限
|
|
18
|
+
- GitHub Actions 自动发布到 npm(基于 Trusted Publishing / OIDC)
|
|
19
|
+
|
|
20
|
+
[Unreleased]: https://github.com/gaoshang212/chandao4/compare/v0.1.0...HEAD
|
|
21
|
+
[0.1.0]: https://github.com/gaoshang212/chandao4/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gaoshang212
|
|
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,184 @@
|
|
|
1
|
+
# 禅道 CLI (chandao4)
|
|
2
|
+
|
|
3
|
+
禅道命令行工具,支持 Bug、任务、项目和产品管理。适用于禅道企业版 4.1.3+。
|
|
4
|
+
|
|
5
|
+
> ⚠️ **当前状态**:查询类命令(`list` / `show` / `my` / `status`)已稳定可用;写入类命令(`create` / `update` / `delete`)仍在完善中,使用时请自行验证结果。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g chandao4
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
或本地开发:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone <repo>
|
|
17
|
+
cd chandao4
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
npm link
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 快速开始
|
|
24
|
+
|
|
25
|
+
### 1. 配置
|
|
26
|
+
|
|
27
|
+
**推荐:交互式登录**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
chandao4 login
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
按提示输入禅道地址、用户名、密码,自动验证并保存。
|
|
34
|
+
|
|
35
|
+
**或手动配置环境变量**
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cp .env.example .env
|
|
39
|
+
# 编辑 .env 填写你的禅道信息
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# .env 文件内容
|
|
44
|
+
ZENTAO_URL=https://your-company.chandao.com
|
|
45
|
+
ZENTAO_USERNAME=your-username
|
|
46
|
+
ZENTAO_PASSWORD=your-password
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. 测试连接
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
chandao4 status
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 命令参考
|
|
56
|
+
|
|
57
|
+
### 登录管理
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# 交互式登录
|
|
61
|
+
chandao4 login
|
|
62
|
+
|
|
63
|
+
# 清除本地凭据
|
|
64
|
+
chandao4 logout
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 项目管理
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# 列出项目
|
|
71
|
+
chandao4 project list
|
|
72
|
+
chandao4 project list --limit 10 --page 1
|
|
73
|
+
|
|
74
|
+
# 查看项目详情
|
|
75
|
+
chandao4 project show 1001
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Bug 管理
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# 获取当前账号的 Bug
|
|
82
|
+
chandao4 bug my
|
|
83
|
+
chandao4 bug my --limit 10 --page 1
|
|
84
|
+
|
|
85
|
+
# 列出 Bug
|
|
86
|
+
chandao4 bug list --product 2001
|
|
87
|
+
chandao4 bug list --product 2001 --status active --limit 10
|
|
88
|
+
|
|
89
|
+
# 查看 Bug 详情
|
|
90
|
+
chandao4 bug show 4001
|
|
91
|
+
|
|
92
|
+
# 创建 Bug
|
|
93
|
+
chandao4 bug create --product 2001
|
|
94
|
+
chandao4 bug create --product 2001 --title "登录页面报错" --severity 2 --priority 2
|
|
95
|
+
|
|
96
|
+
# 更新 Bug
|
|
97
|
+
chandao4 bug update 4001 --status resolved --assigned-to zhangsan
|
|
98
|
+
|
|
99
|
+
# 删除 Bug
|
|
100
|
+
chandao4 bug delete 4001
|
|
101
|
+
chandao4 bug delete 4001 --force
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 任务管理
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# 获取当前账号的任务
|
|
108
|
+
chandao4 task my
|
|
109
|
+
chandao4 task my --limit 10 --page 1
|
|
110
|
+
|
|
111
|
+
# 列出任务
|
|
112
|
+
chandao4 task list -p 1001
|
|
113
|
+
chandao4 task list -p 1001 --status doing --limit 10
|
|
114
|
+
|
|
115
|
+
# 查看任务详情
|
|
116
|
+
chandao4 task show 3001
|
|
117
|
+
|
|
118
|
+
# 创建任务
|
|
119
|
+
chandao4 task create -p 1001
|
|
120
|
+
chandao4 task create -p 1001 --name "实现登录接口" --priority 2 --estimate 4
|
|
121
|
+
|
|
122
|
+
# 更新任务
|
|
123
|
+
chandao4 task update 3001 --status doing --assigned-to lisi
|
|
124
|
+
chandao4 task update 3001 --consumed 3.5 --left 4.5
|
|
125
|
+
|
|
126
|
+
# 删除任务
|
|
127
|
+
chandao4 task delete 3001 -p 1001
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 产品管理
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# 列出产品
|
|
134
|
+
chandao4 product list
|
|
135
|
+
chandao4 product list --limit 10 --page 2
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### JSON 输出
|
|
139
|
+
|
|
140
|
+
所有命令支持 `--json` 选项用于脚本集成:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
chandao4 project list --json | jq '.[] | {id, name}'
|
|
144
|
+
chandao4 task list -p 1001 --json | jq '.[] | {id, name, status}'
|
|
145
|
+
chandao4 bug my --json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 配置管理
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
chandao4 config show
|
|
152
|
+
chandao4 config set server.url https://your-company.chandao.com
|
|
153
|
+
chandao4 config set server.username yourname
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 开发
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npm run dev -- status
|
|
160
|
+
npm run dev -- bug show 4001
|
|
161
|
+
npm run dev -- task list -p 1001
|
|
162
|
+
npm run build
|
|
163
|
+
npm run check
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## 技术栈
|
|
167
|
+
|
|
168
|
+
- TypeScript + Commander.js
|
|
169
|
+
- Axios (HTTP 客户端)
|
|
170
|
+
- cli-table3 + Chalk (输出格式化)
|
|
171
|
+
|
|
172
|
+
## 认证
|
|
173
|
+
|
|
174
|
+
使用用户名/密码登录获取 Session Cookie,无需管理员后台权限。
|
|
175
|
+
|
|
176
|
+
方案:登入 → Session Cookie → 所有后续请求携带 Cookie 认证。
|
|
177
|
+
|
|
178
|
+
## 兼容性
|
|
179
|
+
|
|
180
|
+
| 版本 | 认证方式 | 状态 |
|
|
181
|
+
|------|---------|------|
|
|
182
|
+
| 禅道企业版 4.1.3 | Session Cookie | 测试通过 |
|
|
183
|
+
| 禅道开源版 12.5.3+ | Session Cookie | 理论兼容 |
|
|
184
|
+
| 其他版本 | - | 未测试 |
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Bug 命令
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createBugCommand = createBugCommand;
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const format_1 = require("../utils/format");
|
|
11
|
+
const prompt_1 = require("../utils/prompt");
|
|
12
|
+
const validators_1 = require("../utils/validators");
|
|
13
|
+
const config_1 = require("../config/config");
|
|
14
|
+
function createBugCommand(bugService, getUseJson) {
|
|
15
|
+
const bug = new commander_1.Command('bug')
|
|
16
|
+
.description('Bug 管理:查看、创建、更新、删除 Bug');
|
|
17
|
+
// bug my - 获取当前用户的 Bug
|
|
18
|
+
bug.command('my')
|
|
19
|
+
.description('获取当前账号的 Bug')
|
|
20
|
+
.option('-l, --limit <n>', '显示条数', (v) => parseInt(v, 10), 20)
|
|
21
|
+
.option('--page <n>', '页码', (v) => parseInt(v, 10), 1)
|
|
22
|
+
.action(async (options) => {
|
|
23
|
+
try {
|
|
24
|
+
const { bugs, total } = await bugService.getMyBugs({
|
|
25
|
+
limit: options.limit,
|
|
26
|
+
page: options.page,
|
|
27
|
+
});
|
|
28
|
+
if (bugs.length === 0) {
|
|
29
|
+
console.log(chalk_1.default.gray('没有找到你的 Bug'));
|
|
30
|
+
}
|
|
31
|
+
else if (getUseJson()) {
|
|
32
|
+
console.log((0, format_1.formatJson)(bugs));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.log((0, format_1.formatBugTable)(bugs));
|
|
36
|
+
const pageInfo = options.limit
|
|
37
|
+
? `第 ${options.page || 1} 页,共 ${total} 条`
|
|
38
|
+
: `共 ${total} 条`;
|
|
39
|
+
console.log(chalk_1.default.gray(`\n${pageInfo}`));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
// bug list
|
|
47
|
+
bug.command('list')
|
|
48
|
+
.description('列出 Bug')
|
|
49
|
+
.option('-p, --product <id>', '产品 ID', (v) => parseInt(v, 10))
|
|
50
|
+
.option('-s, --status <status>', '状态过滤 (active|resolved|closed)')
|
|
51
|
+
.option('-l, --limit <n>', '显示条数', (v) => parseInt(v, 10), 20)
|
|
52
|
+
.option('--page <n>', '页码', (v) => parseInt(v, 10), 1)
|
|
53
|
+
.action(async (options) => {
|
|
54
|
+
if (!options.product) {
|
|
55
|
+
console.error(chalk_1.default.red('错误: 需要指定 --product <id>'));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
if (options.status && !(0, validators_1.isValidBugStatus)(options.status)) {
|
|
59
|
+
console.error(chalk_1.default.red(`错误: 无效的状态 "${options.status}",有效值: active, resolved, closed`));
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const { bugs, total } = await bugService.getList(options.product, {
|
|
64
|
+
status: options.status,
|
|
65
|
+
limit: options.limit,
|
|
66
|
+
page: options.page,
|
|
67
|
+
});
|
|
68
|
+
if (bugs.length === 0) {
|
|
69
|
+
console.log(chalk_1.default.gray('没有找到 Bug'));
|
|
70
|
+
}
|
|
71
|
+
else if (getUseJson()) {
|
|
72
|
+
console.log((0, format_1.formatJson)(bugs));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.log((0, format_1.formatBugTable)(bugs));
|
|
76
|
+
const pageInfo = options.limit
|
|
77
|
+
? `第 ${options.page || 1} 页,共 ${total} 条`
|
|
78
|
+
: `共 ${total} 条`;
|
|
79
|
+
console.log(chalk_1.default.gray(`\n${pageInfo}`));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// bug show
|
|
87
|
+
bug.command('show <id>')
|
|
88
|
+
.description('查看 Bug 详情')
|
|
89
|
+
.action(async (id) => {
|
|
90
|
+
const bugId = parseInt(id, 10);
|
|
91
|
+
if (!(0, validators_1.isValidId)(bugId)) {
|
|
92
|
+
console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const bug = await bugService.getDetail(bugId);
|
|
97
|
+
if (!bug) {
|
|
98
|
+
console.error(chalk_1.default.red(`错误: Bug #${bugId} 未找到`));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
if (getUseJson()) {
|
|
102
|
+
console.log((0, format_1.formatJson)(bug));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
console.log((0, format_1.formatBugDetail)(bug));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// bug create
|
|
113
|
+
bug.command('create')
|
|
114
|
+
.description('创建 Bug(交互式)')
|
|
115
|
+
.requiredOption('-p, --product <id>', '产品 ID', (v) => parseInt(v, 10))
|
|
116
|
+
.option('-t, --title <title>', 'Bug 标题')
|
|
117
|
+
.option('-s, --severity <n>', '严重程度 (1-4)', (v) => parseInt(v, 10))
|
|
118
|
+
.option('-P, --priority <n>', '优先级 (1-4)', (v) => parseInt(v, 10))
|
|
119
|
+
.option('--type <type>', '类型', 'code')
|
|
120
|
+
.option('-a, --assigned-to <user>', '指派给(默认当前登录账号)')
|
|
121
|
+
.option('--opened-build <build>', '发现版本 (build ID,多个用逗号分隔)')
|
|
122
|
+
.option('--deadline <date>', '截止日期 (YYYY-MM-DD)')
|
|
123
|
+
.option('--steps <text>', '重现步骤')
|
|
124
|
+
.action(async (options) => {
|
|
125
|
+
try {
|
|
126
|
+
let title = options.title;
|
|
127
|
+
let severity = options.severity;
|
|
128
|
+
let priority = options.priority;
|
|
129
|
+
let assignedTo = options.assignedTo;
|
|
130
|
+
let openedBuild = options.openedBuild;
|
|
131
|
+
let deadline = options.deadline;
|
|
132
|
+
const rawCfg = (0, config_1.loadRawConfig)();
|
|
133
|
+
const today = new Date().toISOString().split('T')[0];
|
|
134
|
+
if (!title) {
|
|
135
|
+
title = await (0, prompt_1.prompt)('Bug 标题');
|
|
136
|
+
if (!title) {
|
|
137
|
+
console.error(chalk_1.default.red('错误: Bug 标题不能为空'));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!severity) {
|
|
142
|
+
const input = await (0, prompt_1.prompt)('严重程度 (1=致命, 2=严重, 3=一般, 4=建议)', '3');
|
|
143
|
+
severity = parseInt(input, 10);
|
|
144
|
+
}
|
|
145
|
+
if (!(0, validators_1.isValidSeverity)(severity)) {
|
|
146
|
+
console.error(chalk_1.default.red(`错误: 无效的严重程度 "${severity}",有效值: 1-4`));
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
if (!priority) {
|
|
150
|
+
const input = await (0, prompt_1.prompt)('优先级 (1=紧急, 2=高, 3=中, 4=低)', '3');
|
|
151
|
+
priority = parseInt(input, 10);
|
|
152
|
+
}
|
|
153
|
+
if (!(0, validators_1.isValidPriority)(priority)) {
|
|
154
|
+
console.error(chalk_1.default.red(`错误: 无效的优先级 "${priority}",有效值: 1-4`));
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
const defaultUser = rawCfg.server.username || '';
|
|
158
|
+
assignedTo = assignedTo || await (0, prompt_1.prompt)('指派给', defaultUser) || defaultUser;
|
|
159
|
+
openedBuild = openedBuild || await (0, prompt_1.prompt)('发现版本 (build ID,默认 trunk)', 'trunk') || 'trunk';
|
|
160
|
+
deadline = deadline || await (0, prompt_1.prompt)('截止日期 (YYYY-MM-DD)', today) || today;
|
|
161
|
+
const bug = await bugService.create(options.product, {
|
|
162
|
+
title,
|
|
163
|
+
severity,
|
|
164
|
+
priority,
|
|
165
|
+
type: options.type,
|
|
166
|
+
assignedTo,
|
|
167
|
+
openedBuild,
|
|
168
|
+
deadline,
|
|
169
|
+
steps: options.steps,
|
|
170
|
+
});
|
|
171
|
+
if (bug) {
|
|
172
|
+
console.log(chalk_1.default.green(`✓ Bug #${bug.id} 创建成功: ${bug.title}`));
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
console.log(chalk_1.default.green('✓ Bug 创建成功'));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
// bug update
|
|
183
|
+
bug.command('update <id>')
|
|
184
|
+
.description('更新 Bug')
|
|
185
|
+
.option('-t, --title <title>', 'Bug 标题')
|
|
186
|
+
.option('-s, --status <status>', '状态 (active|resolved|closed)')
|
|
187
|
+
.option('--severity <n>', '严重程度 (1-4)', (v) => parseInt(v, 10))
|
|
188
|
+
.option('--priority <n>', '优先级 (1-4)', (v) => parseInt(v, 10))
|
|
189
|
+
.option('-a, --assigned-to <user>', '指派给')
|
|
190
|
+
.option('--deadline <date>', '截止日期 (YYYY-MM-DD)')
|
|
191
|
+
.option('--steps <text>', '重现步骤')
|
|
192
|
+
.action(async (id, options) => {
|
|
193
|
+
const bugId = parseInt(id, 10);
|
|
194
|
+
if (!(0, validators_1.isValidId)(bugId)) {
|
|
195
|
+
console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
if (options.status && !(0, validators_1.isValidBugStatus)(options.status)) {
|
|
199
|
+
console.error(chalk_1.default.red(`错误: 无效的状态 "${options.status}"`));
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
const updates = {};
|
|
203
|
+
if (options.title)
|
|
204
|
+
updates.title = options.title;
|
|
205
|
+
if (options.status)
|
|
206
|
+
updates.status = options.status;
|
|
207
|
+
if (options.severity)
|
|
208
|
+
updates.severity = options.severity;
|
|
209
|
+
if (options.priority)
|
|
210
|
+
updates.priority = options.priority;
|
|
211
|
+
if (options.assignedTo)
|
|
212
|
+
updates.assignedTo = options.assignedTo;
|
|
213
|
+
if (options.deadline)
|
|
214
|
+
updates.deadline = options.deadline;
|
|
215
|
+
if (options.steps)
|
|
216
|
+
updates.steps = options.steps;
|
|
217
|
+
if (Object.keys(updates).length === 0) {
|
|
218
|
+
console.error(chalk_1.default.yellow('没有指定要更新的字段'));
|
|
219
|
+
process.exit(0);
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const bug = await bugService.update(bugId, updates);
|
|
223
|
+
if (bug) {
|
|
224
|
+
console.log(chalk_1.default.green(`✓ Bug #${bug.id} 更新成功`));
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
console.log(chalk_1.default.green('✓ Bug 更新成功'));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
// bug resolve
|
|
235
|
+
bug.command('resolve <id>')
|
|
236
|
+
.description('解决 Bug(active → resolved)')
|
|
237
|
+
.option('-r, --resolution <type>', '解决方式: fixed|bydesign|duplicate|external|notrepro|postponed', 'fixed')
|
|
238
|
+
.option('--build <build>', '解决版本', 'trunk')
|
|
239
|
+
.option('-a, --assigned-to <user>', '指派给(默认回指给创建者)')
|
|
240
|
+
.option('-c, --comment <text>', '备注')
|
|
241
|
+
.option('-f, --force', '跳过确认')
|
|
242
|
+
.action(async (id, options) => {
|
|
243
|
+
const bugId = parseInt(id, 10);
|
|
244
|
+
if (!(0, validators_1.isValidId)(bugId)) {
|
|
245
|
+
console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
const validResolutions = ['fixed', 'bydesign', 'duplicate', 'external', 'notrepro', 'postponed'];
|
|
249
|
+
if (!validResolutions.includes(options.resolution)) {
|
|
250
|
+
console.error(chalk_1.default.red(`错误: 无效的解决方式 "${options.resolution}",有效值: ${validResolutions.join(', ')}`));
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
if (!options.force) {
|
|
255
|
+
const confirmed = await (0, prompt_1.confirm)(`确定要解决 Bug #${bugId} 吗?`);
|
|
256
|
+
if (!confirmed) {
|
|
257
|
+
console.log(chalk_1.default.gray('已取消'));
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const bug = await bugService.resolve(bugId, {
|
|
262
|
+
resolution: options.resolution,
|
|
263
|
+
resolvedBuild: options.build,
|
|
264
|
+
assignedTo: options.assignedTo,
|
|
265
|
+
comment: options.comment,
|
|
266
|
+
});
|
|
267
|
+
if (bug) {
|
|
268
|
+
console.log(chalk_1.default.green(`✓ Bug #${bug.id} 已解决(${options.resolution})`));
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
console.error(chalk_1.default.red('解决 Bug 失败'));
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
// bug close
|
|
280
|
+
bug.command('close <id>')
|
|
281
|
+
.description('关闭 Bug(resolved → closed)')
|
|
282
|
+
.option('-c, --comment <text>', '备注')
|
|
283
|
+
.option('-f, --force', '跳过确认')
|
|
284
|
+
.action(async (id, options) => {
|
|
285
|
+
const bugId = parseInt(id, 10);
|
|
286
|
+
if (!(0, validators_1.isValidId)(bugId)) {
|
|
287
|
+
console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
if (!options.force) {
|
|
292
|
+
const confirmed = await (0, prompt_1.confirm)(`确定要关闭 Bug #${bugId} 吗?`);
|
|
293
|
+
if (!confirmed) {
|
|
294
|
+
console.log(chalk_1.default.gray('已取消'));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const bug = await bugService.close(bugId, options.comment);
|
|
299
|
+
if (bug) {
|
|
300
|
+
console.log(chalk_1.default.green(`✓ Bug #${bug.id} 已关闭`));
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
console.error(chalk_1.default.red('关闭 Bug 失败'));
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
// bug activate
|
|
312
|
+
bug.command('activate <id>')
|
|
313
|
+
.description('激活 Bug(resolved/closed → active)')
|
|
314
|
+
.option('-a, --assigned-to <user>', '指派给')
|
|
315
|
+
.option('-c, --comment <text>', '备注')
|
|
316
|
+
.option('-f, --force', '跳过确认')
|
|
317
|
+
.action(async (id, options) => {
|
|
318
|
+
const bugId = parseInt(id, 10);
|
|
319
|
+
if (!(0, validators_1.isValidId)(bugId)) {
|
|
320
|
+
console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
try {
|
|
324
|
+
if (!options.force) {
|
|
325
|
+
const confirmed = await (0, prompt_1.confirm)(`确定要激活 Bug #${bugId} 吗?`);
|
|
326
|
+
if (!confirmed) {
|
|
327
|
+
console.log(chalk_1.default.gray('已取消'));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const bug = await bugService.activate(bugId, {
|
|
332
|
+
assignedTo: options.assignedTo,
|
|
333
|
+
comment: options.comment,
|
|
334
|
+
});
|
|
335
|
+
if (bug) {
|
|
336
|
+
console.log(chalk_1.default.green(`✓ Bug #${bug.id} 已激活(状态: ${bug.status})`));
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
console.error(chalk_1.default.red('激活 Bug 失败'));
|
|
340
|
+
process.exit(1);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
// bug delete
|
|
348
|
+
bug.command('delete <id>')
|
|
349
|
+
.description('删除 Bug')
|
|
350
|
+
.option('-f, --force', '跳过确认')
|
|
351
|
+
.action(async (id, options) => {
|
|
352
|
+
const bugId = parseInt(id, 10);
|
|
353
|
+
if (!(0, validators_1.isValidId)(bugId)) {
|
|
354
|
+
console.error(chalk_1.default.red(`错误: 无效的 Bug ID "${id}"`));
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
if (!options.force) {
|
|
359
|
+
const confirmed = await (0, prompt_1.confirm)(`确定要删除 Bug #${bugId} 吗?`);
|
|
360
|
+
if (!confirmed) {
|
|
361
|
+
console.log(chalk_1.default.gray('已取消'));
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
await bugService.delete(bugId);
|
|
366
|
+
console.log(chalk_1.default.green(`✓ Bug #${bugId} 已删除`));
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
return bug;
|
|
373
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// 配置管理命令
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createConfigCommand = createConfigCommand;
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const config_1 = require("../config/config");
|
|
11
|
+
function createConfigCommand() {
|
|
12
|
+
const config = new commander_1.Command('config')
|
|
13
|
+
.description('配置管理');
|
|
14
|
+
config.command('show')
|
|
15
|
+
.description('显示当前配置')
|
|
16
|
+
.action(() => {
|
|
17
|
+
const cfg = (0, config_1.loadConfig)();
|
|
18
|
+
console.log(chalk_1.default.bold('\n当前配置:\n'));
|
|
19
|
+
console.log(` 服务器: ${cfg.server.url || chalk_1.default.gray('(未设置)')}`);
|
|
20
|
+
if (cfg.server.username) {
|
|
21
|
+
console.log(` 用户名: ${cfg.server.username}`);
|
|
22
|
+
console.log(` 密码: ${chalk_1.default.gray('******')}`);
|
|
23
|
+
}
|
|
24
|
+
console.log(` 认证模式: ${cfg.authMode === 'apikey' ? 'API Key' : 'Session (用户名/密码)'}`);
|
|
25
|
+
console.log('');
|
|
26
|
+
});
|
|
27
|
+
config.command('set <key> <value>')
|
|
28
|
+
.description('设置配置项(保存到用户目录)')
|
|
29
|
+
.action(async (key, value) => {
|
|
30
|
+
const validKeys = ['server.url', 'server.username', 'server.password', 'server.code', 'server.token'];
|
|
31
|
+
if (!validKeys.includes(key)) {
|
|
32
|
+
console.error(chalk_1.default.red(`错误: 无效的配置项 "${key}",有效值: ${validKeys.join(', ')}`));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
(0, config_1.saveUserConfig)(key, value);
|
|
36
|
+
});
|
|
37
|
+
return config;
|
|
38
|
+
}
|