long-git-cli 1.0.12 → 1.0.13
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 +148 -0
- package/dist/commands/config.d.ts +9 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +33 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/deploy.d.ts +14 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +342 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/tag.d.ts.map +1 -1
- package/dist/commands/tag.js +9 -7
- package/dist/commands/tag.js.map +1 -1
- package/dist/devops/api/bitbucket-client.d.ts +101 -0
- package/dist/devops/api/bitbucket-client.d.ts.map +1 -0
- package/dist/devops/api/bitbucket-client.js +335 -0
- package/dist/devops/api/bitbucket-client.js.map +1 -0
- package/dist/devops/api/jenkins-client.d.ts +96 -0
- package/dist/devops/api/jenkins-client.d.ts.map +1 -0
- package/dist/devops/api/jenkins-client.js +240 -0
- package/dist/devops/api/jenkins-client.js.map +1 -0
- package/dist/devops/config/config-manager.d.ts +96 -0
- package/dist/devops/config/config-manager.d.ts.map +1 -0
- package/dist/devops/config/config-manager.js +331 -0
- package/dist/devops/config/config-manager.js.map +1 -0
- package/dist/devops/config/encryption.d.ts +39 -0
- package/dist/devops/config/encryption.d.ts.map +1 -0
- package/dist/devops/config/encryption.js +133 -0
- package/dist/devops/config/encryption.js.map +1 -0
- package/dist/devops/config/storage.d.ts +37 -0
- package/dist/devops/config/storage.d.ts.map +1 -0
- package/dist/devops/config/storage.js +132 -0
- package/dist/devops/config/storage.js.map +1 -0
- package/dist/devops/constants.d.ts +51 -0
- package/dist/devops/constants.d.ts.map +1 -0
- package/dist/devops/constants.js +95 -0
- package/dist/devops/constants.js.map +1 -0
- package/dist/devops/deployer/full-deployer.d.ts +77 -0
- package/dist/devops/deployer/full-deployer.d.ts.map +1 -0
- package/dist/devops/deployer/full-deployer.js +221 -0
- package/dist/devops/deployer/full-deployer.js.map +1 -0
- package/dist/devops/deployer/jenkins-deployer.d.ts +55 -0
- package/dist/devops/deployer/jenkins-deployer.d.ts.map +1 -0
- package/dist/devops/deployer/jenkins-deployer.js +110 -0
- package/dist/devops/deployer/jenkins-deployer.js.map +1 -0
- package/dist/devops/monitor/pipeline-monitor.d.ts +48 -0
- package/dist/devops/monitor/pipeline-monitor.d.ts.map +1 -0
- package/dist/devops/monitor/pipeline-monitor.js +170 -0
- package/dist/devops/monitor/pipeline-monitor.js.map +1 -0
- package/dist/devops/test-ui.d.ts +6 -0
- package/dist/devops/test-ui.d.ts.map +1 -0
- package/dist/devops/test-ui.js +31 -0
- package/dist/devops/test-ui.js.map +1 -0
- package/dist/devops/types.d.ts +138 -0
- package/dist/devops/types.d.ts.map +1 -0
- package/dist/devops/types.js +20 -0
- package/dist/devops/types.js.map +1 -0
- package/dist/devops/ui/server.d.ts +53 -0
- package/dist/devops/ui/server.d.ts.map +1 -0
- package/dist/devops/ui/server.js +1310 -0
- package/dist/devops/ui/server.js.map +1 -0
- package/dist/index.js +31 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/message.d.ts +0 -2
- package/dist/utils/message.d.ts.map +1 -1
- package/dist/utils/message.js +7 -15
- package/dist/utils/message.js.map +1 -1
- package/dist/utils/tag.d.ts +65 -6
- package/dist/utils/tag.d.ts.map +1 -1
- package/dist/utils/tag.js +148 -14
- package/dist/utils/tag.js.map +1 -1
- package/package.json +19 -2
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Jenkins API 客户端
|
|
4
|
+
* 负责与 Jenkins API 交互,触发构建和获取构建状态
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.JenkinsClient = void 0;
|
|
11
|
+
const axios_1 = __importDefault(require("axios"));
|
|
12
|
+
const constants_1 = require("../constants");
|
|
13
|
+
/**
|
|
14
|
+
* Jenkins API 客户端类
|
|
15
|
+
*/
|
|
16
|
+
class JenkinsClient {
|
|
17
|
+
/**
|
|
18
|
+
* 构造函数
|
|
19
|
+
* @param baseUrl Jenkins 服务器地址
|
|
20
|
+
* @param username Jenkins 用户名
|
|
21
|
+
* @param apiToken Jenkins API Token(明文)
|
|
22
|
+
*/
|
|
23
|
+
constructor(baseUrl, username, apiToken) {
|
|
24
|
+
this.baseUrl = baseUrl.replace(/\/$/, ""); // 移除末尾斜杠
|
|
25
|
+
this.username = username;
|
|
26
|
+
this.apiToken = apiToken;
|
|
27
|
+
// 创建 axios 实例,配置 Basic Auth
|
|
28
|
+
this.client = axios_1.default.create({
|
|
29
|
+
baseURL: this.baseUrl,
|
|
30
|
+
auth: {
|
|
31
|
+
username: this.username,
|
|
32
|
+
password: this.apiToken,
|
|
33
|
+
},
|
|
34
|
+
headers: {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
},
|
|
37
|
+
timeout: 30000, // 30 秒超时
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 测试连接
|
|
42
|
+
* @returns 连接是否成功
|
|
43
|
+
*/
|
|
44
|
+
async testConnection() {
|
|
45
|
+
try {
|
|
46
|
+
// 尝试获取 Jenkins 版本信息来测试连接
|
|
47
|
+
await this.client.get("/api/json");
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 触发构建
|
|
56
|
+
* @param jobName Job 名称(可以包含文件夹路径,如 "folder/job-name")
|
|
57
|
+
* @param parameters 构建参数
|
|
58
|
+
* @returns 队列 ID
|
|
59
|
+
*/
|
|
60
|
+
async triggerBuild(jobName, parameters) {
|
|
61
|
+
try {
|
|
62
|
+
// 构建参数数组
|
|
63
|
+
const paramArray = Object.entries(parameters).map(([key, value]) => ({
|
|
64
|
+
name: key,
|
|
65
|
+
value: value,
|
|
66
|
+
}));
|
|
67
|
+
// 处理文件夹路径:将 "folder/job" 转换为 "/job/folder/job/job"
|
|
68
|
+
const jobPath = jobName.split('/').map(part => `job/${encodeURIComponent(part)}`).join('/');
|
|
69
|
+
// 触发带参数的构建
|
|
70
|
+
const response = await this.client.post(`/${jobPath}/buildWithParameters`, null, {
|
|
71
|
+
params: parameters,
|
|
72
|
+
});
|
|
73
|
+
// Jenkins 返回 201 状态码,Location header 包含队列 URL
|
|
74
|
+
const location = response.headers["location"];
|
|
75
|
+
if (!location) {
|
|
76
|
+
throw new Error("未获取到队列 URL");
|
|
77
|
+
}
|
|
78
|
+
// 从 Location 中提取队列 ID
|
|
79
|
+
// 格式: http://jenkins.example.com/queue/item/123/
|
|
80
|
+
const queueIdMatch = location.match(/\/queue\/item\/(\d+)/);
|
|
81
|
+
if (!queueIdMatch) {
|
|
82
|
+
throw new Error("无法解析队列 ID");
|
|
83
|
+
}
|
|
84
|
+
return queueIdMatch[1];
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
this.handleError(error, "触发构建失败");
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 获取队列项信息
|
|
93
|
+
* @param queueId 队列 ID
|
|
94
|
+
* @returns 队列项信息
|
|
95
|
+
*/
|
|
96
|
+
async getQueueItem(queueId) {
|
|
97
|
+
try {
|
|
98
|
+
const response = await this.client.get(`/queue/item/${queueId}/api/json`);
|
|
99
|
+
return response.data;
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
this.handleError(error, "获取队列信息失败");
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 获取构建信息
|
|
108
|
+
* @param jobName Job 名称(可以包含文件夹路径,如 "folder/job-name")
|
|
109
|
+
* @param buildNumber 构建号
|
|
110
|
+
* @returns 构建信息
|
|
111
|
+
*/
|
|
112
|
+
async getBuildInfo(jobName, buildNumber) {
|
|
113
|
+
try {
|
|
114
|
+
// 处理文件夹路径:将 "folder/job" 转换为 "/job/folder/job/job"
|
|
115
|
+
const jobPath = jobName.split('/').map(part => `job/${encodeURIComponent(part)}`).join('/');
|
|
116
|
+
const response = await this.client.get(`/${jobPath}/${buildNumber}/api/json`);
|
|
117
|
+
return {
|
|
118
|
+
number: response.data.number,
|
|
119
|
+
url: response.data.url,
|
|
120
|
+
result: response.data.result,
|
|
121
|
+
building: response.data.building,
|
|
122
|
+
duration: response.data.duration,
|
|
123
|
+
timestamp: response.data.timestamp,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
this.handleError(error, "获取构建信息失败");
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 等待构建完成
|
|
133
|
+
* @param jobName Job 名称
|
|
134
|
+
* @param buildNumber 构建号
|
|
135
|
+
* @param options 等待选项
|
|
136
|
+
* @returns 最终的构建信息
|
|
137
|
+
*/
|
|
138
|
+
async waitForBuildCompletion(jobName, buildNumber, options) {
|
|
139
|
+
const pollInterval = options?.pollInterval || constants_1.JENKINS_POLL_INTERVAL;
|
|
140
|
+
const timeout = options?.timeout || constants_1.JENKINS_TIMEOUT;
|
|
141
|
+
const onProgress = options?.onProgress;
|
|
142
|
+
const startTime = Date.now();
|
|
143
|
+
// 轮询构建状态
|
|
144
|
+
while (true) {
|
|
145
|
+
// 检查超时
|
|
146
|
+
if (Date.now() - startTime > timeout) {
|
|
147
|
+
throw new Error(`构建监听超时(${timeout / 1000}秒),Job: ${jobName}, Build: ${buildNumber}`);
|
|
148
|
+
}
|
|
149
|
+
// 获取当前状态
|
|
150
|
+
const buildInfo = await this.getBuildInfo(jobName, buildNumber);
|
|
151
|
+
// 调用进度回调
|
|
152
|
+
if (onProgress) {
|
|
153
|
+
onProgress(buildInfo);
|
|
154
|
+
}
|
|
155
|
+
// 检查是否完成
|
|
156
|
+
if (!buildInfo.building) {
|
|
157
|
+
return buildInfo;
|
|
158
|
+
}
|
|
159
|
+
// 等待下一次轮询
|
|
160
|
+
await this.sleep(pollInterval);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 等待队列项变成构建
|
|
165
|
+
* @param queueId 队列 ID
|
|
166
|
+
* @param timeout 超时时间(毫秒)
|
|
167
|
+
* @returns 构建号
|
|
168
|
+
*/
|
|
169
|
+
async waitForQueueToBuild(queueId, timeout = 60000) {
|
|
170
|
+
const startTime = Date.now();
|
|
171
|
+
const pollInterval = 2000; // 2 秒
|
|
172
|
+
while (true) {
|
|
173
|
+
// 检查超时
|
|
174
|
+
if (Date.now() - startTime > timeout) {
|
|
175
|
+
throw new Error(`等待队列转换为构建超时(${timeout / 1000}秒)`);
|
|
176
|
+
}
|
|
177
|
+
// 获取队列项
|
|
178
|
+
const queueItem = await this.getQueueItem(queueId);
|
|
179
|
+
// 检查是否已经开始构建
|
|
180
|
+
if (queueItem.executable?.number) {
|
|
181
|
+
return queueItem.executable.number;
|
|
182
|
+
}
|
|
183
|
+
// 检查是否被阻塞或卡住
|
|
184
|
+
if (queueItem.stuck) {
|
|
185
|
+
throw new Error("构建队列卡住,无法启动");
|
|
186
|
+
}
|
|
187
|
+
// 等待下一次轮询
|
|
188
|
+
await this.sleep(pollInterval);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 触发构建并等待完成
|
|
193
|
+
* @param jobName Job 名称
|
|
194
|
+
* @param parameters 构建参数
|
|
195
|
+
* @param options 等待选项
|
|
196
|
+
* @returns 最终的构建信息
|
|
197
|
+
*/
|
|
198
|
+
async triggerAndWait(jobName, parameters, options) {
|
|
199
|
+
// 触发构建
|
|
200
|
+
const queueId = await this.triggerBuild(jobName, parameters);
|
|
201
|
+
// 等待队列转换为构建
|
|
202
|
+
const buildNumber = await this.waitForQueueToBuild(queueId);
|
|
203
|
+
// 等待构建完成
|
|
204
|
+
return await this.waitForBuildCompletion(jobName, buildNumber, options);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 休眠指定毫秒数
|
|
208
|
+
* @param ms 毫秒数
|
|
209
|
+
*/
|
|
210
|
+
sleep(ms) {
|
|
211
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 处理错误
|
|
215
|
+
* @param error 错误对象
|
|
216
|
+
* @param message 错误消息
|
|
217
|
+
*/
|
|
218
|
+
handleError(error, message) {
|
|
219
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
220
|
+
const axiosError = error;
|
|
221
|
+
if (axiosError.response) {
|
|
222
|
+
// 服务器返回错误响应
|
|
223
|
+
console.error(`${message}: ${axiosError.response.status} - ${JSON.stringify(axiosError.response.data)}`);
|
|
224
|
+
}
|
|
225
|
+
else if (axiosError.request) {
|
|
226
|
+
// 请求已发送但没有收到响应
|
|
227
|
+
console.error(`${message}: 网络错误,无法连接到 Jenkins`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// 请求配置错误
|
|
231
|
+
console.error(`${message}: ${axiosError.message}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
console.error(`${message}:`, error);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
exports.JenkinsClient = JenkinsClient;
|
|
240
|
+
//# sourceMappingURL=jenkins-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jenkins-client.js","sourceRoot":"","sources":["../../../src/devops/api/jenkins-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置管理器
|
|
3
|
+
* 管理所有配置数据的读取、写入、验证和加密
|
|
4
|
+
*/
|
|
5
|
+
import { Storage } from "./storage";
|
|
6
|
+
import { DeployConfig, ProjectConfig, JenkinsConfig } from "../types";
|
|
7
|
+
/**
|
|
8
|
+
* ConfigManager 类
|
|
9
|
+
* 负责配置的 CRUD 操作
|
|
10
|
+
*/
|
|
11
|
+
export declare class ConfigManager {
|
|
12
|
+
private storage;
|
|
13
|
+
private configFilename;
|
|
14
|
+
/** 内存缓存(用于存储明文密码,仅在运行时有效) */
|
|
15
|
+
private credentialsCache;
|
|
16
|
+
constructor(storage?: Storage);
|
|
17
|
+
/**
|
|
18
|
+
* 加载配置
|
|
19
|
+
*/
|
|
20
|
+
loadConfig(): Promise<DeployConfig>;
|
|
21
|
+
/**
|
|
22
|
+
* 保存配置(不验证完整性)
|
|
23
|
+
* 用于部分更新配置
|
|
24
|
+
*/
|
|
25
|
+
saveConfigWithoutValidation(config: DeployConfig): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* 保存配置
|
|
28
|
+
*/
|
|
29
|
+
saveConfig(config: DeployConfig): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* 验证配置完整性
|
|
32
|
+
*/
|
|
33
|
+
validateConfig(config: DeployConfig): Promise<boolean>;
|
|
34
|
+
/**
|
|
35
|
+
* 获取项目配置
|
|
36
|
+
*/
|
|
37
|
+
getProjectConfig(projectPath: string): Promise<ProjectConfig | null>;
|
|
38
|
+
/**
|
|
39
|
+
* 更新项目配置
|
|
40
|
+
*/
|
|
41
|
+
updateProjectConfig(projectPath: string, projectConfig: ProjectConfig): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* 删除项目配置
|
|
44
|
+
*/
|
|
45
|
+
deleteProjectConfig(projectPath: string): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* 加密 Token
|
|
48
|
+
*/
|
|
49
|
+
encryptToken(token: string): Promise<string>;
|
|
50
|
+
/**
|
|
51
|
+
* 解密 Token
|
|
52
|
+
*/
|
|
53
|
+
decryptToken(encryptedToken: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* 验证 Token
|
|
56
|
+
* @deprecated 不再需要,直接解密使用
|
|
57
|
+
*/
|
|
58
|
+
verifyToken(token: string, hash: string): Promise<boolean>;
|
|
59
|
+
/**
|
|
60
|
+
* 更新 Bitbucket 配置
|
|
61
|
+
* @param username Bitbucket 用户名
|
|
62
|
+
* @param token API Token 或 App Password(兼容旧版)
|
|
63
|
+
*/
|
|
64
|
+
updateBitbucketConfig(username: string, token: string): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* 获取 Bitbucket 明文密码
|
|
67
|
+
* 优先从缓存获取,如果没有则从配置文件解密
|
|
68
|
+
*/
|
|
69
|
+
getBitbucketPassword(): Promise<string | undefined>;
|
|
70
|
+
/**
|
|
71
|
+
* 更新 Jenkins 配置
|
|
72
|
+
*/
|
|
73
|
+
updateJenkinsConfig(jenkinsConfigs: JenkinsConfig[]): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* 添加 Jenkins 实例
|
|
76
|
+
*/
|
|
77
|
+
addJenkinsInstance(type: "app" | "pcalpha", url: string, username: string, apiToken: string): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* 获取 Jenkins 明文 token
|
|
80
|
+
* 优先从缓存获取,如果没有则从配置文件解密
|
|
81
|
+
*/
|
|
82
|
+
getJenkinsToken(type: "app" | "pcalpha"): Promise<string | undefined>;
|
|
83
|
+
/**
|
|
84
|
+
* 更新部署状态
|
|
85
|
+
*/
|
|
86
|
+
updateDeployStatus(projectPath: string, environment: string, status: any): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* 获取默认配置
|
|
89
|
+
*/
|
|
90
|
+
private getDefaultConfig;
|
|
91
|
+
/**
|
|
92
|
+
* 检查配置文件是否存在
|
|
93
|
+
*/
|
|
94
|
+
configExists(): Promise<boolean>;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=config-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../../src/devops/config/config-manager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EACL,YAAY,EACZ,aAAa,EAEb,aAAa,EACd,MAAM,UAAU,CAAC;AAIlB;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,cAAc,CAAS;IAC/B,6BAA6B;IAC7B,OAAO,CAAC,gBAAgB,CAKtB;gBAEU,OAAO,CAAC,EAAE,OAAO;IAK7B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC;IAWzC;;;OAGG;IACG,2BAA2B,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrD;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAsD5D;;OAEG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAK1E;;OAEG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,IAAI,CAAC;IAOhB;;OAEG;IACG,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO7D;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlD;;OAEG;IACH,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAI5C;;;OAGG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIhE;;;;OAIG;IACG,qBAAqB,CACzB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC;IAkBhB;;;OAGG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAyBzD;;OAEG;IACG,mBAAmB,CAAC,cAAc,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAOzE;;OAEG;IACG,kBAAkB,CACtB,IAAI,EAAE,KAAK,GAAG,SAAS,EACvB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IA4BhB;;;OAGG;IACG,eAAe,CAAC,IAAI,EAAE,KAAK,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IA0B3E;;OAEG;IACG,kBAAkB,CACtB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,GAAG,GACV,OAAO,CAAC,IAAI,CAAC;IAuBhB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;CAGvC"}
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 配置管理器
|
|
4
|
+
* 管理所有配置数据的读取、写入、验证和加密
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.ConfigManager = void 0;
|
|
41
|
+
const storage_1 = require("./storage");
|
|
42
|
+
const encryption_1 = require("./encryption");
|
|
43
|
+
const constants_1 = require("../constants");
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
/**
|
|
46
|
+
* ConfigManager 类
|
|
47
|
+
* 负责配置的 CRUD 操作
|
|
48
|
+
*/
|
|
49
|
+
class ConfigManager {
|
|
50
|
+
constructor(storage) {
|
|
51
|
+
/** 内存缓存(用于存储明文密码,仅在运行时有效) */
|
|
52
|
+
this.credentialsCache = {
|
|
53
|
+
jenkinsTokens: new Map(),
|
|
54
|
+
};
|
|
55
|
+
this.storage = storage || new storage_1.Storage();
|
|
56
|
+
this.configFilename = path.basename(constants_1.CONFIG_FILE);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 加载配置
|
|
60
|
+
*/
|
|
61
|
+
async loadConfig() {
|
|
62
|
+
const config = await this.storage.read(this.configFilename);
|
|
63
|
+
if (!config) {
|
|
64
|
+
/** 返回默认配置 */
|
|
65
|
+
return this.getDefaultConfig();
|
|
66
|
+
}
|
|
67
|
+
return config;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 保存配置(不验证完整性)
|
|
71
|
+
* 用于部分更新配置
|
|
72
|
+
*/
|
|
73
|
+
async saveConfigWithoutValidation(config) {
|
|
74
|
+
await this.storage.write(this.configFilename, config);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 保存配置
|
|
78
|
+
*/
|
|
79
|
+
async saveConfig(config) {
|
|
80
|
+
/** 验证配置 */
|
|
81
|
+
const isValid = await this.validateConfig(config);
|
|
82
|
+
if (!isValid) {
|
|
83
|
+
throw new Error("配置验证失败");
|
|
84
|
+
}
|
|
85
|
+
await this.storage.write(this.configFilename, config);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 验证配置完整性
|
|
89
|
+
*/
|
|
90
|
+
async validateConfig(config) {
|
|
91
|
+
try {
|
|
92
|
+
/** 检查必需字段 */
|
|
93
|
+
if (!config.version) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/** 检查 Bitbucket 配置 */
|
|
97
|
+
if (!config.bitbucket?.username) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
// 兼容新旧两种 token 格式
|
|
101
|
+
if (!config.bitbucket?.apiTokenHash && !config.bitbucket?.appPasswordHash) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
/** 检查至少有一个 Jenkins 实例 */
|
|
105
|
+
if (!config.jenkins || config.jenkins.length === 0) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
/** 检查每个 Jenkins 实例的配置 */
|
|
109
|
+
for (const jenkins of config.jenkins) {
|
|
110
|
+
if (!jenkins.type ||
|
|
111
|
+
!jenkins.url ||
|
|
112
|
+
!jenkins.username ||
|
|
113
|
+
!jenkins.apiTokenHash) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** 检查至少有一个项目配置 */
|
|
118
|
+
const projectPaths = Object.keys(config.projects || {});
|
|
119
|
+
if (projectPaths.length === 0) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
/** 检查每个项目至少有一个环境 */
|
|
123
|
+
for (const projectPath of projectPaths) {
|
|
124
|
+
const project = config.projects[projectPath];
|
|
125
|
+
const envs = Object.keys(project.environments || {});
|
|
126
|
+
if (envs.length === 0) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 获取项目配置
|
|
138
|
+
*/
|
|
139
|
+
async getProjectConfig(projectPath) {
|
|
140
|
+
const config = await this.loadConfig();
|
|
141
|
+
return config.projects[projectPath] || null;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 更新项目配置
|
|
145
|
+
*/
|
|
146
|
+
async updateProjectConfig(projectPath, projectConfig) {
|
|
147
|
+
const config = await this.loadConfig();
|
|
148
|
+
config.projects[projectPath] = projectConfig;
|
|
149
|
+
// 使用不验证的保存方法,允许部分配置
|
|
150
|
+
await this.saveConfigWithoutValidation(config);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 删除项目配置
|
|
154
|
+
*/
|
|
155
|
+
async deleteProjectConfig(projectPath) {
|
|
156
|
+
const config = await this.loadConfig();
|
|
157
|
+
delete config.projects[projectPath];
|
|
158
|
+
// 使用不验证的保存方法,允许部分配置
|
|
159
|
+
await this.saveConfigWithoutValidation(config);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 加密 Token
|
|
163
|
+
*/
|
|
164
|
+
async encryptToken(token) {
|
|
165
|
+
return encryption_1.Encryption.encrypt(token);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 解密 Token
|
|
169
|
+
*/
|
|
170
|
+
decryptToken(encryptedToken) {
|
|
171
|
+
return encryption_1.Encryption.decrypt(encryptedToken);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 验证 Token
|
|
175
|
+
* @deprecated 不再需要,直接解密使用
|
|
176
|
+
*/
|
|
177
|
+
async verifyToken(token, hash) {
|
|
178
|
+
return encryption_1.Encryption.verify(token, hash);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 更新 Bitbucket 配置
|
|
182
|
+
* @param username Bitbucket 用户名
|
|
183
|
+
* @param token API Token 或 App Password(兼容旧版)
|
|
184
|
+
*/
|
|
185
|
+
async updateBitbucketConfig(username, token) {
|
|
186
|
+
const config = await this.loadConfig();
|
|
187
|
+
const tokenHash = await this.encryptToken(token);
|
|
188
|
+
config.bitbucket = {
|
|
189
|
+
username,
|
|
190
|
+
apiTokenHash: tokenHash,
|
|
191
|
+
// 保留旧字段以兼容
|
|
192
|
+
appPasswordHash: tokenHash,
|
|
193
|
+
};
|
|
194
|
+
/** 缓存明文 token */
|
|
195
|
+
this.credentialsCache.bitbucketPassword = token;
|
|
196
|
+
// 使用不验证的保存方法,允许部分配置
|
|
197
|
+
await this.saveConfigWithoutValidation(config);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* 获取 Bitbucket 明文密码
|
|
201
|
+
* 优先从缓存获取,如果没有则从配置文件解密
|
|
202
|
+
*/
|
|
203
|
+
async getBitbucketPassword() {
|
|
204
|
+
// 先尝试从缓存获取
|
|
205
|
+
if (this.credentialsCache.bitbucketPassword) {
|
|
206
|
+
return this.credentialsCache.bitbucketPassword;
|
|
207
|
+
}
|
|
208
|
+
// 从配置文件解密
|
|
209
|
+
const config = await this.loadConfig();
|
|
210
|
+
const encryptedToken = config.bitbucket.apiTokenHash || config.bitbucket.appPasswordHash;
|
|
211
|
+
if (encryptedToken) {
|
|
212
|
+
try {
|
|
213
|
+
const decrypted = this.decryptToken(encryptedToken);
|
|
214
|
+
// 缓存到内存
|
|
215
|
+
this.credentialsCache.bitbucketPassword = decrypted;
|
|
216
|
+
return decrypted;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error('解密 Bitbucket token 失败:', error);
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* 更新 Jenkins 配置
|
|
227
|
+
*/
|
|
228
|
+
async updateJenkinsConfig(jenkinsConfigs) {
|
|
229
|
+
const config = await this.loadConfig();
|
|
230
|
+
config.jenkins = jenkinsConfigs;
|
|
231
|
+
// 使用不验证的保存方法,允许部分配置
|
|
232
|
+
await this.saveConfigWithoutValidation(config);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* 添加 Jenkins 实例
|
|
236
|
+
*/
|
|
237
|
+
async addJenkinsInstance(type, url, username, apiToken) {
|
|
238
|
+
const config = await this.loadConfig();
|
|
239
|
+
const apiTokenHash = await this.encryptToken(apiToken);
|
|
240
|
+
const newJenkins = {
|
|
241
|
+
type,
|
|
242
|
+
url,
|
|
243
|
+
username,
|
|
244
|
+
apiTokenHash,
|
|
245
|
+
};
|
|
246
|
+
/** 检查是否已存在相同类型的实例 */
|
|
247
|
+
const existingIndex = config.jenkins.findIndex((j) => j.type === type);
|
|
248
|
+
if (existingIndex >= 0) {
|
|
249
|
+
/** 更新现有实例 */
|
|
250
|
+
config.jenkins[existingIndex] = newJenkins;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
/** 添加新实例 */
|
|
254
|
+
config.jenkins.push(newJenkins);
|
|
255
|
+
}
|
|
256
|
+
/** 缓存明文 token */
|
|
257
|
+
this.credentialsCache.jenkinsTokens?.set(type, apiToken);
|
|
258
|
+
// 使用不验证的保存方法,允许部分配置
|
|
259
|
+
await this.saveConfigWithoutValidation(config);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* 获取 Jenkins 明文 token
|
|
263
|
+
* 优先从缓存获取,如果没有则从配置文件解密
|
|
264
|
+
*/
|
|
265
|
+
async getJenkinsToken(type) {
|
|
266
|
+
// 先尝试从缓存获取
|
|
267
|
+
const cached = this.credentialsCache.jenkinsTokens?.get(type);
|
|
268
|
+
if (cached) {
|
|
269
|
+
return cached;
|
|
270
|
+
}
|
|
271
|
+
// 从配置文件解密
|
|
272
|
+
const config = await this.loadConfig();
|
|
273
|
+
const jenkinsConfig = config.jenkins.find((j) => j.type === type);
|
|
274
|
+
if (jenkinsConfig?.apiTokenHash) {
|
|
275
|
+
try {
|
|
276
|
+
const decrypted = this.decryptToken(jenkinsConfig.apiTokenHash);
|
|
277
|
+
// 缓存到内存
|
|
278
|
+
this.credentialsCache.jenkinsTokens?.set(type, decrypted);
|
|
279
|
+
return decrypted;
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
console.error(`解密 Jenkins ${type} token 失败:`, error);
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* 更新部署状态
|
|
290
|
+
*/
|
|
291
|
+
async updateDeployStatus(projectPath, environment, status) {
|
|
292
|
+
const config = await this.loadConfig();
|
|
293
|
+
const project = config.projects[projectPath];
|
|
294
|
+
if (!project) {
|
|
295
|
+
throw new Error(`项目不存在: ${projectPath}`);
|
|
296
|
+
}
|
|
297
|
+
const env = project.environments[environment];
|
|
298
|
+
if (!env) {
|
|
299
|
+
throw new Error(`环境不存在: ${environment}`);
|
|
300
|
+
}
|
|
301
|
+
// 更新部署状态
|
|
302
|
+
env.deployStatus = {
|
|
303
|
+
...env.deployStatus,
|
|
304
|
+
...status,
|
|
305
|
+
lastUpdated: new Date().toISOString(),
|
|
306
|
+
};
|
|
307
|
+
await this.saveConfigWithoutValidation(config);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* 获取默认配置
|
|
311
|
+
*/
|
|
312
|
+
getDefaultConfig() {
|
|
313
|
+
return {
|
|
314
|
+
version: constants_1.CONFIG_VERSION,
|
|
315
|
+
projects: {},
|
|
316
|
+
bitbucket: {
|
|
317
|
+
username: "",
|
|
318
|
+
apiTokenHash: "",
|
|
319
|
+
},
|
|
320
|
+
jenkins: [],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* 检查配置文件是否存在
|
|
325
|
+
*/
|
|
326
|
+
async configExists() {
|
|
327
|
+
return this.storage.exists(this.configFilename);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
exports.ConfigManager = ConfigManager;
|
|
331
|
+
//# sourceMappingURL=config-manager.js.map
|