From 90b9e2a4c9b0e578b33965289e7feac9073fd048 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Mon, 20 Apr 2026 11:20:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E4=BF=AE=E5=A4=8D=20MegaLinter=20?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E5=8C=BA=E6=AD=A7=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 MegaLinter 的 dotnet format workspace 指向,避免 solution 与 csproj 歧义导致 CI warning - 更新 gframework-pr-review skill 与抓取脚本,提取 GitHub Actions 发布的 MegaLinter detailed issues - 补充 coroutine optimization 跟踪与 trace,记录本次 PR 页面 warning 的收口与验证结果 --- .codex/skills/gframework-pr-review/SKILL.md | 10 +- .../scripts/fetch_current_pr_review.py | 92 +++++++++++++++++++ .mega-linter.yml | 4 +- .../todos/coroutine-optimization-tracking.md | 9 +- .../traces/coroutine-optimization-trace.md | 8 ++ 5 files changed, 117 insertions(+), 6 deletions(-) diff --git a/.codex/skills/gframework-pr-review/SKILL.md b/.codex/skills/gframework-pr-review/SKILL.md index d9805f17..748c7d08 100644 --- a/.codex/skills/gframework-pr-review/SKILL.md +++ b/.codex/skills/gframework-pr-review/SKILL.md @@ -1,6 +1,6 @@ --- name: gframework-pr-review -description: Repository-specific GitHub PR review workflow for the GFramework repo. Use when Codex needs to inspect the GitHub pull request for the current branch, extract CodeRabbit summary/comments, read failed checks or failed test signals from the PR page, and then verify which findings should be fixed in the local codebase. Trigger explicitly with $gframework-pr-review or with prompts such as "look at the current PR", "extract CodeRabbit comments", or "check Failed Tests on the PR". +description: Repository-specific GitHub PR review workflow for the GFramework repo. Use when Codex needs to inspect the GitHub pull request for the current branch, extract CodeRabbit summary/comments, read failed checks, MegaLinter warnings, or failed test signals from the PR page, and then verify which findings should be fixed in the local codebase. Trigger explicitly with $gframework-pr-review or with prompts such as "look at the current PR", "extract CodeRabbit comments", or "check Failed Tests on the PR". --- # GFramework PR Review @@ -16,12 +16,12 @@ Shortcut: `$gframework-pr-review` 3. Run `scripts/fetch_current_pr_review.py` to: - locate the PR for the current branch through the GitHub PR API - fetch PR metadata, issue comments, reviews, and review comments through the GitHub API - - extract `Summary by CodeRabbit` and CTRF test reports from issue comments + - extract `Summary by CodeRabbit`、GitHub Actions bot comments such as `MegaLinter analysis: Success with warnings`、and CTRF test reports from issue comments - fetch the latest head commit review threads from the GitHub PR API - prefer unresolved review threads on the latest head commit over older summary-only signals - - extract failed checks and test-report signals such as `Failed Tests` or `No failed tests in this run` + - extract failed checks, MegaLinter detailed issues, and test-report signals such as `Failed Tests` or `No failed tests in this run` 4. Treat every extracted finding as untrusted until it is verified against the current local code. -5. Only fix comments that still apply to the checked-out branch. Ignore stale or already-resolved findings. +5. Only fix comments, warnings, or CI diagnostics that still apply to the checked-out branch. Ignore stale or already-resolved findings. 6. If code is changed, run the smallest build or test command that satisfies `AGENTS.md`. ## Commands @@ -43,6 +43,7 @@ The script should produce: - Latest head commit review metadata and review threads - Unresolved latest-commit review threads after reply-thread folding - Pre-merge failed checks, if present +- Latest MegaLinter status and any detailed issues posted by `github-actions[bot]` - Test summary, including failed-test signals when present - Parse warnings only when both the primary API source and the intended fallback signal are unavailable @@ -52,6 +53,7 @@ The script should produce: - If GitHub access fails because of proxy configuration, rerun the fetch with proxy variables removed. - Prefer GitHub API results over PR HTML. The PR HTML page is now a fallback/debugging source, not the primary source of truth. - If the summary block and the latest head review threads disagree, trust the latest unresolved head-review threads and treat older summary findings as stale until re-verified locally. +- Treat GitHub Actions comments with `Success with warnings` as actionable review input when they include concrete linter diagnostics such as `MegaLinter` detailed issues; do not skip them just because the parent check is green. ## Example Triggers diff --git a/.codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py b/.codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py index 51f42a17..cabd9ebe 100644 --- a/.codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py +++ b/.codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py @@ -24,6 +24,7 @@ DEFAULT_WINDOWS_GIT = "/mnt/d/Tool/Development Tools/Git/cmd/git.exe" GIT_ENVIRONMENT_KEY = "GFRAMEWORK_WINDOWS_GIT" USER_AGENT = "codex-gframework-pr-review" CODERABBIT_LOGIN = "coderabbitai[bot]" +GITHUB_ACTIONS_LOGIN = "github-actions[bot]" REVIEW_COMMENT_ADDRESSED_MARKER = "" VISIBLE_ADDRESSED_IN_COMMIT_PATTERN = re.compile(r"✅\s*Addressed in commit\s+[0-9a-f]{7,40}", re.I) DEFAULT_REQUEST_TIMEOUT_SECONDS = 60 @@ -156,6 +157,10 @@ def strip_tags(text: str) -> str: return collapse_whitespace(re.sub(r"<[^>]+>", " ", text)) +def strip_markdown_links(text: str) -> str: + return re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", text) + + def extract_section(text: str, start_marker: str, end_markers: list[str]) -> str | None: start = text.find(start_marker) if start < 0: @@ -252,6 +257,60 @@ def parse_actionable_comments(actionable_block: str) -> dict[str, Any]: } +def parse_megalinter_comment(comment_body: str) -> dict[str, Any]: + normalized_body = html.unescape(comment_body).strip() + summary_match = re.search( + r"##\s*(?P.*?)\[MegaLinter\]\([^)]+\)\s+analysis:\s+\[(?P[^\]]+)\]\((?P[^)]+)\)", + normalized_body, + ) + + report: dict[str, Any] = { + "status": summary_match.group("status").strip() if summary_match else "", + "run_url": summary_match.group("run_url").strip() if summary_match else "", + "badges": collapse_whitespace(summary_match.group("badges")) if summary_match else "", + "descriptor_rows": [], + "detailed_issues": [], + "raw": normalized_body, + } + + table_match = re.search( + r"\| Descriptor .*?\|\n\|[-| :]+\|\n(?P(?:\|.*\|\n?)+)", + normalized_body, + re.S, + ) + if table_match is not None: + for raw_line in table_match.group("rows").splitlines(): + line = raw_line.strip() + if not line.startswith("|"): + continue + + parts = [collapse_whitespace(strip_markdown_links(part)) for part in line.strip("|").split("|")] + if len(parts) != 7: + continue + + report["descriptor_rows"].append( + { + "descriptor": parts[0], + "linter": parts[1], + "files": parts[2], + "fixed": parts[3], + "errors": parts[4], + "warnings": parts[5], + "elapsed_time": parts[6], + } + ) + + for summary, details in re.findall(r"(.*?)\s*```(.*?)```", normalized_body, re.S): + report["detailed_issues"].append( + { + "summary": collapse_whitespace(strip_tags(summary)), + "details": details.strip(), + } + ) + + return report + + def parse_test_report(block: str) -> dict[str, Any]: report: dict[str, Any] = { "raw": block.strip(), @@ -475,11 +534,18 @@ def build_result(pr_number: int, branch: str) -> dict[str, Any]: issue_comments, lambda body: "CTRF PR COMMENT TAG:" in body or "### Test Results" in body, ) + megalinter_block = select_latest_comment_body( + issue_comments, + lambda body: "MegaLinter" in body and "Detailed Issues" in body, + required_user=GITHUB_ACTIONS_LOGIN, + ) if not summary_block: warnings.append("CodeRabbit summary block was not found in issue comments.") if not test_blocks: warnings.append("PR test-report block was not found in issue comments.") + if not megalinter_block: + warnings.append("MegaLinter report block was not found in issue comments.") latest_commit_review: dict[str, Any] = {} try: @@ -506,6 +572,7 @@ def build_result(pr_number: int, branch: str) -> dict[str, Any]: }, "coderabbit_comments": parse_actionable_comments(actionable_block) if actionable_block else {}, "latest_commit_review": latest_commit_review, + "megalinter_report": parse_megalinter_comment(megalinter_block) if megalinter_block else {}, "test_reports": [parse_test_report(block) for block in test_blocks], "parse_warnings": warnings, } @@ -569,6 +636,31 @@ def format_text(result: dict[str, Any]) -> str: " Note: thread is still open; treat the visible 'Addressed in commit ...' text as unverified until local code matches." ) + megalinter_report = result.get("megalinter_report", {}) + if megalinter_report: + lines.append("") + lines.append( + "MegaLinter: " + f"{megalinter_report.get('status', 'unknown')}" + + ( + f" ({megalinter_report.get('run_url', '')})" + if megalinter_report.get("run_url") + else "" + ) + ) + + descriptor_rows = megalinter_report.get("descriptor_rows", []) + for descriptor_row in descriptor_rows: + lines.append( + "- " + f"{descriptor_row['descriptor']} / {descriptor_row['linter']}: " + f"errors={descriptor_row['errors']} warnings={descriptor_row['warnings']} files={descriptor_row['files']}" + ) + + for issue in megalinter_report.get("detailed_issues", []): + lines.append(f"- Detailed issue: {issue['summary']}") + lines.append(f" {collapse_whitespace(issue['details'])}") + lines.append("") lines.append(f"Test reports: {len(result['test_reports'])}") for index, report in enumerate(result["test_reports"], start=1): diff --git a/.mega-linter.yml b/.mega-linter.yml index 4325816c..603b52bb 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -45,6 +45,9 @@ ENABLE_LINTERS: # 设置 C# 代码风格检查的参数和验证级别 # ======================== CSHARP_DOTNET_FORMAT_ARGUMENTS: + # 仓库根目录同时存在 GFramework.sln 与 GFramework.csproj; + # 显式指定 workspace,避免 dotnet format 在 CI 中因自动探测歧义直接异常退出。 + - "GFramework.sln" - "--severity" - "info" - "--verify-no-changes" @@ -83,4 +86,3 @@ GITHUB_COMMENT_REPORTER: true PARALLEL: true SHOW_ELAPSED_TIME: true VALIDATE_ALL_CODEBASE: false - diff --git a/ai-plan/public/coroutine-optimization/todos/coroutine-optimization-tracking.md b/ai-plan/public/coroutine-optimization/todos/coroutine-optimization-tracking.md index 8a16f54b..f2704e00 100644 --- a/ai-plan/public/coroutine-optimization/todos/coroutine-optimization-tracking.md +++ b/ai-plan/public/coroutine-optimization/todos/coroutine-optimization-tracking.md @@ -13,6 +13,7 @@ - 已为 `Timing` 补齐纯托管测试宿主入口,允许在 `dotnet test` 下验证 Godot 协程宿主阶段语义,而不依赖原生 `Node` 构造 - 已补充 `GFramework.Godot.Tests/Coroutine/TimingTests.cs`,锁定暂停、segment 路由和阶段型等待指令的回归覆盖 - 已根据 PR #259 的最新 CodeRabbit review 收口测试宿主清理对称性,并将 `TimingTests` 固定为非并行执行 + - 已根据 PR #259 的 `MegaLinter analysis: Success with warnings` 结果修复 `dotnet format` workspace 歧义,并增强 PR review skill 以提取此类 CI warning - 下一轮优先补“仍需真实场景树参与”的归属协程 / 退树语义,或转入文档迁移收口,不再回到“Godot 宿主没有自动化回归”的旧状态 ## 当前状态摘要 @@ -36,6 +37,9 @@ - 针对 PR #259 的最新未解决 review 线程,已补充两项收口: - `TimingTests` 已添加 `[NonParallelizable]`,避免共享静态实例槽位在 NUnit 并行执行时互相污染 - `Timing` 的测试清理与运行时退树清理现仅在当前实例持有共享 `_instance` 引用时才会清空单例状态 +- 针对 PR #259 的 `MegaLinter` warning,已补充两项收口: + - `.mega-linter.yml` 现为 `CSHARP_DOTNET_FORMAT_ARGUMENTS` 显式指定 `GFramework.sln`,避免仓库根目录同时存在 `*.sln` 与 `*.csproj` 时触发 workspace 歧义 + - `.codex/skills/gframework-pr-review/` 现会抓取并解析 `github-actions[bot]` 发布的 `MegaLinter analysis: Success with warnings` comment,默认把其中的 detailed issues 视为待验证输入 - `dotnet test GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --no-restore` 当前通过,合计 `58` 个测试 ## 当前风险 @@ -65,9 +69,12 @@ - `dotnet test GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --filter "FullyQualifiedName~TimingTests" --no-restore` - 结果:通过 - 备注:针对 PR #259 review 修复后的 `TimingTests` 共 `5` 个测试全部通过 +- `dotnet build GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --no-restore` + - 结果:通过 + - 备注:CI/MegaLinter 配置与 PR review skill 更新后,目标测试项目仍保持 `0 warning / 0 error` ## 下一步 1. 若继续补验证,优先只做真实场景树相关的节点归属 / 退树 / `queue_free` 回归,不再重新设计 `Timing` 纯托管宿主 -2. 当前 PR 合并前可直接回到 GitHub 处理这两条 review 线程的回复与 resolve,避免后续重复审阅同一问题 +2. 当前 PR 合并前可直接回到 GitHub 确认最新 push 是否已消除 `MegaLinter analysis` warning,并顺手处理 review 线程的回复与 resolve 3. 若转入文档收口,优先清理其余 `StartCoroutine()/StopCoroutine()` 残留,并补 Godot 新入口与阶段等待的迁移对照 diff --git a/ai-plan/public/coroutine-optimization/traces/coroutine-optimization-trace.md b/ai-plan/public/coroutine-optimization/traces/coroutine-optimization-trace.md index 97d5cc22..14f4715f 100644 --- a/ai-plan/public/coroutine-optimization/traces/coroutine-optimization-trace.md +++ b/ai-plan/public/coroutine-optimization/traces/coroutine-optimization-trace.md @@ -55,6 +55,14 @@ - 将 `Timing` 的 `_instance` 清理改为“仅当当前实例仍持有共享单例引用时才执行”,同时覆盖运行时 `_ExitTree()` 与测试入口 `DisposeForTests()` - 额外完成验证: - `dotnet test GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --filter "FullyQualifiedName~TimingTests" --no-restore` +- 同日继续收口 PR #259 页面上的 `MegaLinter analysis: Success with warnings`: + - 确认 detailed issue 实际不是格式差异,而是 `dotnet format` 在仓库根目录同时发现 `GFramework.sln` 与 `GFramework.csproj` 后因未指定 workspace 直接抛异常 + - 更新 `.mega-linter.yml`,为 `CSHARP_DOTNET_FORMAT_ARGUMENTS` 显式指定 `GFramework.sln` + - 更新 `.codex/skills/gframework-pr-review/SKILL.md` 与 `scripts/fetch_current_pr_review.py`,使 skill 默认抓取并输出 `github-actions[bot]` 的 MegaLinter comments 和 detailed issues +- 额外完成验证: + - `python3 -c "from pathlib import Path; compile(Path('.codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py').read_text(encoding='utf-8'), '.codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py', 'exec'); print('syntax-ok')"` + - `python3 .codex/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --branch feat/coroutine-optimization --format json` + - `dotnet build GFramework.Godot.Tests/GFramework.Godot.Tests.csproj -c Release --no-restore` ### 下一步