long-git-cli
Version:
A CLI tool for Git tag management.
240 lines • 8.47 kB
JavaScript
;
/**
* Jenkins API 客户端
* 负责与 Jenkins API 交互,触发构建和获取构建状态
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JenkinsClient = void 0;
const axios_1 = __importDefault(require("axios"));
const constants_1 = require("../constants");
/**
* Jenkins API 客户端类
*/
class JenkinsClient {
/**
* 构造函数
* @param baseUrl Jenkins 服务器地址
* @param username Jenkins 用户名
* @param apiToken Jenkins API Token(明文)
*/
constructor(baseUrl, username, apiToken) {
this.baseUrl = baseUrl.replace(/\/$/, ""); // 移除末尾斜杠
this.username = username;
this.apiToken = apiToken;
// 创建 axios 实例,配置 Basic Auth
this.client = axios_1.default.create({
baseURL: this.baseUrl,
auth: {
username: this.username,
password: this.apiToken,
},
headers: {
"Content-Type": "application/json",
},
timeout: 30000, // 30 秒超时
});
}
/**
* 测试连接
* @returns 连接是否成功
*/
async testConnection() {
try {
// 尝试获取 Jenkins 版本信息来测试连接
await this.client.get("/api/json");
return true;
}
catch (error) {
return false;
}
}
/**
* 触发构建
* @param jobName Job 名称(可以包含文件夹路径,如 "folder/job-name")
* @param parameters 构建参数
* @returns 队列 ID
*/
async triggerBuild(jobName, parameters) {
try {
// 构建参数数组
const paramArray = Object.entries(parameters).map(([key, value]) => ({
name: key,
value: value,
}));
// 处理文件夹路径:将 "folder/job" 转换为 "/job/folder/job/job"
const jobPath = jobName.split('/').map(part => `job/${encodeURIComponent(part)}`).join('/');
// 触发带参数的构建
const response = await this.client.post(`/${jobPath}/buildWithParameters`, null, {
params: parameters,
});
// Jenkins 返回 201 状态码,Location header 包含队列 URL
const location = response.headers["location"];
if (!location) {
throw new Error("未获取到队列 URL");
}
// 从 Location 中提取队列 ID
// 格式: http://jenkins.example.com/queue/item/123/
const queueIdMatch = location.match(/\/queue\/item\/(\d+)/);
if (!queueIdMatch) {
throw new Error("无法解析队列 ID");
}
return queueIdMatch[1];
}
catch (error) {
this.handleError(error, "触发构建失败");
throw error;
}
}
/**
* 获取队列项信息
* @param queueId 队列 ID
* @returns 队列项信息
*/
async getQueueItem(queueId) {
try {
const response = await this.client.get(`/queue/item/${queueId}/api/json`);
return response.data;
}
catch (error) {
this.handleError(error, "获取队列信息失败");
throw error;
}
}
/**
* 获取构建信息
* @param jobName Job 名称(可以包含文件夹路径,如 "folder/job-name")
* @param buildNumber 构建号
* @returns 构建信息
*/
async getBuildInfo(jobName, buildNumber) {
try {
// 处理文件夹路径:将 "folder/job" 转换为 "/job/folder/job/job"
const jobPath = jobName.split('/').map(part => `job/${encodeURIComponent(part)}`).join('/');
const response = await this.client.get(`/${jobPath}/${buildNumber}/api/json`);
return {
number: response.data.number,
url: response.data.url,
result: response.data.result,
building: response.data.building,
duration: response.data.duration,
timestamp: response.data.timestamp,
};
}
catch (error) {
this.handleError(error, "获取构建信息失败");
throw error;
}
}
/**
* 等待构建完成
* @param jobName Job 名称
* @param buildNumber 构建号
* @param options 等待选项
* @returns 最终的构建信息
*/
async waitForBuildCompletion(jobName, buildNumber, options) {
const pollInterval = options?.pollInterval || constants_1.JENKINS_POLL_INTERVAL;
const timeout = options?.timeout || constants_1.JENKINS_TIMEOUT;
const onProgress = options?.onProgress;
const startTime = Date.now();
// 轮询构建状态
while (true) {
// 检查超时
if (Date.now() - startTime > timeout) {
throw new Error(`构建监听超时(${timeout / 1000}秒),Job: ${jobName}, Build: ${buildNumber}`);
}
// 获取当前状态
const buildInfo = await this.getBuildInfo(jobName, buildNumber);
// 调用进度回调
if (onProgress) {
onProgress(buildInfo);
}
// 检查是否完成
if (!buildInfo.building) {
return buildInfo;
}
// 等待下一次轮询
await this.sleep(pollInterval);
}
}
/**
* 等待队列项变成构建
* @param queueId 队列 ID
* @param timeout 超时时间(毫秒)
* @returns 构建号
*/
async waitForQueueToBuild(queueId, timeout = 60000) {
const startTime = Date.now();
const pollInterval = 2000; // 2 秒
while (true) {
// 检查超时
if (Date.now() - startTime > timeout) {
throw new Error(`等待队列转换为构建超时(${timeout / 1000}秒)`);
}
// 获取队列项
const queueItem = await this.getQueueItem(queueId);
// 检查是否已经开始构建
if (queueItem.executable?.number) {
return queueItem.executable.number;
}
// 检查是否被阻塞或卡住
if (queueItem.stuck) {
throw new Error("构建队列卡住,无法启动");
}
// 等待下一次轮询
await this.sleep(pollInterval);
}
}
/**
* 触发构建并等待完成
* @param jobName Job 名称
* @param parameters 构建参数
* @param options 等待选项
* @returns 最终的构建信息
*/
async triggerAndWait(jobName, parameters, options) {
// 触发构建
const queueId = await this.triggerBuild(jobName, parameters);
// 等待队列转换为构建
const buildNumber = await this.waitForQueueToBuild(queueId);
// 等待构建完成
return await this.waitForBuildCompletion(jobName, buildNumber, options);
}
/**
* 休眠指定毫秒数
* @param ms 毫秒数
*/
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* 处理错误
* @param error 错误对象
* @param message 错误消息
*/
handleError(error, message) {
if (axios_1.default.isAxiosError(error)) {
const axiosError = error;
if (axiosError.response) {
// 服务器返回错误响应
console.error(`${message}: ${axiosError.response.status} - ${JSON.stringify(axiosError.response.data)}`);
}
else if (axiosError.request) {
// 请求已发送但没有收到响应
console.error(`${message}: 网络错误,无法连接到 Jenkins`);
}
else {
// 请求配置错误
console.error(`${message}: ${axiosError.message}`);
}
}
else {
console.error(`${message}:`, error);
}
}
}
exports.JenkinsClient = JenkinsClient;
//# sourceMappingURL=jenkins-client.js.map