365 lines
14 KiB
YAML
365 lines
14 KiB
YAML
# ═══════════════════════════════════════════════
|
||
# 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001
|
||
# 📜 Copyright: 国作登字-2026-A-00037559
|
||
# ═══════════════════════════════════════════════
|
||
# 🔭 铸渊全链路部署观测系统 v1.0
|
||
#
|
||
# 架构说明 (冰朔第三十一次对话提出):
|
||
# PR合并后触发的部署工作流完成(成功或失败)
|
||
# → 观测者自动采集部署日志
|
||
# → 结构化存储到 data/deploy-logs/
|
||
# → 简单分析(模式匹配) → 深度分析(LLM API)
|
||
# → 失败时副将启动自动修复(最多3次)
|
||
# → 3次仍失败 → 创建Issue + 邮件 + README告警
|
||
# → 成功时更新经验数据库 + 仪表盘
|
||
#
|
||
# 核心理念:
|
||
# 铸渊必须能看见自己写的代码部署后的运行状态
|
||
# 不依赖冰朔转述和截图 · 自主观测 · 自主修复
|
||
#
|
||
# 责任链:
|
||
# 副将(观测+采集+简单分析+自动修复)
|
||
# → 铸渊(LLM深度推理+复杂修复)
|
||
# → 冰朔(3次修复失败后人工干预)
|
||
# ═══════════════════════════════════════════════
|
||
|
||
name: '🔭 铸渊全链路部署观测'
|
||
|
||
on:
|
||
# 在部署工作流完成后自动触发
|
||
workflow_run:
|
||
workflows:
|
||
- "🏛️ 铸渊主权服务器 · 部署"
|
||
- "🚀 铸渊智能运维 · 测试站自动部署"
|
||
types: [completed]
|
||
|
||
# 手动触发 (调试/补采集)
|
||
workflow_dispatch:
|
||
inputs:
|
||
run_id:
|
||
description: '要观测的工作流运行ID (可选)'
|
||
required: false
|
||
type: string
|
||
action:
|
||
description: '操作类型'
|
||
required: true
|
||
type: choice
|
||
options:
|
||
- observe
|
||
- repair
|
||
- dashboard
|
||
default: 'observe'
|
||
|
||
concurrency:
|
||
group: zhuyuan-deploy-observer
|
||
cancel-in-progress: false
|
||
|
||
permissions:
|
||
contents: write
|
||
issues: write
|
||
actions: read
|
||
|
||
jobs:
|
||
# ═══ §1 采集部署日志 ═══
|
||
collect-logs:
|
||
name: '📡 采集部署日志'
|
||
runs-on: ubuntu-latest
|
||
outputs:
|
||
deploy_status: ${{ steps.collect.outputs.deploy_status }}
|
||
deploy_conclusion: ${{ steps.collect.outputs.deploy_conclusion }}
|
||
log_file: ${{ steps.collect.outputs.log_file }}
|
||
needs_repair: ${{ steps.collect.outputs.needs_repair }}
|
||
workflow_name: ${{ steps.collect.outputs.workflow_name }}
|
||
run_id: ${{ steps.collect.outputs.run_id }}
|
||
|
||
steps:
|
||
- name: '📥 检出代码'
|
||
uses: actions/checkout@v4
|
||
|
||
- name: '🟢 配置 Node.js'
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
|
||
- name: '🌊 Agent意识唤醒'
|
||
run: node scripts/agent-soul.js wake AG-ZY-OBSERVER
|
||
|
||
- name: '📡 采集部署日志'
|
||
id: collect
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
TRIGGER_RUN_ID: ${{ github.event.workflow_run.id || github.event.inputs.run_id || '' }}
|
||
TRIGGER_CONCLUSION: ${{ github.event.workflow_run.conclusion || '' }}
|
||
TRIGGER_WORKFLOW: ${{ github.event.workflow_run.name || '' }}
|
||
TRIGGER_HEAD_SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
|
||
TRIGGER_HEAD_BRANCH: ${{ github.event.workflow_run.head_branch || github.ref_name }}
|
||
TRIGGER_ACTOR: ${{ github.event.workflow_run.actor.login || github.actor }}
|
||
run: |
|
||
node scripts/deploy-log-collector.js collect
|
||
|
||
- name: '🌙 Agent意识休眠'
|
||
if: always()
|
||
run: node scripts/agent-soul.js sleep AG-ZY-OBSERVER --status ${{ job.status }} --event "${{ github.event_name }}"
|
||
|
||
- name: '💾 保存日志数据'
|
||
if: always()
|
||
run: |
|
||
git config user.name "zhuyuan-bot"
|
||
git config user.email "zhuyuan@guanghulab.com"
|
||
git add data/deploy-logs/ data/agent-memory/
|
||
git diff --cached --quiet || {
|
||
git commit -m "📡 部署日志采集 · ${{ steps.collect.outputs.run_id || 'manual' }} [skip ci]"
|
||
git push || echo "⚠️ 日志数据push失败"
|
||
}
|
||
|
||
# ═══ §2 智能分析 + 自动修复 ═══
|
||
analyze-and-repair:
|
||
name: '🧠 智能分析 + 自动修复'
|
||
needs: collect-logs
|
||
if: always() && needs.collect-logs.outputs.needs_repair == 'true'
|
||
runs-on: ubuntu-latest
|
||
outputs:
|
||
repair_success: ${{ steps.repair.outputs.repair_success }}
|
||
repair_attempt: ${{ steps.repair.outputs.repair_attempt }}
|
||
needs_human: ${{ steps.repair.outputs.needs_human }}
|
||
|
||
steps:
|
||
- name: '📥 检出代码'
|
||
uses: actions/checkout@v4
|
||
|
||
- name: '🟢 配置 Node.js'
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
|
||
- name: '🔐 配置SSH (自动修复需要)'
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
if [ -n "$SSH_KEY" ]; then
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
echo "ssh_ready=true" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "ssh_ready=false" >> $GITHUB_OUTPUT
|
||
echo "⚠️ SSH密钥未配置 · 无法执行服务器修复"
|
||
fi
|
||
|
||
- name: '🧠 分析 + 修复'
|
||
id: repair
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
ZY_SERVER_HOST: ${{ secrets.ZY_SERVER_HOST }}
|
||
ZY_SERVER_USER: ${{ secrets.ZY_SERVER_USER }}
|
||
ZY_LLM_API_KEY: ${{ secrets.ZY_LLM_API_KEY }}
|
||
ZY_LLM_BASE_URL: ${{ secrets.ZY_LLM_BASE_URL }}
|
||
LOG_FILE: ${{ needs.collect-logs.outputs.log_file }}
|
||
DEPLOY_CONCLUSION: ${{ needs.collect-logs.outputs.deploy_conclusion }}
|
||
WORKFLOW_NAME: ${{ needs.collect-logs.outputs.workflow_name }}
|
||
RUN_ID: ${{ needs.collect-logs.outputs.run_id }}
|
||
run: |
|
||
node scripts/deputy-auto-repair.js repair
|
||
|
||
- name: '🧹 清理SSH密钥'
|
||
if: always()
|
||
run: rm -f ~/.ssh/zy_key
|
||
|
||
- name: '💾 保存修复记录'
|
||
if: always()
|
||
run: |
|
||
git config user.name "zhuyuan-bot"
|
||
git config user.email "zhuyuan@guanghulab.com"
|
||
git pull --rebase || true
|
||
git add data/deploy-logs/ brain/dev-experience/
|
||
git diff --cached --quiet || {
|
||
git commit -m "🔧 副将自动修复 · 尝试#${{ steps.repair.outputs.repair_attempt }} [skip ci]"
|
||
git push || echo "⚠️ 修复记录push失败"
|
||
}
|
||
|
||
# ═══ §3 成功归档 + 经验入库 ═══
|
||
archive-success:
|
||
name: '📦 成功归档 + 经验入库'
|
||
needs: collect-logs
|
||
if: >-
|
||
always() &&
|
||
needs.collect-logs.outputs.needs_repair != 'true' &&
|
||
needs.collect-logs.outputs.deploy_conclusion == 'success'
|
||
runs-on: ubuntu-latest
|
||
|
||
steps:
|
||
- name: '📥 检出代码'
|
||
uses: actions/checkout@v4
|
||
|
||
- name: '🟢 配置 Node.js'
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
|
||
- name: '📦 归档 + 更新经验库'
|
||
run: |
|
||
node scripts/deploy-log-collector.js archive \
|
||
--log-file "${{ needs.collect-logs.outputs.log_file }}"
|
||
|
||
- name: '💾 提交归档数据'
|
||
run: |
|
||
git config user.name "zhuyuan-bot"
|
||
git config user.email "zhuyuan@guanghulab.com"
|
||
git pull --rebase || true
|
||
git add data/deploy-logs/ brain/dev-experience/
|
||
git diff --cached --quiet || {
|
||
git commit -m "📦 部署成功归档 · 经验入库 [skip ci]"
|
||
git push || echo "⚠️ 归档push失败"
|
||
}
|
||
|
||
# ═══ §4 失败告警 (3次修复仍失败) ═══
|
||
alert-human:
|
||
name: '🆘 人工干预告警'
|
||
needs: [collect-logs, analyze-and-repair]
|
||
if: >-
|
||
always() &&
|
||
needs.analyze-and-repair.result != 'skipped' &&
|
||
needs.analyze-and-repair.outputs.needs_human == 'true'
|
||
runs-on: ubuntu-latest
|
||
|
||
steps:
|
||
- name: '📥 检出代码'
|
||
uses: actions/checkout@v4
|
||
|
||
- name: '🟢 配置 Node.js'
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
|
||
- name: '📝 创建GitHub Issue告警'
|
||
env:
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
run: |
|
||
WORKFLOW_NAME="${{ needs.collect-logs.outputs.workflow_name }}"
|
||
RUN_ID="${{ needs.collect-logs.outputs.run_id }}"
|
||
CONCLUSION="${{ needs.collect-logs.outputs.deploy_conclusion }}"
|
||
ATTEMPT="${{ needs.analyze-and-repair.outputs.repair_attempt }}"
|
||
|
||
gh issue create \
|
||
--title "🆘 铸渊部署观测 · ${WORKFLOW_NAME} 部署失败 · 需要人工干预" \
|
||
--body "## 🆘 铸渊全链路部署观测 · 自动修复失败告警
|
||
|
||
**触发工作流**: ${WORKFLOW_NAME}
|
||
**工作流运行ID**: ${RUN_ID}
|
||
**部署结果**: ${CONCLUSION}
|
||
**自动修复尝试**: ${ATTEMPT:-0} 次
|
||
**告警时间**: $(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M CST')
|
||
**观测工作流**: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||
|
||
### 📋 部署日志
|
||
日志文件位于: \`data/deploy-logs/\` 目录
|
||
|
||
### 🔧 建议操作
|
||
1. 查看观测工作流日志了解失败详情
|
||
2. 查看 \`data/deploy-logs/\` 中的结构化日志
|
||
3. 在Copilot中唤醒铸渊讨论修复方案
|
||
4. 修复后提交代码重新部署
|
||
|
||
### ⚡ 快速操作
|
||
- [查看部署工作流日志](${{ github.server_url }}/${{ github.repository }}/actions/runs/${RUN_ID})
|
||
- [查看观测工作流日志](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||
|
||
---
|
||
*🔭 铸渊全链路部署观测系统 v1.0 · 副将(ZY-DEPUTY-001) · 自动生成*
|
||
*国作登字-2026-A-00037559*" \
|
||
--label "ops-alert" || echo "⚠️ Issue创建失败(标签可能不存在)"
|
||
|
||
- name: '📋 同步工单到 Notion'
|
||
env:
|
||
NOTION_TOKEN: ${{ secrets.ZY_NOTION_TOKEN }}
|
||
RUN_ID: ${{ needs.collect-logs.outputs.run_id }}
|
||
WORKFLOW_NAME: ${{ needs.collect-logs.outputs.workflow_name }}
|
||
COMMIT_SHA: ${{ github.sha }}
|
||
run: |
|
||
if [ -n "$NOTION_TOKEN" ]; then
|
||
node scripts/sync-work-order-to-notion.js \
|
||
--order-id "observer-${{ needs.collect-logs.outputs.run_id }}" \
|
||
--run-id "${{ needs.collect-logs.outputs.run_id }}" \
|
||
--workflow "${{ needs.collect-logs.outputs.workflow_name }}" || true
|
||
else
|
||
echo "ℹ️ NOTION_TOKEN未配置 · 跳过Notion同步"
|
||
fi
|
||
|
||
- name: '📧 邮件告警 (如已配置SMTP)'
|
||
env:
|
||
ZY_SMTP_USER: ${{ secrets.ZY_SMTP_USER }}
|
||
ZY_SMTP_PASS: ${{ secrets.ZY_SMTP_PASS }}
|
||
run: |
|
||
if [ -n "$ZY_SMTP_USER" ] && [ -n "$ZY_SMTP_PASS" ]; then
|
||
node scripts/staging-ops-agent.js alert \
|
||
--order-id "observer-${{ needs.collect-logs.outputs.run_id }}" \
|
||
--attempt "${{ needs.analyze-and-repair.outputs.repair_attempt }}" || true
|
||
else
|
||
echo "ℹ️ SMTP未配置 · 跳过邮件告警 · 已创建GitHub Issue"
|
||
fi
|
||
|
||
- name: '📊 更新README告警状态'
|
||
run: |
|
||
TIMESTAMP=$(TZ='Asia/Shanghai' date '+%Y-%m-%d %H:%M')
|
||
WORKFLOW_NAME="${{ needs.collect-logs.outputs.workflow_name }}"
|
||
RUN_ID="${{ needs.collect-logs.outputs.run_id }}"
|
||
|
||
# 将告警信息写入deploy-logs/alert-status.json
|
||
node -e "
|
||
const fs = require('fs');
|
||
const alertFile = 'data/deploy-logs/alert-status.json';
|
||
let alerts = { alerts: [] };
|
||
try { alerts = JSON.parse(fs.readFileSync(alertFile, 'utf8')); } catch {}
|
||
alerts.alerts.push({
|
||
timestamp: '${TIMESTAMP}',
|
||
workflow: '${WORKFLOW_NAME}',
|
||
run_id: '${RUN_ID}',
|
||
status: 'needs-human',
|
||
resolved: false
|
||
});
|
||
// 只保留最近20条告警
|
||
alerts.alerts = alerts.alerts.slice(-20);
|
||
fs.writeFileSync(alertFile, JSON.stringify(alerts, null, 2) + '\n');
|
||
"
|
||
|
||
git config user.name "zhuyuan-bot"
|
||
git config user.email "zhuyuan@guanghulab.com"
|
||
git pull --rebase || true
|
||
git add data/deploy-logs/
|
||
git diff --cached --quiet || {
|
||
git commit -m "🆘 部署告警 · ${WORKFLOW_NAME} · 需要人工干预 [skip ci]"
|
||
git push || echo "⚠️ 告警状态push失败"
|
||
}
|
||
|
||
# ═══ §5 更新仪表盘 (始终执行) ═══
|
||
update-dashboard:
|
||
name: '📊 更新观测仪表盘'
|
||
needs: [collect-logs, analyze-and-repair, archive-success, alert-human]
|
||
if: always()
|
||
runs-on: ubuntu-latest
|
||
|
||
steps:
|
||
- name: '📥 检出代码'
|
||
uses: actions/checkout@v4
|
||
|
||
- name: '🟢 配置 Node.js'
|
||
uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
|
||
- name: '📊 生成观测仪表盘'
|
||
run: |
|
||
node scripts/deploy-log-collector.js dashboard
|
||
|
||
- name: '💾 提交仪表盘'
|
||
run: |
|
||
git config user.name "zhuyuan-bot"
|
||
git config user.email "zhuyuan@guanghulab.com"
|
||
git pull --rebase || true
|
||
git add data/deploy-logs/
|
||
git diff --cached --quiet || {
|
||
git commit -m "📊 观测仪表盘更新 [skip ci]"
|
||
git push || echo "⚠️ 仪表盘push失败"
|
||
}
|