在 Jenkins 页面左上角添加状态显示、进度条和“联合构建”按钮
当前为
// ==UserScript==
// @name Jenkins 联合构建 (v3.3 - 修复加载问题)
// @namespace http://tampermonkey.net/
// @version 3.3
// @description 在 Jenkins 页面左上角添加状态显示、进度条和“联合构建”按钮
// @author Tandy
// @match http://10.9.31.83:9001/job/sz-newcis-dev/*
// @grant none
// @license MIT
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// -----------------------------------------------------------------
// !!! 核心修复 !!!
// 我们不再立即执行代码,而是定义一个 main 函数,
// 并等待 "load" 事件触发 (即页面所有资源都加载完毕) 后再运行。
// 我还添加了 @run-at document-idle 确保时机更晚。
// -----------------------------------------------------------------
// 1. 定义所有函数
function getMyJenkinsCrumb() {
const crumbInput = document.querySelector('input[name="Jenkins-Crumb"]');
if (crumbInput) {
return crumbInput.value;
}
console.error("未能找到 Jenkins Crumb input 元素。");
return null;
}
let statusDisplay, progressBar, progressContainer;
function updateStatus(message, isError = false) {
if (!statusDisplay) return; // 安全检查
// console.log(message);
statusDisplay.innerText = message;
statusDisplay.style.color = isError ? 'red' : 'black';
statusDisplay.style.borderColor = isError ? 'red' : '#ccc';
}
function startVisibleTimer(durationMs) {
return new Promise(resolve => {
if (!progressContainer || !progressBar) {
console.error("进度条元素未初始化。");
return resolve(); // 立即结束,防止卡住
}
progressContainer.style.display = 'block';
progressBar.style.width = '0%';
const startTime = Date.now();
const updateIntervalMs = 100;
const timerInterval = setInterval(() => {
const elapsed = Date.now() - startTime;
if (elapsed >= durationMs) {
clearInterval(timerInterval);
progressContainer.style.display = 'none';
progressBar.style.width = '0%';
resolve();
} else {
const progressPercent = (elapsed / durationMs) * 100;
const remainingSeconds = (durationMs - elapsed) / 1000;
progressBar.style.width = `${progressPercent}%`;
updateStatus(`步骤 2: 等待中... ${remainingSeconds.toFixed(1)}s`);
}
}, updateIntervalMs);
});
}
async function triggerSingleBuild(jobName, buildUrl, crumb) {
updateStatus(`[${jobName}] 正在请求构建...`);
try {
const response = await fetch(buildUrl, {
method: 'POST',
headers: { 'Jenkins-Crumb': crumb },
body: null
});
if (response.ok) {
updateStatus(`[${jobName}] 构建请求已成功发送!`);
return true;
} else {
updateStatus(`[${jobName}] 构建请求失败!状态: ${response.status}`, true);
return false;
}
} catch (error) {
updateStatus(`[${jobName}] 发送请求时发生网络错误: ${error}`, true);
return false;
}
}
async function startCombinedChain() {
const crumb = getMyJenkinsCrumb();
if (!crumb) {
updateStatus("错误:无法获取 Crumb。", true);
return;
}
updateStatus('步骤 1: 正在同时触发 Common 和 API ...');
const commonUrl = 'http://10.9.31.83:9001/job/sz-newcis-dev/job/sz-newcis-dev_cis-common/build?delay=0sec';
const apiUrl = 'http://10.9.31.83:9001/job/sz-newcis-dev/job/sz-newcis-dev_cis-api/build?delay=0sec';
const parallelBuilds = [
triggerSingleBuild('Common', commonUrl, crumb),
triggerSingleBuild('API', apiUrl, crumb)
];
const results = await Promise.all(parallelBuilds);
const allSucceeded = results.every(res => res === true);
if (!allSucceeded) {
updateStatus("步骤 1 失败:Common 或 API 构建请求失败。构建链中止。", true);
return;
}
updateStatus('Common 和 API 构建已全部触发。');
await startVisibleTimer(60000);
updateStatus('步骤 3: 正在触发 Bill Service ...');
const billUrl = 'http://10.9.31.83:9001/job/sz-newcis-dev/job/sz-newcis-dev_cis-bill-service/build?delay=0sec';
let success = await triggerSingleBuild('Bill Service', billUrl, crumb);
if (!success) {
updateStatus("构建链因 Bill Service 失败而中止。", true);
return;
}
updateStatus('步骤 4: 正在触发 Customer Service ...');
const customerUrl = 'http://10.9.31.83:9001/job/sz-newcis-dev/job/sz-newcis-dev_cis-customer-service/build?delay=0sec';
success = await triggerSingleBuild('Customer Service', customerUrl, crumb);
if (!success) {
updateStatus("Customer Service 构建失败。构建链结束。", true);
return;
}
updateStatus("联合构建链 (Common/API -> Bill -> Customer) 全部完成。");
}
// 2. 定义我们的 "主" 函数,用于创建和附加 UI 元素
function createUI() {
// --- 状态显示 ---
statusDisplay = document.createElement('div');
statusDisplay.id = 'gm-status-display';
statusDisplay.style = `
position: fixed; top: 10px; left: 10px; z-index: 9998; padding: 8px;
background-color: #f0f0f0; border: 1px solid #ccc; border-radius: 4px;
width: 250px; font-family: Arial, sans-serif; font-size: 13px;
box-sizing: border-box;
`;
statusDisplay.innerText = '准备就绪。';
document.body.appendChild(statusDisplay);
// --- 进度条 ---
progressContainer = document.createElement('div');
progressContainer.id = 'gm-progress-container';
progressContainer.style = `
position: fixed; top: 40px; left: 10px; z-index: 9998;
width: 250px; height: 10px; background-color: #e9ecef;
border: 1px solid #ced4da; border-radius: 4px;
box-sizing: border-box; display: none;
`;
progressBar = document.createElement('div');
progressBar.id = 'gm-progress-bar';
progressBar.style = `
height: 100%; width: 0%; background-color: #007bff;
border-radius: 2px; transition: width 0.1s linear;
`;
progressContainer.appendChild(progressBar);
document.body.appendChild(progressContainer);
// --- 按钮 ---
const combinedButton = document.createElement('button');
combinedButton.innerText = '▶ 启动联合构建';
combinedButton.style = `
position: fixed; left: 10px; top: 60px; z-index: 9999;
padding: 8px 12px; color: white; border: none; border-radius: 4px;
cursor: pointer; box-shadow: 0 2px 5px rgba(0,0,0,0.2);
width: 250px; text-align: left; background-color: #f0ad4e;
box-sizing: border-box;
`;
combinedButton.onmouseover = function() { combinedButton.style.backgroundColor = '#ec971f'; };
combinedButton.onmouseout = function() { combinedButton.style.backgroundColor = '#f0ad4e'; };
combinedButton.onclick = startCombinedChain;
document.body.appendChild(combinedButton);
}
// 3. 确保在 document.body 可用后才执行 UI 创建
if (document.body) {
createUI();
} else {
// 如果 body 还没好,就添加一个监听器
window.addEventListener('load', createUI);
}
})();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址