guanghulab/.github/skyeye-core/skyeye-repair.sh
2026-05-10 13:12:44 +08:00

167 lines
5.2 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

#!/usr/bin/env bash
# ━━━ 天眼自修复脚本 · SkyEye Repair v4.0 ━━━
# 尝试修复失联的兄弟 workflow
# 修复失败达到阈值时自动升级到创建 Issue 告警
# 版权:国作登字-2026-A-00037559 · 冰朔ICE-GL∞
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
set -euo pipefail
AUTO_FIX="false"
MAX_RETRIES=3
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo '.')"
EARTH_STATUS_FILE="${REPO_ROOT}/signal-log/skyeye-earth-status.json"
while [[ $# -gt 0 ]]; do
case $1 in
--auto-fix) AUTO_FIX="$2"; shift 2 ;;
--max-retries) MAX_RETRIES="$2"; shift 2 ;;
*) shift ;;
esac
done
NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
OWNER="${GITHUB_REPOSITORY_OWNER:-qinfendebingshuo}"
REPO_NAME="${GITHUB_REPOSITORY##*/}"
REPO_NAME="${REPO_NAME:-guanghulab}"
CURRENT_AGENT="${GITHUB_WORKFLOW:-unknown}"
echo "===== 天眼自修复启动 · auto_fix=${AUTO_FIX} · max_retries=${MAX_RETRIES} ====="
if [[ "$AUTO_FIX" != "true" ]]; then
echo " 自动修复未启用,跳过"
exit 0
fi
# Read earth status to find dead workflows
if [[ ! -f "$EARTH_STATUS_FILE" ]]; then
echo "⚠️ 地球状态文件不存在,跳过修复"
exit 0
fi
TOKEN="${GITHUB_TOKEN:-${GH_TOKEN:-}}"
if [[ -z "$TOKEN" ]]; then
echo "⚠️ 无 GitHub Token无法执行自动修复"
exit 0
fi
SEVERITY=$(node -e "
const d = JSON.parse(require('fs').readFileSync('${EARTH_STATUS_FILE}', 'utf8'));
console.log(d.health || 'unknown');
" 2>/dev/null || echo "unknown")
DEAD_COUNT=$(node -e "
const d = JSON.parse(require('fs').readFileSync('${EARTH_STATUS_FILE}', 'utf8'));
console.log(d.dead_eyes || 0);
" 2>/dev/null || echo "0")
echo "🔍 当前地球状态: ${SEVERITY} | 失联: ${DEAD_COUNT}"
REPAIR_LOG="[]"
REPAIR_COUNT=0
REPAIR_SUCCESS=0
REPAIR_FAILED=0
# Level 1: Try to re-run failed workflows
if [[ "$DEAD_COUNT" -gt 0 ]] && command -v node &>/dev/null; then
echo "🔧 Level 1: 尝试重新触发失败的 workflow..."
DEAD_LIST=$(node -e "
const d = JSON.parse(require('fs').readFileSync('${EARTH_STATUS_FILE}', 'utf8'));
console.log(JSON.stringify(d.dead_list || []));
" 2>/dev/null || echo "[]")
# Note: Re-running workflows requires specific run IDs and permissions
# This is a best-effort attempt
echo "📋 失联列表: ${DEAD_LIST}"
echo " Level 1 修复需要人工介入(重新运行失败的 workflow"
REPAIR_COUNT=$((REPAIR_COUNT + 1))
fi
# Level 3: Create GitHub Issue for critical/black severity
if [[ "$SEVERITY" == "red" ]] || [[ "$SEVERITY" == "black" ]]; then
echo "🚨 Level 3: 严重告警 → 创建 GitHub Issue"
ISSUE_TITLE="🚨 天眼地球层告警:${SEVERITY} 级别 · 需要人类介入"
# Write issue body to temp file to avoid shell quoting issues
ISSUE_BODY_FILE=$(mktemp)
cat > "$ISSUE_BODY_FILE" <<EOFBODY
## 天眼地球层 v4.0 自动告警
**告警时间**: ${NOW}
**告警级别**: ${SEVERITY}
**发现者**: ${CURRENT_AGENT}
**失联 workflow 数**: ${DEAD_COUNT}
### 失联列表
\`\`\`json
${DEAD_LIST:-[]}
\`\`\`
### 可能原因
- Secret/Token 过期GUANGHU_TOKEN、NOTION_TOKEN、PAT_TOKEN
- 分支保护规则阻止直接推送GH006
- Google Drive OAuth Token 过期
- API 配额耗尽
### 需要冰朔操作
1. 检查 Settings → Secrets and variables → Actions
2. 更新过期的 Secret/Token
3. 检查分支保护规则是否正确
4. 手动重新运行失败的 workflow
---
*此 Issue 由天眼地球层 v4.0 自动创建 · ${CURRENT_AGENT}*
EOFBODY
# Create issue via GitHub API using temp file for body
ISSUE_PAYLOAD_FILE=$(mktemp)
node -e "
const fs = require('fs');
const body = fs.readFileSync('${ISSUE_BODY_FILE}', 'utf8');
const payload = JSON.stringify({
title: '${ISSUE_TITLE}',
body: body,
labels: ['skyeye-alert', 'urgent']
});
fs.writeFileSync('${ISSUE_PAYLOAD_FILE}', payload);
" 2>/dev/null
ISSUE_RESULT=$(curl -s -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Accept: application/vnd.github.v3+json" \
-H "Content-Type: application/json" \
"https://api.github.com/repos/${OWNER}/${REPO_NAME}/issues" \
-d "@${ISSUE_PAYLOAD_FILE}" 2>/dev/null || echo '{"message":"failed"}')
rm -f "$ISSUE_BODY_FILE" "$ISSUE_PAYLOAD_FILE"
ISSUE_URL=$(echo "$ISSUE_RESULT" | node -e "
const d = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
console.log(d.html_url || 'creation failed');
" 2>/dev/null || echo "creation failed")
echo "📝 Issue 已创建: ${ISSUE_URL}"
REPAIR_LOG="[{\"time\":\"${NOW}\",\"repairer\":\"${CURRENT_AGENT}\",\"action\":\"create-issue\",\"result\":\"${ISSUE_URL}\"}]"
fi
# Update earth status with repair log
if [[ -f "$EARTH_STATUS_FILE" ]] && command -v node &>/dev/null; then
node -e "
const fs = require('fs');
const d = JSON.parse(fs.readFileSync('${EARTH_STATUS_FILE}', 'utf8'));
d.repair_log = ${REPAIR_LOG};
d.last_repair = '${NOW}';
d.last_repairer = '${CURRENT_AGENT}';
fs.writeFileSync('${EARTH_STATUS_FILE}', JSON.stringify(d, null, 2));
" 2>/dev/null || true
fi
echo "===== 天眼自修复完成 · 修复尝试: ${REPAIR_COUNT} ====="