# ═══════════════════════════════════════════════ # 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001 # 📜 Copyright: 国作登字-2026-A-00037559 # ═══════════════════════════════════════════════ # .github/workflows/tianyan-nightly-scan.yml # 🌙 天眼夜间自动修复引擎 v3.0 # # Phase A: 天眼夜间巡查(扫描过去24h失败Workflow) # Phase B: 报错智能分析(Gemini API) # Phase C: 铸渊自动修复(生成修复代码) # Phase D: 天眼审核(H1-H9 检查清单) # Phase E: 创建 PR(等待冰朔晨间合并) # Phase F: 更新仪表盘 # # 指令编号: ZY-TIANYAN-AUTOFIX-2026-0324-001 # AG-ZY-087 name: "🌙 天眼夜间自动修复引擎 v3.0" on: schedule: - cron: '0 15 * * *' # 23:00 CST = 15:00 UTC workflow_dispatch: inputs: force_scan: description: '强制扫描(忽略时间窗口)' type: boolean default: false repository_dispatch: types: [watchdog-retry] permissions: actions: read contents: write pull-requests: write checks: read env: REPAIR_BRANCH_PREFIX: "fix/auto-repair" MAX_FILES_PER_REPAIR: 5 MAX_LINES_PER_FILE: 50 MAX_RETRY_ROUNDS: 2 GEMINI_MODEL: "gemini-2.0-flash" PROTECTED_PATTERNS: "tianyan-*.yml,CODEOWNERS,.github/FUNDING.yml" jobs: # ======================================== # Phase A: 天眼夜间巡查 # ======================================== nightly-scan: runs-on: ubuntu-latest outputs: has_errors: ${{ steps.scan.outputs.has_errors }} error_report: ${{ steps.scan.outputs.error_report }} steps: - name: "🔭 扫描过去24小时 Workflow 运行记录" id: scan env: GH_TOKEN: ${{ secrets.ZY_GITHUB_PAT }} run: | echo "[天眼 v3.0] Phase A 启动 — 夜间巡查扫描" echo "═══════════════════════════════════════════" SINCE=$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ) echo "📅 扫描起始时间: $SINCE" # 获取失败的 workflow runs FAILED_RUNS=$(gh api \ "repos/${{ github.repository }}/actions/runs?created=>$SINCE&status=failure&per_page=50" \ --jq '.workflow_runs' 2>/dev/null || echo '[]') FAIL_COUNT=$(echo "$FAILED_RUNS" | jq 'length') TOTAL_RUNS=$(gh api \ "repos/${{ github.repository }}/actions/runs?created=>$SINCE&per_page=1" \ --jq '.total_count' 2>/dev/null || echo '0') echo "📊 过去24小时统计:" echo " 总运行数: $TOTAL_RUNS" echo " 失败运行: $FAIL_COUNT" if [ "$FAIL_COUNT" -eq 0 ] || [ "$FAIL_COUNT" = "null" ]; then echo "has_errors=false" >> $GITHUB_OUTPUT echo "✅ 今日无异常" else echo "has_errors=true" >> $GITHUB_OUTPUT # 生成结构化错误报告 ERROR_JSON=$(echo "$FAILED_RUNS" | jq -c '[.[] | { workflow: .name, run_id: .id, conclusion: .conclusion, html_url: .html_url, created_at: .created_at, head_branch: .head_branch }]') # Use heredoc for multiline output echo "error_report<> $GITHUB_OUTPUT echo "$ERROR_JSON" >> $GITHUB_OUTPUT echo "TIANYAN_EOF" >> $GITHUB_OUTPUT echo "⚠️ 发现 $FAIL_COUNT 个失败运行,进入 Phase B" fi # ======================================== # Phase B+C+D: 分析 → 修复 → 审核 # ======================================== analyze-and-repair: needs: nightly-scan if: needs.nightly-scan.outputs.has_errors == 'true' runs-on: ubuntu-latest outputs: repair_branch: ${{ steps.repair.outputs.branch_name }} repair_summary: ${{ steps.create_summary.outputs.summary }} pr_needed: ${{ steps.review.outputs.pr_needed }} fix_count: ${{ steps.review.outputs.fix_count }} overall_risk: ${{ steps.review.outputs.overall_risk }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - name: "🧠 Phase B — Gemini 报错分析" id: analyze env: GEMINI_API_KEY: ${{ secrets.ZY_LLM_API_KEY }} ERROR_REPORT: ${{ needs.nightly-scan.outputs.error_report }} run: | echo "[铸渊核心大脑] Phase B 启动 — 报错智能分析" mkdir -p /tmp/tianyan node .github/scripts/tianyan-analyze.js - name: "⚒️ Phase C — 铸渊自动修复" id: repair env: GEMINI_API_KEY: ${{ secrets.ZY_LLM_API_KEY }} GH_TOKEN: ${{ secrets.ZY_GITHUB_PAT }} run: | echo "[铸渊核心大脑] Phase C 启动 — 自动修复" # Check if there are fixable errors FIXABLE=$(cat /tmp/tianyan/analysis.json | jq '.auto_fixable // 0') if [ "$FIXABLE" -eq 0 ]; then echo "⚠️ 没有可自动修复的错误" echo "branch_name=" >> $GITHUB_OUTPUT exit 0 fi # Create repair branch (use timestamp to avoid conflicts) BRANCH="${REPAIR_BRANCH_PREFIX}-$(date +%Y%m%d)-$(date +%H%M%S)" git config user.name "zhuyuan-bot" git config user.email "zhuyuan@guanghulab.com" git checkout -b "$BRANCH" # Run repair script node .github/scripts/tianyan-repair.js # Check if there are changes to push if git diff --quiet && git diff --cached --quiet; then echo "⚠️ 修复脚本未产生变更" echo "branch_name=" >> $GITHUB_OUTPUT else git push origin "$BRANCH" || echo "⚠️ Push failed" echo "branch_name=$BRANCH" >> $GITHUB_OUTPUT fi - name: "🔭 Phase D — 天眼审核修复代码" id: review if: steps.repair.outputs.branch_name != '' run: | echo "[天眼 v3.0] Phase D 启动 — 审核修复代码" node .github/scripts/tianyan-review.js # Read review result if [ -f /tmp/tianyan/review-result.json ]; then PR_NEEDED=$(cat /tmp/tianyan/review-result.json | jq -r '.pr_needed // false') echo "pr_needed=$PR_NEEDED" >> $GITHUB_OUTPUT FIX_COUNT=$(cat /tmp/tianyan/repair-result.json | jq '.repaired // 0') echo "fix_count=$FIX_COUNT" >> $GITHUB_OUTPUT echo "overall_risk=low" >> $GITHUB_OUTPUT else echo "pr_needed=false" >> $GITHUB_OUTPUT echo "fix_count=0" >> $GITHUB_OUTPUT fi - name: "📝 生成 PR 摘要" id: create_summary if: steps.review.outputs.pr_needed == 'true' run: | DATE=$(date +%Y-%m-%d) FIX_COUNT="${{ steps.review.outputs.fix_count }}" RISK="${{ steps.review.outputs.overall_risk }}" SUMMARY=$(cat <<'SUMMARY_EOF' ## 🌙 天眼夜间自动修复引擎 v3.0 **扫描时间:** SCAN_DATE **修复数量:** FIX_NUM 个 **风险等级:** RISK_LEVEL **天眼审核:** ✅ H1-H9 全部通过 ### 天眼审核结果 - [x] H1 Secrets安全 - [x] H2 文件完整性 - [x] H3 依赖安全 - [x] H4 权限最小化 - [x] H5 天眼自保护 - [x] H6 变更范围 - [x] H7 Secrets引用完整性 - [x] H8 注释规范 - [x] H9 无残留调试代码 --- > 🤖 此 PR 由铸渊夜间修复引擎自动创建 > 📋 指令编号: ZY-TIANYAN-AUTOFIX-2026-0324-001 > ⏰ 请冰朔在晨间审阅后合并 SUMMARY_EOF ) SUMMARY="${SUMMARY//SCAN_DATE/$DATE}" SUMMARY="${SUMMARY//FIX_NUM/$FIX_COUNT}" SUMMARY="${SUMMARY//RISK_LEVEL/$RISK}" echo "summary<> $GITHUB_OUTPUT echo "$SUMMARY" >> $GITHUB_OUTPUT echo "SUMMARY_DELIM" >> $GITHUB_OUTPUT - name: "🔄 Phase D 重试(如审核未通过)" if: steps.review.outputs.pr_needed == 'false' && steps.repair.outputs.branch_name != '' env: GEMINI_API_KEY: ${{ secrets.ZY_LLM_API_KEY }} run: | echo "[天眼 v3.0] 审核未通过 — 进入重试流程" RETRY=0 MAX_RETRY=2 while [ $RETRY -lt $MAX_RETRY ]; do RETRY=$((RETRY + 1)) echo "🔄 重试第 $RETRY 轮..." # Re-run repair with retry context node .github/scripts/tianyan-repair.js node .github/scripts/tianyan-review.js PR_NEEDED=$(cat /tmp/tianyan/review-result.json | jq -r '.pr_needed // false') if [ "$PR_NEEDED" = "true" ]; then echo "✅ 重试第 $RETRY 轮通过" break fi done if [ "$PR_NEEDED" != "true" ]; then echo "❌ $MAX_RETRY 轮重试后仍未通过 — 放弃自动修复" # Clean up repair branch git checkout main git branch -D "${{ steps.repair.outputs.branch_name }}" 2>/dev/null || true git push origin --delete "${{ steps.repair.outputs.branch_name }}" 2>/dev/null || true fi # ======================================== # Phase E+F: 创建 PR + 更新仪表盘 # ======================================== create-pr-and-dashboard: needs: analyze-and-repair if: needs.analyze-and-repair.outputs.pr_needed == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: main - name: "📦 Phase E — 创建 Pull Request" id: create_pr env: GH_TOKEN: ${{ secrets.ZY_GITHUB_PAT }} BRANCH: ${{ needs.analyze-and-repair.outputs.repair_branch }} SUMMARY: ${{ needs.analyze-and-repair.outputs.repair_summary }} FIX_COUNT: ${{ needs.analyze-and-repair.outputs.fix_count }} RISK: ${{ needs.analyze-and-repair.outputs.overall_risk }} run: | echo "[天眼 v3.0] Phase E — 创建 Pull Request" PR_TITLE="🌙 [AUTO-FIX] $(date +%Y-%m-%d) 天眼夜间自动修复 (${FIX_COUNT}个修复)" # Create labels if they don't exist gh label create "auto-fix" --color "0E8A16" --description "自动修复 PR" 2>/dev/null || true gh label create "tianyan-v3" --color "1D76DB" --description "天眼 v3.0 引擎" 2>/dev/null || true PR_URL=$(gh pr create \ --base main \ --head "$BRANCH" \ --title "$PR_TITLE" \ --body "$SUMMARY" \ --label "auto-fix,tianyan-v3" 2>/dev/null || echo "") if [ -n "$PR_URL" ]; then echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT echo "✅ PR 已创建: $PR_URL" # Add risk label if [ "$RISK" = "low" ]; then gh label create "safe-to-merge" --color "0E8A16" 2>/dev/null || true gh pr edit "$PR_URL" --add-label "safe-to-merge" 2>/dev/null || true elif [ "$RISK" = "medium" ]; then gh label create "review-carefully" --color "FBCA04" 2>/dev/null || true gh pr edit "$PR_URL" --add-label "review-carefully" 2>/dev/null || true fi else echo "⚠️ PR 创建失败" echo "pr_url=" >> $GITHUB_OUTPUT fi - name: "📊 Phase F — 更新仪表盘" env: GH_TOKEN: ${{ secrets.ZY_GITHUB_PAT }} PR_URL: ${{ steps.create_pr.outputs.pr_url }} FIX_COUNT: ${{ needs.analyze-and-repair.outputs.fix_count }} run: | echo "[天眼 v3.0] Phase F — 更新仪表盘" DATE=$(date +%Y-%m-%d) TIME=$(date +%H:%M) # Update DASHBOARD.md status table if [ -f DASHBOARD.md ]; then sed -i "s/| 上次扫描 | .*/| 上次扫描 | $DATE $TIME CST |/" DASHBOARD.md sed -i "s/| 扫描结果 | .*/| 扫描结果 | ⚠️ 发现错误,已自动修复 |/" DASHBOARD.md sed -i "s/| 自动修复 | .*/| 自动修复 | ✅ ${FIX_COUNT} 个已修复 |/" DASHBOARD.md if [ -n "$PR_URL" ]; then PR_NUM=$(echo "$PR_URL" | grep -oP '\d+$' || echo "?") sed -i "s/| 待合并 PR | .*/| 待合并 PR | 🟡 PR #${PR_NUM} |/" DASHBOARD.md fi git config user.name "tianyan-bot" git config user.email "tianyan@guanghulab.com" git add DASHBOARD.md git diff --cached --quiet || git commit -m "[DASHBOARD] 更新天眼仪表盘 $DATE — ${FIX_COUNT}个修复待合并" git push origin main || true fi # ======================================== # 无错误时的仪表盘更新 # ======================================== dashboard-no-errors: needs: nightly-scan if: needs.nightly-scan.outputs.has_errors == 'false' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: "✅ 更新仪表盘 — 今日无异常" env: GH_TOKEN: ${{ secrets.ZY_GITHUB_PAT }} run: | DATE=$(date +%Y-%m-%d) TIME=$(date +%H:%M) echo "[天眼 v3.0] 今日无异常,更新仪表盘" if [ -f DASHBOARD.md ]; then sed -i "s/| 上次扫描 | .*/| 上次扫描 | $DATE $TIME CST |/" DASHBOARD.md sed -i "s/| 扫描结果 | .*/| 扫描结果 | ✅ 今日无异常 |/" DASHBOARD.md sed -i "s/| 自动修复 | .*/| 自动修复 | — |/" DASHBOARD.md sed -i "s/| 需人工处理 | .*/| 需人工处理 | — |/" DASHBOARD.md git config user.name "tianyan-bot" git config user.email "tianyan@guanghulab.com" git add DASHBOARD.md git diff --cached --quiet || git commit -m "[DASHBOARD] 今日无异常 $DATE" git push origin main || true fi