UNPKG

long-git-cli

Version:

A CLI tool for Git tag management.

240 lines 8.47 kB
"use strict"; /** * 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