guanghulab/scripts/pipeline-reporter.js
2026-05-10 13:12:44 +08:00

141 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ━━━ 实时进度上报模块 ━━━
* 文件位置scripts/pipeline-reporter.js
*
* 每完成一步,在 Issue 评论区发一条新评论,开发者刷新即可看到实时进度。
*
* 使用方式CLI
* ISSUE_NUMBER=92 STEP_NUM=2 STEP_NAME="解析内容" STEP_STATUS=ok \
* STEP_DETAIL="广播编号: BC-M22-007-AW" GITHUB_TOKEN=xxx \
* node scripts/pipeline-reporter.js
*
* 或在 Node.js 中 require
* const { reportStep, reportComplete } = require('./pipeline-reporter');
*/
const https = require('https');
const OWNER = process.env.GITHUB_REPOSITORY
? process.env.GITHUB_REPOSITORY.split('/')[0]
: 'qinfendebingshuo';
const REPO = process.env.GITHUB_REPOSITORY
? process.env.GITHUB_REPOSITORY.split('/')[1]
: 'guanghulab';
/**
* 发送 GitHub API 请求
*/
function githubAPI(method, path, data) {
return new Promise((resolve, reject) => {
const body = data ? JSON.stringify(data) : null;
const req = https.request({
hostname: 'api.github.com',
path: path,
method: method,
headers: {
'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`,
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'Zhuyuan-Pipeline-Reporter',
'Content-Type': 'application/json',
...(body ? { 'Content-Length': Buffer.byteLength(body) } : {})
}
}, (res) => {
let responseBody = '';
res.on('data', chunk => { responseBody += chunk; });
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
try { resolve(JSON.parse(responseBody)); }
catch (e) { console.error('⚠️ JSON parse error:', e.message); resolve(responseBody); }
} else {
reject(new Error(`GitHub API ${res.statusCode}: ${responseBody.slice(0, 200)}`));
}
});
});
req.on('error', reject);
if (body) req.write(body);
req.end();
});
}
/**
* 每完成一步,在 Issue 评论区发一条新评论
* @param {number} issueNumber - Issue 编号
* @param {number} stepNum - 步骤编号1-8
* @param {string} stepName - 步骤名称
* @param {'ok'|'error'|'skip'} status - 步骤状态
* @param {string} detail - 详情说明
*/
async function reportStep(issueNumber, stepNum, stepName, status, detail) {
const icon = status === 'ok' ? '✅' : status === 'error' ? '❌' : '⏭️';
const statusText = status === 'ok' ? '已完成' : status === 'error' ? '失败' : '跳过';
const body = [
`${icon} **步骤 ${stepNum}/8 · ${stepName}** · ${statusText}`,
'',
detail,
'',
`——铸渊ICE-GL-ZY001· ${new Date().toISOString()}`
].join('\n');
await githubAPI('POST', `/repos/${OWNER}/${REPO}/issues/${issueNumber}/comments`, { body });
}
/**
* 闭环完成时发最终汇总
* @param {number} issueNumber - Issue 编号
* @param {boolean} success - 是否全部成功
* @param {string} summary - 汇总内容
*/
async function reportComplete(issueNumber, success, summary) {
const icon = success ? '🎉' : '⚠️';
const title = success ? '闭环处理完成' : '闭环处理异常';
const body = [
`## ${icon} ${title}`,
'',
summary,
'',
success
? '> 新广播已生成,结果将发送到您的邮箱。'
: '> 部分步骤异常,已记录日志。冰朔会在下次巡检时处理。',
'',
`——铸渊ICE-GL-ZY001· ${new Date().toISOString()}`
].join('\n');
await githubAPI('POST', `/repos/${OWNER}/${REPO}/issues/${issueNumber}/comments`, { body });
}
// ━━━ CLI 模式:直接 node scripts/pipeline-reporter.js ━━━
if (require.main === module) {
const issueNumber = parseInt(process.env.ISSUE_NUMBER);
const stepNum = parseInt(process.env.STEP_NUM);
const stepName = process.env.STEP_NAME || '';
const status = process.env.STEP_STATUS || 'ok';
const detail = process.env.STEP_DETAIL || '';
const isComplete = process.env.REPORT_COMPLETE === 'true';
const isSuccess = process.env.REPORT_SUCCESS !== 'false';
if (!issueNumber) {
console.error('❌ 缺少 ISSUE_NUMBER');
process.exit(1);
}
(async () => {
try {
if (isComplete) {
await reportComplete(issueNumber, isSuccess, detail);
console.log(`📋 闭环汇总已发送 · Issue #${issueNumber}`);
} else {
await reportStep(issueNumber, stepNum, stepName, status, detail);
console.log(`📋 步骤 ${stepNum}/8 · ${stepName} · ${status} 已上报 · Issue #${issueNumber}`);
}
} catch (err) {
console.error(`⚠️ 进度上报失败: ${err.message}`);
// 上报失败不应阻断管道
process.exit(0);
}
})();
}
module.exports = { reportStep, reportComplete };