@hecom/codearts 0.2.1 → 0.2.2
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 +17 -5
- package/dist/bin/cli.js +25 -18
- package/dist/commands/bug.command.d.ts +0 -2
- package/dist/commands/bug.command.js +104 -60
- package/dist/commands/config.command.js +33 -32
- package/dist/commands/daily.command.d.ts +0 -5
- package/dist/commands/daily.command.js +265 -99
- package/dist/commands/work-hour.command.js +189 -131
- package/dist/types/index.d.ts +4 -0
- package/dist/utils/config-loader.d.ts +9 -6
- package/dist/utils/config-loader.js +19 -14
- package/dist/utils/csv-writer.d.ts +12 -0
- package/dist/utils/csv-writer.js +60 -0
- package/dist/utils/logger.d.ts +62 -0
- package/dist/utils/logger.js +98 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,14 +24,24 @@ npx @hecom/codearts daily
|
|
|
24
24
|
# 生成当年工时统计
|
|
25
25
|
npx @hecom/codearts work-hour
|
|
26
26
|
|
|
27
|
+
# 按迭代统计产品缺陷率
|
|
28
|
+
npx @hecom/codearts bug-rate "迭代1,迭代2"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 3. 更新配置
|
|
32
|
+
|
|
33
|
+
```bash
|
|
27
34
|
# 更新全局配置
|
|
28
35
|
npx @hecom/codearts config
|
|
29
36
|
|
|
30
37
|
# 单独更新角色配置
|
|
31
38
|
npx @hecom/codearts config role-id
|
|
39
|
+
|
|
40
|
+
# 查看当前配置
|
|
41
|
+
npx @hecom/codearts config show
|
|
32
42
|
```
|
|
33
43
|
|
|
34
|
-
###
|
|
44
|
+
### 4. 升级
|
|
35
45
|
|
|
36
46
|
```bash
|
|
37
47
|
# 更新最新版本
|
|
@@ -56,13 +66,15 @@ npm install
|
|
|
56
66
|
|
|
57
67
|
### 本地运行
|
|
58
68
|
|
|
69
|
+
> 本地运行命令时,注意使用 `--` 分隔 npm 参数和 CLI 参数,否则 CLI 参数可能无法正确传递。
|
|
70
|
+
|
|
59
71
|
```bash
|
|
60
72
|
# 运行命令
|
|
61
73
|
npm run dev
|
|
62
74
|
|
|
63
|
-
npm run dev daily
|
|
75
|
+
npm run dev -- daily
|
|
64
76
|
|
|
65
|
-
npm run dev work-hour
|
|
77
|
+
npm run dev -- work-hour
|
|
66
78
|
```
|
|
67
79
|
|
|
68
80
|
### 本地链接 CLI 工具
|
|
@@ -79,10 +91,10 @@ codearts --help
|
|
|
79
91
|
| --------------------------- | ---------------------------------- | ---- |
|
|
80
92
|
| `HUAWEI_CLOUD_IAM_ENDPOINT` | IAM 认证端点 | 是 |
|
|
81
93
|
| `HUAWEI_CLOUD_REGION` | 华为云区域 | 是 |
|
|
94
|
+
| `CODEARTS_BASE_URL` | CodeArts API 地址 | 是 |
|
|
95
|
+
| `HUAWEI_CLOUD_DOMAIN` | 华为云账号名 | 是 |
|
|
82
96
|
| `HUAWEI_CLOUD_USERNAME` | IAM 用户名 | 是 |
|
|
83
97
|
| `HUAWEI_CLOUD_PASSWORD` | IAM 密码 | 是 |
|
|
84
|
-
| `HUAWEI_CLOUD_DOMAIN` | 华为云账号名 | 是 |
|
|
85
|
-
| `CODEARTS_BASE_URL` | CodeArts API 地址 | 是 |
|
|
86
98
|
| `PROJECT_ID` | 项目 ID | 是 |
|
|
87
99
|
| `ROLE_ID` | 角色 ID(支持逗号分隔,如: 1,2,3) | 是 |
|
|
88
100
|
|
package/dist/bin/cli.js
CHANGED
|
@@ -42,6 +42,7 @@ const config_command_1 = require("../commands/config.command");
|
|
|
42
42
|
const daily_command_1 = require("../commands/daily.command");
|
|
43
43
|
const work_hour_command_1 = require("../commands/work-hour.command");
|
|
44
44
|
const config_loader_1 = require("../utils/config-loader");
|
|
45
|
+
const logger_1 = require("../utils/logger");
|
|
45
46
|
// 读取 package.json 中的版本号
|
|
46
47
|
const packageJsonPath = path.join(__dirname, '../../package.json');
|
|
47
48
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
@@ -49,11 +50,13 @@ const version = packageJson.version;
|
|
|
49
50
|
const program = new commander_1.Command();
|
|
50
51
|
program.name('codearts').description('华为云 CodeArts 统计分析工具').version(version);
|
|
51
52
|
// 全局选项(环境变量覆盖)
|
|
52
|
-
program
|
|
53
|
+
program
|
|
54
|
+
.option('--role-id <ids>', '角色 ID(支持逗号分隔,如: 1,2)')
|
|
55
|
+
.option('--output <format>', '输出格式:console、csv、json', 'console');
|
|
53
56
|
// config 命令 - 交互式配置向导
|
|
54
57
|
const configCmd = program
|
|
55
58
|
.command('config')
|
|
56
|
-
.description('
|
|
59
|
+
.description('交互式配置向导,引导用户创建或更新全局配置文件')
|
|
57
60
|
.action(async () => {
|
|
58
61
|
await (0, config_command_1.configCommand)();
|
|
59
62
|
});
|
|
@@ -78,26 +81,30 @@ availableConfigs.forEach((configItem) => {
|
|
|
78
81
|
// daily 命令
|
|
79
82
|
program
|
|
80
83
|
.command('daily [date]')
|
|
81
|
-
.description('
|
|
82
|
-
.
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
.description('每日工时统计(默认日期为当天)')
|
|
85
|
+
.option('-r, --report', '显示总结报告', false)
|
|
86
|
+
.action(async (date, options, command) => {
|
|
87
|
+
const cliOptions = { ...command.parent.opts(), report: options.report };
|
|
88
|
+
logger_1.logger.setOutputFormat(cliOptions.output);
|
|
89
|
+
await (0, daily_command_1.dailyCommand)(date, cliOptions);
|
|
85
90
|
});
|
|
86
91
|
// work-hour 命令
|
|
87
92
|
program
|
|
88
93
|
.command('work-hour [year]')
|
|
89
|
-
.description('
|
|
90
|
-
.action(async (year) => {
|
|
91
|
-
const
|
|
92
|
-
|
|
94
|
+
.description('年度工时统计(默认当前年份)')
|
|
95
|
+
.action(async (year, options, command) => {
|
|
96
|
+
const cliOptions = command.parent.opts();
|
|
97
|
+
logger_1.logger.setOutputFormat(cliOptions.output);
|
|
98
|
+
await (0, work_hour_command_1.workHourCommand)(year, cliOptions);
|
|
93
99
|
});
|
|
94
100
|
// bug-rate 命令
|
|
95
101
|
program
|
|
96
102
|
.command('bug-rate <iterations>')
|
|
97
|
-
.description('
|
|
98
|
-
.action(async (iterations) => {
|
|
99
|
-
const
|
|
100
|
-
|
|
103
|
+
.description('产品缺陷率统计,支持多个迭代')
|
|
104
|
+
.action(async (iterations, options, command) => {
|
|
105
|
+
const cliOptions = command.parent.opts();
|
|
106
|
+
logger_1.logger.setOutputFormat(cliOptions.output);
|
|
107
|
+
await (0, bug_command_1.bugCommand)(iterations, cliOptions);
|
|
101
108
|
});
|
|
102
109
|
// 检查配置并自动执行 config 命令
|
|
103
110
|
async function checkConfigAndRun() {
|
|
@@ -105,10 +112,10 @@ async function checkConfigAndRun() {
|
|
|
105
112
|
// 如果没有参数(直接执行 codearts),检测配置
|
|
106
113
|
if (args.length === 0) {
|
|
107
114
|
// 检查是否有全局配置
|
|
108
|
-
const
|
|
109
|
-
if (!
|
|
115
|
+
const hasConfig = (0, config_loader_1.configExists)();
|
|
116
|
+
if (!hasConfig) {
|
|
110
117
|
// 没有配置,自动执行 config 命令
|
|
111
|
-
|
|
118
|
+
logger_1.logger.info('未检测到配置文件,启动配置向导...\n');
|
|
112
119
|
await (0, config_command_1.configCommand)();
|
|
113
120
|
return;
|
|
114
121
|
}
|
|
@@ -119,6 +126,6 @@ async function checkConfigAndRun() {
|
|
|
119
126
|
program.parse();
|
|
120
127
|
}
|
|
121
128
|
checkConfigAndRun().catch((error) => {
|
|
122
|
-
|
|
129
|
+
logger_1.logger.error('执行失败: ', error);
|
|
123
130
|
process.exit(1);
|
|
124
131
|
});
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { CliOptions } from '../utils/config-loader';
|
|
2
2
|
/**
|
|
3
3
|
* bug 命令入口
|
|
4
|
-
* @param iterationTitlesStr 迭代标题(支持逗号、分号、空格、竖线、顿号等分隔符)
|
|
5
|
-
* @param cliOptions CLI 选项
|
|
6
4
|
*/
|
|
7
5
|
export declare function bugCommand(iterationTitlesStr: string, cliOptions?: CliOptions): Promise<void>;
|
|
@@ -4,13 +4,12 @@ exports.bugCommand = bugCommand;
|
|
|
4
4
|
const business_service_1 = require("../services/business.service");
|
|
5
5
|
const types_1 = require("../types");
|
|
6
6
|
const config_loader_1 = require("../utils/config-loader");
|
|
7
|
+
const csv_writer_1 = require("../utils/csv-writer");
|
|
8
|
+
const logger_1 = require("../utils/logger");
|
|
7
9
|
/**
|
|
8
10
|
* 判断 Bug 是否属于需求变更或产品设计问题
|
|
9
|
-
* @param bug Bug 工作项
|
|
10
|
-
* @returns 是否属于需求变更或产品设计问题
|
|
11
11
|
*/
|
|
12
12
|
function isRequirementOrDesignBug(bug) {
|
|
13
|
-
// 检查 customValueNew(v2 版本的自定义字段,是对象格式)
|
|
14
13
|
const defectAnalysisValue = bug.customValueNew?.custom_field32;
|
|
15
14
|
if (defectAnalysisValue) {
|
|
16
15
|
return (defectAnalysisValue === types_1.DefectAnalysisType.REQUIREMENT_CHANGE ||
|
|
@@ -19,32 +18,35 @@ function isRequirementOrDesignBug(bug) {
|
|
|
19
18
|
return false;
|
|
20
19
|
}
|
|
21
20
|
/**
|
|
22
|
-
*
|
|
21
|
+
* 查询 Bug 统计数据
|
|
23
22
|
*/
|
|
24
|
-
async function
|
|
25
|
-
|
|
26
|
-
console.log(`目标迭代: ${iterationTitles.join(', ')}`);
|
|
27
|
-
console.log(`角色过滤: ${roleIds.length > 0 ? roleIds.join(', ') : '无(保留所有人)'}`);
|
|
28
|
-
console.log(`统计规则: 统计所有 Bug,同时标记"需求变更问题"和"产品设计问题"`);
|
|
29
|
-
// Step 1: 获取指定角色的成员列表(如果启用了角色过滤)
|
|
23
|
+
async function queryBugReportData(businessService, projectId, roleIds, iterationTitles) {
|
|
24
|
+
const roleNames = new Set();
|
|
30
25
|
const targetMemberIds = new Set();
|
|
31
26
|
if (roleIds.length > 0) {
|
|
32
27
|
for (const roleId of roleIds) {
|
|
33
28
|
const members = await businessService.getMembersByRoleId(projectId, roleId);
|
|
34
|
-
members.forEach((member) =>
|
|
29
|
+
members.forEach((member) => {
|
|
30
|
+
targetMemberIds.add(member.user_num_id);
|
|
31
|
+
roleNames.add(member.role_name);
|
|
32
|
+
});
|
|
35
33
|
}
|
|
36
|
-
console.log(`\n目标角色成员数: ${targetMemberIds.size} 人`);
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
console.log(`\n角色过滤: 已禁用,将统计所有人`);
|
|
40
34
|
}
|
|
41
|
-
// Step 2: 获取所有 Story
|
|
42
35
|
const stories = await businessService.getStoriesByIterationTitles(projectId, iterationTitles);
|
|
43
36
|
if (stories.length === 0) {
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
return {
|
|
38
|
+
iterations: iterationTitles,
|
|
39
|
+
roleIds,
|
|
40
|
+
roleNames: Array.from(roleNames),
|
|
41
|
+
userStats: [],
|
|
42
|
+
summary: {
|
|
43
|
+
totalBugs: 0,
|
|
44
|
+
totalProductBugs: 0,
|
|
45
|
+
overallProductDefectRate: 0,
|
|
46
|
+
userCount: 0,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
46
49
|
}
|
|
47
|
-
// Step 3: 过滤出目标角色成员处理的 Story(如果启用了角色过滤)
|
|
48
50
|
const filteredStories = roleIds.length > 0
|
|
49
51
|
? stories.filter((story) => {
|
|
50
52
|
const assignedUserId = story.assigned_user?.id;
|
|
@@ -52,27 +54,27 @@ async function generateBugReport(businessService, projectId, roleIds, iterationT
|
|
|
52
54
|
})
|
|
53
55
|
: stories;
|
|
54
56
|
if (filteredStories.length === 0) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
return {
|
|
58
|
+
iterations: iterationTitles,
|
|
59
|
+
roleIds,
|
|
60
|
+
roleNames: Array.from(roleNames),
|
|
61
|
+
userStats: [],
|
|
62
|
+
summary: {
|
|
63
|
+
totalBugs: 0,
|
|
64
|
+
totalProductBugs: 0,
|
|
65
|
+
overallProductDefectRate: 0,
|
|
66
|
+
userCount: 0,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
60
69
|
}
|
|
61
|
-
else {
|
|
62
|
-
console.log(`\n找到 ${filteredStories.length} 个 Story,正在统计 Bug...`);
|
|
63
|
-
}
|
|
64
|
-
// Step 4: 获取每个 Story 的子工作项(Bug)
|
|
65
70
|
const bugStatsMap = new Map();
|
|
66
71
|
for (const story of filteredStories) {
|
|
67
72
|
const childIssues = await businessService.getChildIssues(projectId, String(story.id));
|
|
68
|
-
// 过滤出所有 Bug 类型的子工作项(tracker_id = 3)
|
|
69
73
|
const allBugs = childIssues.filter((issue) => issue.tracker.id === 3);
|
|
70
74
|
if (allBugs.length === 0) {
|
|
71
75
|
continue;
|
|
72
76
|
}
|
|
73
|
-
// 统计产品问题 Bug(需求变更 + 产品设计)
|
|
74
77
|
const productBugs = allBugs.filter((bug) => isRequirementOrDesignBug(bug));
|
|
75
|
-
// Step 3: 按 Story 的处理人统计 Bug
|
|
76
78
|
const assignedUserId = story.assigned_user?.id;
|
|
77
79
|
const assignedUserName = story.assigned_user?.nick_name;
|
|
78
80
|
if (!assignedUserId) {
|
|
@@ -98,61 +100,103 @@ async function generateBugReport(businessService, projectId, roleIds, iterationT
|
|
|
98
100
|
productBugCount: productBugs.length,
|
|
99
101
|
});
|
|
100
102
|
}
|
|
101
|
-
// 计算每个人的产品缺陷率
|
|
102
103
|
bugStatsMap.forEach((stats) => {
|
|
103
104
|
stats.productDefectRate =
|
|
104
|
-
stats.totalBugCount > 0
|
|
105
|
+
stats.totalBugCount > 0
|
|
106
|
+
? Math.round((stats.productBugCount / stats.totalBugCount) * 100 * 100) / 100
|
|
107
|
+
: 0;
|
|
105
108
|
});
|
|
106
|
-
// Step 4: 输出统计结果
|
|
107
|
-
console.log('\n');
|
|
108
|
-
console.log('='.repeat(80));
|
|
109
|
-
console.log(`Bug 统计报告 [${iterationTitles.join(', ')}]`);
|
|
110
|
-
console.log('='.repeat(80));
|
|
111
|
-
console.log('');
|
|
112
|
-
if (bugStatsMap.size === 0) {
|
|
113
|
-
console.log('未找到任何 Bug');
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
// 按总 Bug 数量降序排列
|
|
117
109
|
const sortedStats = Array.from(bugStatsMap.values()).sort((a, b) => b.totalBugCount - a.totalBugCount);
|
|
118
110
|
let totalBugs = 0;
|
|
119
111
|
let totalProductBugs = 0;
|
|
120
112
|
sortedStats.forEach((stats) => {
|
|
121
|
-
console.log(`\x1b[31m${stats.assignedUser}: 总 Bug ${stats.totalBugCount} 个 | 产品问题 ${stats.productBugCount} 个 | 产品缺陷率 ${stats.productDefectRate.toFixed(1)}%\x1b[0m`);
|
|
122
|
-
// 输出该用户的 Story 及其 Bug 数量
|
|
123
|
-
stats.stories.forEach((story) => {
|
|
124
|
-
console.log(` - ${story.storyName} (总 ${story.totalBugCount} 个 Bug,其中产品问题 ${story.productBugCount} 个)`);
|
|
125
|
-
});
|
|
126
113
|
totalBugs += stats.totalBugCount;
|
|
127
114
|
totalProductBugs += stats.productBugCount;
|
|
128
|
-
console.log('');
|
|
129
115
|
});
|
|
130
|
-
const overallProductDefectRate = totalBugs > 0 ? (totalProductBugs / totalBugs) * 100 : 0;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
116
|
+
const overallProductDefectRate = totalBugs > 0 ? Math.round((totalProductBugs / totalBugs) * 100 * 100) / 100 : 0;
|
|
117
|
+
return {
|
|
118
|
+
iterations: iterationTitles,
|
|
119
|
+
roleIds,
|
|
120
|
+
roleNames: Array.from(roleNames),
|
|
121
|
+
userStats: sortedStats,
|
|
122
|
+
summary: {
|
|
123
|
+
totalBugs,
|
|
124
|
+
totalProductBugs,
|
|
125
|
+
overallProductDefectRate,
|
|
126
|
+
userCount: sortedStats.length,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 控制台输出产品缺陷率统计
|
|
132
|
+
*/
|
|
133
|
+
function outputConsole(data) {
|
|
134
|
+
logger_1.logger.info(`产品缺陷率统计 [${data.roleNames.join(', ')}]`);
|
|
135
|
+
logger_1.logger.info('='.repeat(80));
|
|
136
|
+
logger_1.logger.info(`产品缺陷率: ${data.summary.overallProductDefectRate.toFixed(2)}% `);
|
|
137
|
+
logger_1.logger.info(`总 Bug 数: ${data.summary.totalBugs}个`);
|
|
138
|
+
logger_1.logger.info(`产品问题: ${data.summary.totalProductBugs}个`);
|
|
139
|
+
logger_1.logger.info(`统计人数: ${data.summary.userCount}人`);
|
|
140
|
+
logger_1.logger.info(`统计迭代: ${data.iterations.join(', ')}`);
|
|
141
|
+
logger_1.logger.info('='.repeat(80));
|
|
142
|
+
data.userStats.forEach((stats) => {
|
|
143
|
+
logger_1.logger.info(`\x1b[31m${stats.assignedUser}: ${stats.productDefectRate.toFixed(2)}% (${stats.productBugCount}/${stats.totalBugCount})\x1b[0m`);
|
|
144
|
+
stats.stories.forEach((story) => {
|
|
145
|
+
logger_1.logger.info(` ${story.storyName} (${story.productBugCount}/${story.totalBugCount})`);
|
|
146
|
+
});
|
|
147
|
+
logger_1.logger.info('');
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* CSV 文件输出
|
|
152
|
+
*/
|
|
153
|
+
function outputCsv(data, iterationTitles) {
|
|
154
|
+
const csvLines = [];
|
|
155
|
+
csvLines.push('迭代,处理人,Story名称,StoryID,总Bug数,产品问题Bug数,产品缺陷率');
|
|
156
|
+
data.userStats.forEach((userStat) => {
|
|
157
|
+
userStat.stories.forEach((story) => {
|
|
158
|
+
const defectRate = story.totalBugCount > 0
|
|
159
|
+
? ((story.productBugCount / story.totalBugCount) * 100).toFixed(2)
|
|
160
|
+
: '0.00';
|
|
161
|
+
csvLines.push(`"${(0, csv_writer_1.escapeCsv)(data.iterations.join(', '))}",${userStat.assignedUser ?? ''},"${(0, csv_writer_1.escapeCsv)(story.storyName ?? '')}",${story.storyId ?? ''},${story.totalBugCount ?? ''},${story.productBugCount ?? ''},${defectRate}%`);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
const filename = `bug-rate-${iterationTitles.join('-')}.csv`;
|
|
165
|
+
(0, csv_writer_1.writeCsvFile)(filename, csvLines, logger_1.logger);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* JSON 输出(直接打印到控制台,供编程调用)
|
|
169
|
+
*/
|
|
170
|
+
function outputJson(data) {
|
|
171
|
+
logger_1.logger.json(data);
|
|
134
172
|
}
|
|
135
173
|
/**
|
|
136
174
|
* bug 命令入口
|
|
137
|
-
* @param iterationTitlesStr 迭代标题(支持逗号、分号、空格、竖线、顿号等分隔符)
|
|
138
|
-
* @param cliOptions CLI 选项
|
|
139
175
|
*/
|
|
140
176
|
async function bugCommand(iterationTitlesStr, cliOptions = {}) {
|
|
141
177
|
try {
|
|
142
178
|
if (!iterationTitlesStr || iterationTitlesStr.trim() === '') {
|
|
143
179
|
throw new Error('请指定至少一个迭代标题');
|
|
144
180
|
}
|
|
145
|
-
// 解析迭代标题(支持多种分隔符:逗号、分号、空格、竖线、顿号等)
|
|
146
181
|
const iterationTitles = iterationTitlesStr
|
|
147
182
|
.split(/[,,;;|\s、]+/)
|
|
148
183
|
.map((title) => title.trim())
|
|
149
184
|
.filter((title) => title.length > 0);
|
|
150
|
-
const { projectId, roleIds, config } = (0, config_loader_1.loadConfig)(cliOptions);
|
|
185
|
+
const { projectId, roleIds, config, outputFormat } = (0, config_loader_1.loadConfig)(cliOptions);
|
|
151
186
|
const businessService = new business_service_1.BusinessService(config);
|
|
152
|
-
await
|
|
187
|
+
const reportData = await queryBugReportData(businessService, projectId, roleIds, iterationTitles);
|
|
188
|
+
if (outputFormat === 'console') {
|
|
189
|
+
outputConsole(reportData);
|
|
190
|
+
}
|
|
191
|
+
else if (outputFormat === 'csv') {
|
|
192
|
+
outputCsv(reportData, iterationTitles);
|
|
193
|
+
}
|
|
194
|
+
else if (outputFormat === 'json') {
|
|
195
|
+
outputJson(reportData);
|
|
196
|
+
}
|
|
153
197
|
}
|
|
154
198
|
catch (error) {
|
|
155
|
-
|
|
199
|
+
logger_1.logger.error(`执行过程中发生错误: `, error);
|
|
156
200
|
process.exit(1);
|
|
157
201
|
}
|
|
158
202
|
}
|
|
@@ -45,6 +45,7 @@ const readline = __importStar(require("readline"));
|
|
|
45
45
|
const business_service_1 = require("../services/business.service");
|
|
46
46
|
const types_1 = require("../types");
|
|
47
47
|
const config_loader_1 = require("../utils/config-loader");
|
|
48
|
+
const logger_1 = require("../utils/logger");
|
|
48
49
|
/**
|
|
49
50
|
* 清除终端上指定行数的内容
|
|
50
51
|
* @param lines 要清除的行数
|
|
@@ -106,7 +107,7 @@ async function configureRoleIds(businessService, projectId, existingValue) {
|
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
catch (error) {
|
|
109
|
-
|
|
110
|
+
logger_1.logger.error('❌ 获取角色列表失败:', error);
|
|
110
111
|
// 如果获取失败,使用手动输入
|
|
111
112
|
const { manualRoleId } = await inquirer_1.default.prompt([
|
|
112
113
|
{
|
|
@@ -149,12 +150,12 @@ const PROJECT_CONFIG_ITEMS = [
|
|
|
149
150
|
* 引导用户创建或更新全局配置文件
|
|
150
151
|
*/
|
|
151
152
|
async function configCommand() {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const existingConfig = (0, config_loader_1.
|
|
157
|
-
if ((0, config_loader_1.
|
|
153
|
+
logger_1.logger.info('\n欢迎使用 Hecom CodeArts 配置向导');
|
|
154
|
+
logger_1.logger.info('='.repeat(60));
|
|
155
|
+
logger_1.logger.info('此向导将帮助您配置华为云 CodeArts API 访问凭证。');
|
|
156
|
+
logger_1.logger.info(`配置将保存到: ${(0, config_loader_1.getConfigPath)()}\n`);
|
|
157
|
+
const existingConfig = (0, config_loader_1.configExists)() ? (0, config_loader_1.readConfig)() : {};
|
|
158
|
+
if ((0, config_loader_1.configExists)()) {
|
|
158
159
|
const { overwrite } = await inquirer_1.default.prompt([
|
|
159
160
|
{
|
|
160
161
|
type: 'confirm',
|
|
@@ -164,7 +165,7 @@ async function configCommand() {
|
|
|
164
165
|
},
|
|
165
166
|
]);
|
|
166
167
|
if (!overwrite) {
|
|
167
|
-
|
|
168
|
+
logger_1.logger.info('\n已取消配置。');
|
|
168
169
|
return;
|
|
169
170
|
}
|
|
170
171
|
}
|
|
@@ -244,7 +245,7 @@ async function configCommand() {
|
|
|
244
245
|
}
|
|
245
246
|
else {
|
|
246
247
|
const errorMessage = `❌ IAM 凭证验证失败: ${validationResult.error}\n`;
|
|
247
|
-
|
|
248
|
+
logger_1.logger.error(errorMessage);
|
|
248
249
|
const { retry } = await inquirer_1.default.prompt([
|
|
249
250
|
{
|
|
250
251
|
type: 'confirm',
|
|
@@ -254,7 +255,7 @@ async function configCommand() {
|
|
|
254
255
|
},
|
|
255
256
|
]);
|
|
256
257
|
if (!retry) {
|
|
257
|
-
|
|
258
|
+
logger_1.logger.info('\n已取消配置。');
|
|
258
259
|
return;
|
|
259
260
|
}
|
|
260
261
|
// 计算需要清除的行数
|
|
@@ -299,7 +300,7 @@ async function configCommand() {
|
|
|
299
300
|
}
|
|
300
301
|
catch (error) {
|
|
301
302
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
302
|
-
|
|
303
|
+
logger_1.logger.error(`❌ 获取项目列表失败: `, error);
|
|
303
304
|
// 获取失败,使用手动输入
|
|
304
305
|
const { manualProjectId } = await inquirer_1.default.prompt([
|
|
305
306
|
{
|
|
@@ -330,13 +331,13 @@ async function configCommand() {
|
|
|
330
331
|
...projectConfigs,
|
|
331
332
|
};
|
|
332
333
|
try {
|
|
333
|
-
(0, config_loader_1.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
334
|
+
(0, config_loader_1.writeConfig)(finalConfig);
|
|
335
|
+
logger_1.logger.success('\n✅ 全局配置已成功保存');
|
|
336
|
+
logger_1.logger.info(`配置文件位置: ${(0, config_loader_1.getConfigPath)()}`);
|
|
337
|
+
logger_1.logger.info('\n提示:配置文件包含敏感信息,请妥善保管。');
|
|
337
338
|
}
|
|
338
339
|
catch (error) {
|
|
339
|
-
|
|
340
|
+
logger_1.logger.error('\n❌ 保存配置文件失败:', error);
|
|
340
341
|
process.exit(1);
|
|
341
342
|
}
|
|
342
343
|
}
|
|
@@ -346,30 +347,30 @@ async function configCommand() {
|
|
|
346
347
|
*/
|
|
347
348
|
async function updateProjectConfigCommand(configKey) {
|
|
348
349
|
// 检查配置文件是否存在
|
|
349
|
-
if (!(0, config_loader_1.
|
|
350
|
-
|
|
350
|
+
if (!(0, config_loader_1.configExists)()) {
|
|
351
|
+
logger_1.logger.error('\n❌ 全局配置文件不存在,请先运行 `npx @hecom/codearts config` 创建配置。');
|
|
351
352
|
process.exit(1);
|
|
352
353
|
}
|
|
353
354
|
// 查找对应的配置项
|
|
354
355
|
const configItem = PROJECT_CONFIG_ITEMS.find((item) => item.key === configKey);
|
|
355
356
|
if (!configItem) {
|
|
356
|
-
|
|
357
|
-
|
|
357
|
+
logger_1.logger.error(`\n❌ 未知的配置项: ${configKey}`);
|
|
358
|
+
logger_1.logger.info(`\n可用的配置项:`);
|
|
358
359
|
PROJECT_CONFIG_ITEMS.forEach((item) => {
|
|
359
|
-
|
|
360
|
+
logger_1.logger.info(` - ${item.key}: ${item.label}`);
|
|
360
361
|
});
|
|
361
362
|
process.exit(1);
|
|
362
363
|
}
|
|
363
364
|
// 读取现有配置
|
|
364
|
-
const existingConfig = (0, config_loader_1.
|
|
365
|
+
const existingConfig = (0, config_loader_1.readConfig)();
|
|
365
366
|
// 检查必要的配置是否存在
|
|
366
367
|
if (!existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_USERNAME] ||
|
|
367
368
|
!existingConfig[types_1.ConfigKey.HUAWEI_CLOUD_PASSWORD]) {
|
|
368
|
-
|
|
369
|
+
logger_1.logger.error('\n❌ 全局配置不完整,请先运行 `npx @hecom/codearts config` 完成配置。');
|
|
369
370
|
process.exit(1);
|
|
370
371
|
}
|
|
371
372
|
if (!existingConfig[types_1.ConfigKey.PROJECT_ID]) {
|
|
372
|
-
|
|
373
|
+
logger_1.logger.error('\n❌ 项目 ID 未配置,请先运行 `npx @hecom/codearts config` 完成配置。');
|
|
373
374
|
process.exit(1);
|
|
374
375
|
}
|
|
375
376
|
// 创建 BusinessService 实例
|
|
@@ -390,12 +391,12 @@ async function updateProjectConfigCommand(configKey) {
|
|
|
390
391
|
[configKey]: newValue,
|
|
391
392
|
};
|
|
392
393
|
try {
|
|
393
|
-
(0, config_loader_1.
|
|
394
|
-
|
|
395
|
-
|
|
394
|
+
(0, config_loader_1.writeConfig)(updatedConfig);
|
|
395
|
+
logger_1.logger.success(`\n✅ ${configItem.label}已成功更新`);
|
|
396
|
+
logger_1.logger.info(`配置文件位置: ${(0, config_loader_1.getConfigPath)()}`);
|
|
396
397
|
}
|
|
397
398
|
catch (error) {
|
|
398
|
-
|
|
399
|
+
logger_1.logger.error('\n❌ 保存配置文件失败:', error);
|
|
399
400
|
process.exit(1);
|
|
400
401
|
}
|
|
401
402
|
}
|
|
@@ -413,7 +414,7 @@ async function showConfigCommand() {
|
|
|
413
414
|
// 获取最终合并后的配置
|
|
414
415
|
const config = (0, config_loader_1.getConfig)();
|
|
415
416
|
// 按类别显示配置
|
|
416
|
-
|
|
417
|
+
logger_1.logger.info('\n【华为云 IAM 凭证】');
|
|
417
418
|
const iamKeys = [
|
|
418
419
|
types_1.ConfigKey.HUAWEI_CLOUD_IAM_ENDPOINT,
|
|
419
420
|
types_1.ConfigKey.HUAWEI_CLOUD_REGION,
|
|
@@ -424,9 +425,9 @@ async function showConfigCommand() {
|
|
|
424
425
|
for (const key of iamKeys) {
|
|
425
426
|
const value = config[key] || '(未配置)';
|
|
426
427
|
const displayValue = key.includes('PASSWORD') && value !== '(未配置)' ? '********' : value;
|
|
427
|
-
|
|
428
|
+
logger_1.logger.info(` ${formatKeyName(key)}: ${displayValue}`);
|
|
428
429
|
}
|
|
429
|
-
|
|
430
|
+
logger_1.logger.info('\n【CodeArts 配置】');
|
|
430
431
|
const codeartsKeys = [
|
|
431
432
|
types_1.ConfigKey.CODEARTS_BASE_URL,
|
|
432
433
|
types_1.ConfigKey.PROJECT_ID,
|
|
@@ -434,7 +435,7 @@ async function showConfigCommand() {
|
|
|
434
435
|
];
|
|
435
436
|
for (const key of codeartsKeys) {
|
|
436
437
|
const value = config[key] || '(未配置)';
|
|
437
|
-
|
|
438
|
+
logger_1.logger.info(` ${formatKeyName(key)}: ${value}`);
|
|
438
439
|
}
|
|
439
440
|
}
|
|
440
441
|
/**
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import { BusinessService } from '../services/business.service';
|
|
2
1
|
import { CliOptions } from '../utils/config-loader';
|
|
3
|
-
/**
|
|
4
|
-
* 生成单个角色的日报
|
|
5
|
-
*/
|
|
6
|
-
export declare function generateDailyReport(businessService: BusinessService, projectId: string, roleId: number, targetDate: string): Promise<void>;
|
|
7
2
|
/**
|
|
8
3
|
* daily 命令入口
|
|
9
4
|
*/
|