Daily Issue Health Report #59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Daily Issue Health Report | |
| on: | |
| schedule: | |
| - cron: "30 23 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| hours: | |
| description: "Lookback hours" | |
| required: false | |
| default: "24" | |
| type: string | |
| discussion_number: | |
| description: "Discussion number to publish report" | |
| required: false | |
| default: "" | |
| type: string | |
| publish_comment: | |
| description: "Publish report to discussion" | |
| required: false | |
| default: "true" | |
| type: choice | |
| options: | |
| - "true" | |
| - "false" | |
| permissions: | |
| contents: read | |
| issues: read | |
| actions: read | |
| discussions: write | |
| env: | |
| REPO_NAME: ${{ github.repository }} | |
| HOURS: ${{ github.event.inputs.hours || '24' }} | |
| # 专用日报贴编号:优先 workflow_dispatch 输入,否则读取仓库变量 DAILY_REPORT_DISCUSSION_NUMBER | |
| DISCUSSION_NUMBER: ${{ github.event.inputs.discussion_number || vars.DAILY_REPORT_DISCUSSION_NUMBER }} | |
| PUBLISH_COMMENT: ${{ github.event.inputs.publish_comment || 'true' }} | |
| GH_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }} | |
| jobs: | |
| report: | |
| if: github.repository == 'gqy20/IssueLab' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: astral-sh/setup-uv@v7 | |
| with: | |
| python-version: '3.13' | |
| enable-cache: true | |
| - run: uv sync | |
| - name: Generate daily facts | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts | |
| uv run python scripts/daily_issue_health_report.py \ | |
| --repo "${REPO_NAME}" \ | |
| --hours "${HOURS}" \ | |
| --output artifacts/daily_issue_health_facts.md \ | |
| --facts-output artifacts/daily_issue_health_facts.json | |
| - name: Run ops_daily diagnosis | |
| id: ops_daily | |
| continue-on-error: true | |
| env: | |
| ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }} | |
| MINIMAX_API_KEY: ${{ secrets.ANTHROPIC_AUTH_TOKEN }} | |
| ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL || 'https://api.minimaxi.com/anthropic' }} | |
| ANTHROPIC_MODEL: ${{ secrets.ANTHROPIC_MODEL || 'MiniMax-M2.1' }} | |
| run: | | |
| set -euo pipefail | |
| uv run python scripts/daily_issue_health_agent_report.py \ | |
| --facts artifacts/daily_issue_health_facts.json \ | |
| --output artifacts/daily_issue_health_agent.md \ | |
| --structured-output artifacts/daily_issue_health_agent.json | |
| - name: Assemble final report | |
| run: | | |
| set -euo pipefail | |
| { | |
| cat artifacts/daily_issue_health_facts.md | |
| echo "" | |
| if [ -f artifacts/daily_issue_health_agent.md ]; then | |
| cat artifacts/daily_issue_health_agent.md | |
| else | |
| echo "## 系统智能体诊断(ops_daily)" | |
| echo "" | |
| echo "> 当日诊断执行失败,已降级为仅发布 facts 报告。" | |
| fi | |
| } > artifacts/daily_issue_health_report.md | |
| - name: Add step summary | |
| run: | | |
| cat artifacts/daily_issue_health_report.md >> "$GITHUB_STEP_SUMMARY" | |
| - name: Validate discussion target | |
| if: ${{ env.PUBLISH_COMMENT != 'false' }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${DISCUSSION_NUMBER}" ]; then | |
| echo "DAILY_REPORT_DISCUSSION_NUMBER is not configured." >&2 | |
| echo "Please set repository variable DAILY_REPORT_DISCUSSION_NUMBER to your dedicated daily report discussion number." >&2 | |
| exit 1 | |
| fi | |
| - name: Publish to discussion | |
| if: ${{ env.PUBLISH_COMMENT != 'false' }} | |
| run: | | |
| set -euo pipefail | |
| REPO_OWNER="${{ github.repository_owner }}" | |
| REPO_SHORT="${{ github.event.repository.name }}" | |
| DISC_ID=$(gh api graphql \ | |
| -f query='query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){discussion(number:$number){id}}}' \ | |
| -f owner="${REPO_OWNER}" \ | |
| -f name="${REPO_SHORT}" \ | |
| -F number="${DISCUSSION_NUMBER}" \ | |
| --jq '.data.repository.discussion.id') | |
| BODY="<!-- issuelab-daily-report -->"$'\n'"$(cat artifacts/daily_issue_health_report.md)" | |
| gh api graphql \ | |
| -f query='mutation($discussionId:ID!,$body:String!){addDiscussionComment(input:{discussionId:$discussionId,body:$body}){comment{url}}}' \ | |
| -f discussionId="${DISC_ID}" \ | |
| -f body="${BODY}" | |
| - name: Upload report artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: daily-issue-health-report-${{ github.run_id }} | |
| path: | | |
| artifacts/daily_issue_health_report.md | |
| artifacts/daily_issue_health_facts.json | |
| artifacts/daily_issue_health_agent.json | |
| retention-days: 14 | |
| - name: Emit observability summary | |
| if: always() | |
| env: | |
| WF_NAME: ${{ github.workflow }} | |
| JOB_NAME: report | |
| RUN_ID: ${{ github.run_id }} | |
| REPO_NAME: ${{ github.repository }} | |
| SHA: ${{ github.sha }} | |
| OPS_OUTCOME: ${{ steps.ops_daily.outcome }} | |
| PUBLISH_ENABLED: ${{ env.PUBLISH_COMMENT }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts/observability | |
| python - << 'PY' | |
| import json | |
| import os | |
| from datetime import UTC, datetime | |
| from pathlib import Path | |
| facts_path = Path("artifacts/daily_issue_health_facts.json") | |
| metrics = {} | |
| if facts_path.exists(): | |
| try: | |
| metrics = json.loads(facts_path.read_text(encoding="utf-8")).get("metrics", {}) | |
| except Exception: | |
| metrics = {} | |
| input_count = int(metrics.get("touched_issues", 0) or 0) | |
| failed_count = int(metrics.get("workflow_failed", 0) or 0) | |
| success_count = max(int(metrics.get("workflow_total", 0) or 0) - failed_count, 0) | |
| skipped_count = 0 | |
| failures = [] | |
| if (os.getenv("OPS_OUTCOME") or "") == "failure": | |
| failures.append({"code": "OPS_DAILY_FAILED", "count": 1, "samples": []}) | |
| if (os.getenv("PUBLISH_ENABLED") or "").lower() == "false": | |
| failures.append({"code": "PUBLISH_SKIPPED_BY_CONFIG", "count": 1, "samples": []}) | |
| status = "success" | |
| if failures: | |
| status = "partial" | |
| if failed_count > 0 and success_count == 0: | |
| status = "failure" | |
| payload = { | |
| "schema_version": "v1", | |
| "workflow": os.getenv("WF_NAME"), | |
| "job": os.getenv("JOB_NAME"), | |
| "run_id": os.getenv("RUN_ID"), | |
| "repo": os.getenv("REPO_NAME"), | |
| "sha": os.getenv("SHA"), | |
| "finished_at": datetime.now(UTC).isoformat().replace("+00:00", "Z"), | |
| "metrics": { | |
| "input_count": input_count, | |
| "success_count": success_count, | |
| "failed_count": failed_count, | |
| "skipped_count": skipped_count, | |
| }, | |
| "failures_topn": failures[:5], | |
| "status": status, | |
| } | |
| Path("artifacts/observability/daily_issue_health_report__report.json").write_text( | |
| json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8" | |
| ) | |
| summary = [ | |
| "## Observability (daily report)", | |
| "", | |
| "| metric | value |", | |
| "|---|---:|", | |
| f"| input_count | {input_count} |", | |
| f"| success_count | {success_count} |", | |
| f"| failed_count | {failed_count} |", | |
| f"| skipped_count | {skipped_count} |", | |
| f"| status | {status} |", | |
| "", | |
| "### Failure TopN", | |
| ] | |
| if failures: | |
| for item in failures: | |
| summary.append(f"- `{item['code']}`: {item['count']}") | |
| else: | |
| summary.append("- none") | |
| with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as f: | |
| f.write("\n".join(summary) + "\n") | |
| PY | |
| - name: Upload observability artifact | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: daily-report-observability-${{ github.run_id }} | |
| path: artifacts/observability/ | |
| retention-days: 14 |