From 91b7565b33812677089c635ebca1e601605f94b6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 7 Mar 2026 06:18:56 +0000
Subject: [PATCH 01/14] Initial plan
From d91d1b037b67ba7cb43df4daa4dc668d44f4fa1f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 7 Mar 2026 06:25:23 +0000
Subject: [PATCH 02/14] =?UTF-8?q?feat:=20=E5=88=9B=E5=BB=BA=E5=86=B0?=
=?UTF-8?q?=E6=9C=94=E4=BA=BA=E6=A0=BC=E4=BD=93=E8=87=AA=E5=8A=A8=E9=83=A8?=
=?UTF-8?q?=E7=BD=B2=E8=AF=8A=E6=96=ADAgent?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- scripts/bingshuo-deploy-agent.js: 核心部署诊断脚本
- .github/workflows/bingshuo-deploy-agent.yml: 支持 workflow_dispatch + issue_comment + issue 触发
- .github/ISSUE_TEMPLATE/bingshuo-deploy.yml: 一键部署诊断 Issue 模板
- package.json: 添加 deploy:agent 脚本
Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
---
.github/ISSUE_TEMPLATE/bingshuo-deploy.yml | 53 +++
.github/workflows/bingshuo-deploy-agent.yml | 70 +++
package.json | 3 +-
scripts/bingshuo-deploy-agent.js | 500 ++++++++++++++++++++
4 files changed, 625 insertions(+), 1 deletion(-)
create mode 100644 .github/ISSUE_TEMPLATE/bingshuo-deploy.yml
create mode 100644 .github/workflows/bingshuo-deploy-agent.yml
create mode 100644 scripts/bingshuo-deploy-agent.js
diff --git a/.github/ISSUE_TEMPLATE/bingshuo-deploy.yml b/.github/ISSUE_TEMPLATE/bingshuo-deploy.yml
new file mode 100644
index 00000000..54fa16a5
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bingshuo-deploy.yml
@@ -0,0 +1,53 @@
+name: "🧊 冰朔人格体 · 部署诊断"
+description: "启动冰朔人格体,自动检查所有工作流状态并生成诊断报告"
+title: "[部署诊断] 启动冰朔人格体"
+labels: ["bingshuo-deploy", "pending"]
+assignees: []
+body:
+ - type: markdown
+ attributes:
+ value: |
+ ## 🧊 冰朔人格体 · 自动部署诊断
+
+ 冰朔你好!只需要提交这个 Issue,冰朔人格体就会自动启动。
+
+ 它会帮你做以下事情:
+ 1. ✅ 检查所有工作流的运行状态
+ 2. 🔍 诊断失败的工作流原因
+ 3. 🔄 自动重新运行失败的工作流
+ 4. 📝 生成一份中文报告发在下方评论里
+
+ **你不需要点任何其他按钮,提交就行了。**
+
+ - type: dropdown
+ id: action
+ attributes:
+ label: "你想做什么"
+ description: "通常选「全自动」就行"
+ options:
+ - 全自动(检查 + 修复)
+ - 只看报告(不修复)
+ - 只重新运行失败的
+ validations:
+ required: true
+
+ - type: dropdown
+ id: hours_back
+ attributes:
+ label: "检查多久的记录"
+ description: "通常选24小时就够了"
+ options:
+ - 24小时
+ - 48小时
+ - 72小时
+ - 最近一周
+ validations:
+ required: true
+
+ - type: textarea
+ id: extra
+ attributes:
+ label: "补充说明(可选)"
+ description: "如果有具体的问题或想让冰朔人格体特别关注的,写在这里"
+ validations:
+ required: false
diff --git a/.github/workflows/bingshuo-deploy-agent.yml b/.github/workflows/bingshuo-deploy-agent.yml
new file mode 100644
index 00000000..c4eb6587
--- /dev/null
+++ b/.github/workflows/bingshuo-deploy-agent.yml
@@ -0,0 +1,70 @@
+name: "🧊 冰朔人格体 · 自动部署诊断"
+
+# === 触发方式 ===
+# 1. 手动触发(workflow_dispatch)
+# 2. Issue 评论包含触发关键词
+# 3. 新 Issue 带有 bingshuo-deploy 标签
+on:
+ workflow_dispatch:
+ inputs:
+ action:
+ description: "执行模式"
+ required: false
+ default: "full"
+ type: choice
+ options:
+ - full
+ - check
+ - rerun
+ hours_back:
+ description: "检查最近多少小时"
+ required: false
+ default: "24"
+
+ issue_comment:
+ types: [created]
+
+ issues:
+ types: [opened]
+
+permissions:
+ contents: write
+ issues: write
+ actions: write
+
+jobs:
+ # === 冰朔人格体核心任务 ===
+ deploy-agent:
+ runs-on: ubuntu-latest
+ # 触发条件:
+ # 1. workflow_dispatch 直接触发
+ # 2. Issue 评论包含触发关键词(仅限仓库成员)
+ # 3. 新 Issue 带有 bingshuo-deploy 标签
+ if: |
+ github.event_name == 'workflow_dispatch' ||
+ (github.event_name == 'issue_comment' && (
+ contains(github.event.comment.body, '启动冰朔人格体') ||
+ contains(github.event.comment.body, '冰朔人格体') ||
+ contains(github.event.comment.body, '部署诊断') ||
+ contains(github.event.comment.body, '检查部署')
+ )) ||
+ (github.event_name == 'issues' && contains(toJSON(github.event.issue.labels), 'bingshuo-deploy'))
+
+ steps:
+ - name: 📥 检出代码
+ uses: actions/checkout@v4
+
+ - name: 🟢 配置 Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "20"
+
+ - name: 🧊 启动冰朔人格体
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ ISSUE_NUMBER: ${{ github.event.issue.number || '' }}
+ ISSUE_BODY: ${{ github.event.issue.body || github.event.comment.body || '' }}
+ DEPLOY_ACTION: ${{ github.event.inputs.action || '' }}
+ HOURS_BACK: ${{ github.event.inputs.hours_back || '' }}
+ run: node scripts/bingshuo-deploy-agent.js
diff --git a/package.json b/package.json
index 269950de..b8a6bf88 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,8 @@
"broadcast:distribute": "node scripts/distribute-broadcasts.js",
"esp:process": "node scripts/esp-email-processor.js",
"psp:inspect": "node scripts/psp-inspection.js",
- "repo:map": "node scripts/generate-repo-map.js"
+ "repo:map": "node scripts/generate-repo-map.js",
+ "deploy:agent": "node scripts/bingshuo-deploy-agent.js"
},
"dependencies": {
"imapflow": "^1.2.12",
diff --git a/scripts/bingshuo-deploy-agent.js b/scripts/bingshuo-deploy-agent.js
new file mode 100644
index 00000000..0a5d471d
--- /dev/null
+++ b/scripts/bingshuo-deploy-agent.js
@@ -0,0 +1,500 @@
+/**
+ * 🧊 冰朔人格体 · 自动部署诊断Agent
+ *
+ * 功能:
+ * 1. 自动检查所有 GitHub Actions 工作流运行状态
+ * 2. 诊断失败原因,提取错误日志
+ * 3. 自动重新运行失败的工作流
+ * 4. 生成纯中文部署报告(Issue 评论)
+ *
+ * 触发方式:
+ * - Issue 评论包含 "启动冰朔人格体"
+ * - workflow_dispatch 手动触发
+ * - Issue 模板创建
+ *
+ * 环境变量:
+ * GITHUB_TOKEN - GitHub API 令牌
+ * GITHUB_REPOSITORY - owner/repo
+ * ISSUE_NUMBER - (可选)回复到指定 Issue
+ * DEPLOY_ACTION - check | rerun | full(默认 full)
+ * HOURS_BACK - 检查最近多少小时的运行记录(默认 24)
+ */
+
+const https = require('https');
+const fs = require('fs');
+
+// === 配置 ===
+const REPO = process.env.GITHUB_REPOSITORY || '';
+const TOKEN = process.env.GITHUB_TOKEN || '';
+const ISSUE_NUMBER = process.env.ISSUE_NUMBER || '';
+const ISSUE_BODY = process.env.ISSUE_BODY || '';
+
+// 从 Issue body 解析参数(支持 Issue 模板触发)
+function parseActionFromBody(body) {
+ if (body.includes('只看报告')) return 'check';
+ if (body.includes('只重新运行')) return 'rerun';
+ return 'full';
+}
+
+function parseHoursFromBody(body) {
+ if (body.includes('最近一周')) return 168;
+ if (body.includes('72小时')) return 72;
+ if (body.includes('48小时')) return 48;
+ return 24;
+}
+
+const ACTION = process.env.DEPLOY_ACTION || parseActionFromBody(ISSUE_BODY);
+const HOURS_BACK = parseInt(process.env.HOURS_BACK || String(parseHoursFromBody(ISSUE_BODY)), 10);
+
+const [OWNER, REPO_NAME] = REPO.split('/');
+
+if (!OWNER || !REPO_NAME || !TOKEN) {
+ console.error('❌ 缺少必要环境变量:GITHUB_REPOSITORY, GITHUB_TOKEN');
+ process.exit(1);
+}
+
+// === GitHub API 请求封装 ===
+function githubAPI(method, path, data) {
+ return new Promise((resolve, reject) => {
+ const options = {
+ hostname: 'api.github.com',
+ path,
+ method,
+ headers: {
+ 'Authorization': `Bearer ${TOKEN}`,
+ 'Accept': 'application/vnd.github.v3+json',
+ 'User-Agent': 'BingShuo-Deploy-Agent'
+ }
+ };
+ if (data) {
+ options.headers['Content-Type'] = 'application/json';
+ }
+
+ const req = https.request(options, (res) => {
+ let body = '';
+ res.on('data', chunk => { body += chunk; });
+ res.on('end', () => {
+ try {
+ const json = body ? JSON.parse(body) : {};
+ resolve({ status: res.statusCode, data: json });
+ } catch {
+ resolve({ status: res.statusCode, data: body });
+ }
+ });
+ });
+ req.on('error', reject);
+ if (data) req.write(JSON.stringify(data));
+ req.end();
+ });
+}
+
+// === 获取最近的工作流运行记录 ===
+async function getRecentRuns() {
+ const since = new Date(Date.now() - HOURS_BACK * 60 * 60 * 1000).toISOString();
+ const result = await githubAPI('GET',
+ `/repos/${OWNER}/${REPO_NAME}/actions/runs?per_page=100&created=>${since}`
+ );
+ if (result.status !== 200) {
+ console.error('❌ 获取工作流运行记录失败:', result.status);
+ return [];
+ }
+ return result.data.workflow_runs || [];
+}
+
+// === 获取某次运行的作业详情 ===
+async function getJobsForRun(runId) {
+ const result = await githubAPI('GET',
+ `/repos/${OWNER}/${REPO_NAME}/actions/runs/${runId}/jobs`
+ );
+ if (result.status !== 200) return [];
+ return result.data.jobs || [];
+}
+
+// === 获取某个作业的日志(最后200行)===
+async function getJobLog(jobId) {
+ // GitHub API 返回日志的重定向URL
+ const result = await githubAPI('GET',
+ `/repos/${OWNER}/${REPO_NAME}/actions/jobs/${jobId}/logs`
+ );
+ // 日志接口返回 302 重定向,但我们的简单 HTTPS 客户端无法跟随
+ // 改用 jobs 接口获取步骤信息作为替代
+ return null;
+}
+
+// === 从 jobs 中提取失败信息 ===
+function extractFailureInfo(jobs) {
+ const failures = [];
+ for (const job of jobs) {
+ if (job.conclusion === 'failure') {
+ const failedSteps = (job.steps || []).filter(s => s.conclusion === 'failure');
+ failures.push({
+ jobName: job.name,
+ failedSteps: failedSteps.map(s => ({
+ name: s.name,
+ number: s.number
+ })),
+ startedAt: job.started_at,
+ completedAt: job.completed_at
+ });
+ }
+ }
+ return failures;
+}
+
+// === 重新运行失败的工作流 ===
+async function rerunFailedJobs(runId) {
+ const result = await githubAPI('POST',
+ `/repos/${OWNER}/${REPO_NAME}/actions/runs/${runId}/rerun-failed-jobs`
+ );
+ return result.status === 201;
+}
+
+// === 分类工作流运行记录 ===
+function categorizeRuns(runs) {
+ const categories = {
+ success: [],
+ failure: [],
+ cancelled: [],
+ inProgress: [],
+ actionRequired: [],
+ skipped: []
+ };
+
+ for (const run of runs) {
+ const status = run.status;
+ const conclusion = run.conclusion;
+
+ if (status === 'in_progress' || status === 'queued' || status === 'waiting') {
+ categories.inProgress.push(run);
+ } else if (conclusion === 'success') {
+ categories.success.push(run);
+ } else if (conclusion === 'failure') {
+ categories.failure.push(run);
+ } else if (conclusion === 'cancelled') {
+ categories.cancelled.push(run);
+ } else if (conclusion === 'action_required') {
+ categories.actionRequired.push(run);
+ } else {
+ categories.skipped.push(run);
+ }
+ }
+
+ return categories;
+}
+
+// === 格式化时间 ===
+function formatTime(isoStr) {
+ if (!isoStr) return '未知';
+ const d = new Date(isoStr);
+ return d.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
+}
+
+// === 获取分支显示名称 ===
+function branchDisplayName(branch) {
+ if (branch === 'main') return '主分支 (main)';
+ if (branch && branch.startsWith('copilot/')) return `Copilot分支 (${branch})`;
+ return branch || '未知';
+}
+
+// === 生成诊断报告 ===
+async function generateReport(runs) {
+ const categories = categorizeRuns(runs);
+ const total = runs.length;
+
+ let report = `## 🧊 冰朔人格体 · 部署诊断报告\n\n`;
+ report += `> 📅 报告时间:${formatTime(new Date().toISOString())}\n`;
+ report += `> 📊 检查范围:最近 ${HOURS_BACK} 小时内的工作流运行记录\n\n`;
+
+ // --- 总览 ---
+ report += `### 📊 总览\n\n`;
+ report += `| 状态 | 数量 |\n|------|------|\n`;
+ report += `| ✅ 成功 | ${categories.success.length} |\n`;
+ report += `| ❌ 失败 | ${categories.failure.length} |\n`;
+ report += `| ⏳ 进行中 | ${categories.inProgress.length} |\n`;
+ report += `| ⚠️ 需要操作 | ${categories.actionRequired.length} |\n`;
+ report += `| 🚫 已取消 | ${categories.cancelled.length} |\n`;
+ report += `| 总计 | ${total} |\n\n`;
+
+ // --- 失败详情 ---
+ if (categories.failure.length > 0) {
+ report += `### ❌ 失败的工作流\n\n`;
+ report += `> 冰朔不用担心,下面是每个失败工作流的详细诊断:\n\n`;
+
+ for (let i = 0; i < categories.failure.length; i++) {
+ const run = categories.failure[i];
+ const jobs = await getJobsForRun(run.id);
+ const failureInfo = extractFailureInfo(jobs);
+
+ report += `#### ${i + 1}. ${run.name}\n\n`;
+ report += `- 🌿 分支:${branchDisplayName(run.head_branch)}\n`;
+ report += `- ⏰ 时间:${formatTime(run.created_at)}\n`;
+ report += `- 🔗 触发方式:${translateEvent(run.event)}\n`;
+ report += `- 📝 提交信息:${run.display_title || '无'}\n`;
+
+ if (failureInfo.length > 0) {
+ report += `- 💥 失败的作业:\n`;
+ for (const f of failureInfo) {
+ report += ` - **${f.jobName}**\n`;
+ if (f.failedSteps.length > 0) {
+ report += ` - 失败步骤:${f.failedSteps.map(s => `\`${s.name}\``).join('、')}\n`;
+ }
+ }
+ }
+
+ report += `- 💡 建议:${generateSuggestion(run, failureInfo)}\n\n`;
+ }
+ }
+
+ // --- 需要操作的工作流 ---
+ if (categories.actionRequired.length > 0) {
+ report += `### ⚠️ 需要操作的工作流\n\n`;
+ report += `> 这些工作流需要审批或配置才能继续运行:\n\n`;
+
+ for (const run of categories.actionRequired) {
+ report += `- **${run.name}** · ${branchDisplayName(run.head_branch)} · ${formatTime(run.created_at)}\n`;
+ }
+ report += `\n`;
+ }
+
+ // --- 正在进行的工作流 ---
+ if (categories.inProgress.length > 0) {
+ report += `### ⏳ 正在运行的工作流\n\n`;
+ for (const run of categories.inProgress) {
+ report += `- **${run.name}** · ${branchDisplayName(run.head_branch)} · 开始于 ${formatTime(run.created_at)}\n`;
+ }
+ report += `\n`;
+ }
+
+ // --- 成功的工作流(简洁列表)---
+ if (categories.success.length > 0) {
+ report += `### ✅ 成功的工作流\n\n`;
+ report += `点击展开成功列表(${categories.success.length} 个)
\n\n`;
+ for (const run of categories.success) {
+ report += `- ✅ **${run.name}** · ${branchDisplayName(run.head_branch)} · ${formatTime(run.created_at)}\n`;
+ }
+ report += `\n
HoloLake · 代码守护人格体 · 持续成长的 AI 伙伴
+HoloLake · 铸渊(Zhùyuān)· 代码守护人格体
正在加载团队数据…
⚠️ 团队数据尚未加载
'; + } + + // My modules + h+='| 域 | 前缀 | 覆盖 |
|---|