From d55a0546a570c096e919b156e07f3d4fcfdecf87 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Tue, 30 Jun 2026 17:11:28 +0200 Subject: [PATCH] Unify GitHub report repo headings (#779) Co-authored-by: UltralyticsAssistant --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- .github/ISSUE_TEMPLATE/feature-request.yml | 2 +- .github/ISSUE_TEMPLATE/question.yml | 2 +- .github/workflows/github_report.yml | 4 +- README.md | 21 ++------- README.zh-CN.md | 21 ++------- actions/failed_scheduled_actions.py | 53 ++++++++++++++------- actions/github_report.py | 7 ++- actions/scan_prs.py | 9 +++- github-report/README.md | 11 +++-- github-report/action.yml | 17 ++++--- pyproject.toml | 1 + tests/test_github_report.py | 54 +++++++++++++++------- 13 files changed, 120 insertions(+), 84 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index ee41b96..3e59c93 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -31,7 +31,7 @@ body: - "Ultralytics Actions workflow (ultralytics/actions@main)" - "Retry Action" - "Cleanup Disk Action" - - "Scan PRs Action" + - "GitHub Report Action" - "Dependabot Action" - "Python package (`ultralytics-actions`)" - "Repository documentation / README" diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index be0673e..84953b6 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -31,7 +31,7 @@ body: - "Ultralytics Actions workflow (ultralytics/actions@main)" - "Retry Action" - "Cleanup Disk Action" - - "Scan PRs Action" + - "GitHub Report Action" - "Dependabot Action" - "Python package (`ultralytics-actions`)" - "Repository documentation / README" diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml index 55501a5..8568254 100644 --- a/.github/ISSUE_TEMPLATE/question.yml +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -29,7 +29,7 @@ body: - "Ultralytics Actions workflow (ultralytics/actions@main)" - "Retry Action" - "Cleanup Disk Action" - - "Scan PRs Action" + - "GitHub Report Action" - "Dependabot Action" - "Python package (`ultralytics-actions`)" - "Repository documentation / README" diff --git a/.github/workflows/github_report.yml b/.github/workflows/github_report.yml index e019215..8c8793b 100644 --- a/.github/workflows/github_report.yml +++ b/.github/workflows/github_report.yml @@ -1,6 +1,6 @@ # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license -# Summarize open PRs and failed scheduled GitHub Actions across public Ultralytics repositories +# Summarize open PRs and failed default-branch GitHub Actions across public Ultralytics repositories name: GitHub Report @@ -8,7 +8,7 @@ on: workflow_dispatch: inputs: days: - description: "Days of scheduled workflow history to include" + description: "Days of workflow history to include" required: false type: number default: 1 diff --git a/README.md b/README.md index c5a90f3..26d7402 100644 --- a/README.md +++ b/README.md @@ -144,23 +144,9 @@ Free up disk space on GitHub runners by removing unnecessary packages and files. [**📖 Full Documentation →**](cleanup-disk/README.md) -### 3. Scan PRs Action +### 3. GitHub Report Action -List open PRs across an organization and auto-merge eligible GitHub Actions update PRs. - -```yaml -- uses: ultralytics/actions/scan-prs@main - with: - token: ${{ secrets.GITHUB_TOKEN }} - org: ultralytics # Optional: defaults to ultralytics - visibility: private,internal # Optional: public, private, internal, all, or comma-separated -``` - -[**📖 Full Documentation →**](scan-prs/README.md) - -### 4. GitHub Report Action - -Summarize open PRs and failed scheduled GitHub Actions across organization repositories. +Summarize open PRs and failed default-branch GitHub Actions across organization repositories. ```yaml - uses: ultralytics/actions/github-report@main @@ -172,7 +158,7 @@ Summarize open PRs and failed scheduled GitHub Actions across organization repos [**📖 Full Documentation →**](github-report/README.md) -### 5. Dependabot Action +### 4. Dependabot Action Update GitHub Actions versions across organization repositories with cached release resolution. @@ -198,6 +184,7 @@ pip install ultralytics-actions - `actions.review_pr` - AI-powered PR review - `actions.summarize_pr` - Generate PR summaries +- `actions.github_report` - Generate GitHub organization reports - `actions.scan_prs` - Scan and manage organization PRs - `actions.first_interaction` - Welcome message for new contributors - And more in `actions/` directory diff --git a/README.zh-CN.md b/README.zh-CN.md index fd0554e..90803ec 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -144,23 +144,9 @@ jobs: [**📖 完整文档 →**](cleanup-disk/README.md) -### 3. Scan PRs Action +### 3. GitHub Report Action -列出组织中的开放 PR,并自动合并符合条件的 GitHub Actions 更新 PR。 - -```yaml -- uses: ultralytics/actions/scan-prs@main - with: - token: ${{ secrets.GITHUB_TOKEN }} - org: ultralytics # Optional: defaults to ultralytics - visibility: private,internal # Optional: public, private, internal, all, or comma-separated -``` - -[**📖 完整文档 →**](scan-prs/README.md) - -### 4. GitHub Report Action - -汇总组织仓库中的开放 PR 和失败的定时 GitHub Actions。 +汇总组织仓库中的开放 PR 和默认分支上失败的 GitHub Actions。 ```yaml - uses: ultralytics/actions/github-report@main @@ -172,7 +158,7 @@ jobs: [**📖 完整文档 →**](github-report/README.md) -### 5. Dependabot Action +### 4. Dependabot Action 使用缓存的 release 解析,批量更新组织仓库中的 GitHub Actions 版本。 @@ -198,6 +184,7 @@ pip install ultralytics-actions - `actions.review_pr` - AI 驱动的 PR review - `actions.summarize_pr` - 生成 PR 摘要 +- `actions.github_report` - 生成 GitHub 组织报告 - `actions.scan_prs` - 扫描并管理组织 PRs - `actions.first_interaction` - 为新贡献者生成欢迎消息 - 更多模块见 `actions/` 目录 diff --git a/actions/failed_scheduled_actions.py b/actions/failed_scheduled_actions.py index 2a60ce4..b08bd1a 100644 --- a/actions/failed_scheduled_actions.py +++ b/actions/failed_scheduled_actions.py @@ -1,5 +1,5 @@ # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license -"""Report failed scheduled GitHub Actions on default branches across an organization.""" +"""Report failed GitHub Actions on default branches across an organization.""" from __future__ import annotations @@ -9,16 +9,16 @@ import time import urllib.error import urllib.parse import urllib.request -from collections import defaultdict +from collections import Counter, defaultdict from datetime import datetime, timedelta, timezone -from actions.scan_prs import get_repo_filter, parse_visibility +from actions.scan_prs import format_repo_heading, get_repo_filter, parse_visibility FAILED_CONCLUSIONS = {"failure", "timed_out", "action_required", "startup_failure", "cancelled"} def run_time(run): - """Return the scheduled run timestamp used for recency filtering and latest-run selection.""" + """Return the run timestamp used for recency filtering and latest-run selection.""" value = run.get("run_started_at") or run.get("created_at") or "" return datetime.fromisoformat(value.replace("Z", "+00:00")) if value else datetime.min.replace(tzinfo=timezone.utc) @@ -67,10 +67,10 @@ def paginate(path, params=None, key=None, max_pages=100, token=None, allow_skip= return items -def collect_failed_scheduled_actions( +def collect_failed_actions( org="ultralytics", visibility="public", repo_visibility="public", max_run_pages=3, days=1, token=None ): - """Collect latest failed scheduled workflow runs on each non-archived repo's default branch.""" + """Collect latest failed workflow runs on each non-archived repo's default branch.""" cutoff = datetime.now(timezone.utc) - timedelta(days=days) if days else None filter_config = get_repo_filter(parse_visibility(visibility, repo_visibility)) repos = [ @@ -84,7 +84,7 @@ def collect_failed_scheduled_actions( branch = repo.get("default_branch") or "main" runs = paginate( f"/repos/{full_name}/actions/runs", - {"event": "schedule", "branch": branch}, + {"branch": branch}, key="workflow_runs", max_pages=max_run_pages, token=token, @@ -102,8 +102,10 @@ def collect_failed_scheduled_actions( failures.append( { "repo": full_name, + "repo_url": repo.get("html_url") or f"https://github.com/{full_name}", "visibility": repo.get("visibility", "private" if repo.get("private") else "public"), "workflow": run.get("name") or run.get("display_title") or "Workflow", + "event": run.get("event") or "unknown", "branch": branch, "run_number": run.get("run_number"), "failed_at": run.get("updated_at") or run.get("created_at") or "", @@ -116,44 +118,63 @@ def collect_failed_scheduled_actions( def format_report(failures, org="ultralytics"): - """Format failed scheduled workflow runs as Markdown.""" + """Format failed workflow runs as Markdown.""" now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") lines = [ - "# Failed Scheduled Actions", + "# Failed Default Branch Actions", "", - f"Failed scheduled workflow runs on default branches for `{org}` repositories as of {now}.", + f"Failed workflow runs on default branches for `{org}` repositories as of {now}.", "", ] if not failures: - lines.append("No failed scheduled workflow runs found.") + lines.append("No failed default-branch workflow runs found.") return "\n".join(lines) + "\n" repo_count = len({failure["repo"] for failure in failures}) - lines.append(f"**{len(failures)} failing scheduled workflow runs** across **{repo_count} repositories**.") + run_word = "run" if len(failures) == 1 else "runs" + repo_word = "repository" if repo_count == 1 else "repositories" + event_counts = Counter(failure.get("event") or "unknown" for failure in failures) + lines.extend( + [ + f"**{len(failures)} failing default-branch workflow {run_word}** across **{repo_count} {repo_word}**.", + "**By Event:** " + " | ".join(f"`{event}` {event_counts[event]}" for event in sorted(event_counts)), + ] + ) grouped = defaultdict(list) for failure in failures: grouped[failure["repo"]].append(failure) for repo, repo_failures in grouped.items(): - lines.extend(["", f"## `{repo}` ({repo_failures[0]['visibility']})"]) + count = len(repo_failures) + lines.extend( + [ + "", + format_repo_heading( + repo, + repo_failures[0].get("repo_url") or f"https://github.com/{repo}", + f"{count} failed run{'s' if count != 1 else ''}", + ), + ] + ) for failure in repo_failures: failed_at = failure["failed_at"].replace("T", " ").replace("Z", " UTC") details = f" @ `{failure['sha']}`" if failure["sha"] else "" details += f" - {failure['title']}" if failure["title"] else "" lines.append( - f"- **{failure['workflow']}** on `{failure['branch']}` failed at {failed_at}{details}. " + f"- **{failure['workflow']}** (`{failure.get('event') or 'unknown'}`) on `{failure['branch']}` " + f"failed at {failed_at}{details}. " f"[Run #{failure['run_number']}]({failure['url']})" ) return "\n".join(lines) + "\n" def run(): - """Write failed scheduled Actions report to stdout and the GitHub step summary.""" + """Write failed Actions report to stdout and the GitHub step summary.""" org = os.getenv("ORG", "ultralytics") max_run_pages = int(os.getenv("MAX_RUN_PAGES", "3")) days = int(os.getenv("REPORT_DAYS", "1")) report = format_report( - collect_failed_scheduled_actions( + collect_failed_actions( org, visibility=os.getenv("VISIBILITY", "public"), repo_visibility=os.getenv("REPO_VISIBILITY", "public"), diff --git a/actions/github_report.py b/actions/github_report.py index 7ca5cb6..7cd80c0 100644 --- a/actions/github_report.py +++ b/actions/github_report.py @@ -9,13 +9,18 @@ def enabled(value): return str(value).lower() == "true" +def enabled_any(*values, default="true"): + """Return the first explicitly set boolean value, otherwise the default.""" + return enabled(next((value for value in values if value not in {None, ""}), default)) + + def run(): """Run enabled GitHub report sections.""" import os if enabled(os.getenv("REPORT_PRS", "true")): scan_prs.run() - if enabled(os.getenv("REPORT_FAILED_SCHEDULED_ACTIONS", "true")): + if enabled_any(os.getenv("REPORT_FAILED_ACTIONS"), os.getenv("REPORT_FAILED_SCHEDULED_ACTIONS")): failed_scheduled_actions.run() diff --git a/actions/scan_prs.py b/actions/scan_prs.py index fe9efa6..3ffd110 100644 --- a/actions/scan_prs.py +++ b/actions/scan_prs.py @@ -60,6 +60,11 @@ def get_repo_filter(visibility_list): } +def format_repo_heading(repo_name, repo_url, detail): + """Format a repository section heading for GitHub report Markdown.""" + return f"## 📦 [{repo_name}]({repo_url}) - {detail}" + + def get_status_checks(rollup): """Extract and validate status checks from rollup, return failed checks.""" checks = rollup if isinstance(rollup, list) else rollup.get("contexts", []) if isinstance(rollup, dict) else [] @@ -147,7 +152,9 @@ def run(): for repo_name in sorted({pr["repository"]["name"] for pr in all_prs}): repo_prs = [pr for pr in all_prs if pr["repository"]["name"] == repo_name] summary.append( - f"## 📦 [{repo_name}]({repos[repo_name]}) - {len(repo_prs)} open PR{'s' if len(repo_prs) > 1 else ''}" + format_repo_heading( + repo_name, repos[repo_name], f"{len(repo_prs)} open PR{'s' if len(repo_prs) > 1 else ''}" + ) ) for pr in repo_prs[:30]: diff --git a/github-report/README.md b/github-report/README.md index 90148fa..dfb8d65 100644 --- a/github-report/README.md +++ b/github-report/README.md @@ -1,6 +1,6 @@ # GitHub Report Action -Summarize open pull requests and failed scheduled GitHub Actions across organization repositories. +Summarize open pull requests and failed default-branch GitHub Actions across organization repositories. ## Usage @@ -35,10 +35,11 @@ jobs: | `org` | GitHub organization name | No | `ultralytics` | | `visibility` | Repository visibility for PR scanning: `public`, `private`, `internal`, `all` | No | `public` | | `prs` | Include open PR summary | No | `true` | -| `failed_scheduled_actions` | Include failed scheduled Actions summary | No | `true` | +| `failed_actions` | Include failed default-branch Actions summary | No | `true` | +| `failed_scheduled_actions` | Deprecated alias for `failed_actions` | No | - | | `auto_merge_actions_prs` | Auto-merge eligible GitHub Actions update PRs in the PR section | No | `false` | -| `max_run_pages` | Pages of scheduled workflow runs to inspect per repository | No | `3` | -| `days` | Days of scheduled workflow history to include | No | `1` | +| `max_run_pages` | Pages of workflow runs to inspect per repository | No | `3` | +| `days` | Days of workflow history to include | No | `1` | ## Output @@ -46,4 +47,4 @@ The action writes a GitHub step summary with: - Open PR totals and age buckets - Eligible GitHub Actions update PR merge results -- Latest failed scheduled workflow runs on each repository's default branch +- Latest failed workflow runs on each repository's default branch, grouped by event diff --git a/github-report/action.yml b/github-report/action.yml index 28281f2..10b353a 100644 --- a/github-report/action.yml +++ b/github-report/action.yml @@ -2,7 +2,7 @@ name: "GitHub Report" author: "Ultralytics" -description: "Summarize open PRs and failed scheduled GitHub Actions across organization repositories" +description: "Summarize open PRs and failed default-branch GitHub Actions across organization repositories" branding: icon: "activity" color: "blue" @@ -22,20 +22,24 @@ inputs: description: "Include open PR summary" required: false default: "true" - failed_scheduled_actions: - description: "Include failed scheduled Actions summary" + failed_actions: + description: "Include failed default-branch Actions summary" required: false - default: "true" + default: "" + failed_scheduled_actions: + description: "Deprecated alias for failed_actions" + required: false + default: "" auto_merge_actions_prs: description: "Auto-merge eligible GitHub Actions update PRs in the PR section" required: false default: "false" max_run_pages: - description: "Pages of scheduled workflow runs to inspect per repository" + description: "Pages of workflow runs to inspect per repository" required: false default: "3" days: - description: "Days of scheduled workflow history to include" + description: "Days of workflow history to include" required: false default: "1" runs: @@ -74,6 +78,7 @@ runs: VISIBILITY: ${{ inputs.visibility }} REPO_VISIBILITY: ${{ github.event.repository.visibility }} REPORT_PRS: ${{ inputs.prs }} + REPORT_FAILED_ACTIONS: ${{ inputs.failed_actions }} REPORT_FAILED_SCHEDULED_ACTIONS: ${{ inputs.failed_scheduled_actions }} AUTO_MERGE_ACTIONS_PRS: ${{ inputs.auto_merge_actions_prs }} MAX_RUN_PAGES: ${{ inputs.max_run_pages }} diff --git a/pyproject.toml b/pyproject.toml index 7a23872..7456457 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,6 +92,7 @@ ultralytics-actions-update-markdown-code-blocks = "actions.update_markdown_code_ ultralytics-actions-headers = "actions.update_file_headers:main" ultralytics-actions-format-python-docstrings = "actions.format_python_docstrings:main" ultralytics-actions-dependabot = "actions.dependabot:run" +ultralytics-actions-failed-actions = "actions.failed_scheduled_actions:run" ultralytics-actions-failed-scheduled-actions = "actions.failed_scheduled_actions:run" ultralytics-actions-github-report = "actions.github_report:run" ultralytics-actions-info = "actions.utils:ultralytics_actions_info" diff --git a/tests/test_github_report.py b/tests/test_github_report.py index 86f5b86..3c4a364 100644 --- a/tests/test_github_report.py +++ b/tests/test_github_report.py @@ -25,14 +25,15 @@ def test_paginate_only_skips_http_errors_when_allowed(monkeypatch): raise AssertionError("Expected org listing failure to raise") -def test_collect_failed_scheduled_actions_latest_run_per_workflow(monkeypatch): - """Only the latest scheduled run for each workflow should determine whether it is reported.""" +def test_collect_failed_actions_latest_run_per_workflow(monkeypatch): + """Only the latest default-branch run for each workflow should determine whether it is reported.""" def fake_paginate(path, params=None, key=None, max_pages=100, token=None, allow_skip=False): if path == "/orgs/ultralytics/repos": return [ { "full_name": "ultralytics/private-repo", + "html_url": "https://github.com/ultralytics/private-repo", "default_branch": "main", "visibility": "private", "archived": False, @@ -61,6 +62,7 @@ def test_collect_failed_scheduled_actions_latest_run_per_workflow(monkeypatch): { "workflow_id": 2, "name": "Links", + "event": "push", "conclusion": "timed_out", "run_started_at": "2026-06-30T04:00:00Z", "run_number": 42, @@ -76,18 +78,19 @@ def test_collect_failed_scheduled_actions_latest_run_per_workflow(monkeypatch): monkeypatch.setattr(failed_scheduled_actions, "paginate", fake_paginate) - failures = failed_scheduled_actions.collect_failed_scheduled_actions( + failures = failed_scheduled_actions.collect_failed_actions( visibility="all", repo_visibility="private", token="token" ) assert len(failures) == 1 assert failures[0]["repo"] == "ultralytics/private-repo" assert failures[0]["workflow"] == "Links" + assert failures[0]["event"] == "push" assert failures[0]["sha"] == "abcdef1" -def test_collect_failed_scheduled_actions_ignores_latest_success(monkeypatch): - """Older failed scheduled runs should not be reported after a newer success.""" +def test_collect_failed_actions_ignores_latest_success(monkeypatch): + """Older failed runs should not be reported after a newer success.""" def fake_paginate(path, params=None, key=None, max_pages=100, token=None, allow_skip=False): if path == "/orgs/ultralytics/repos": @@ -118,11 +121,11 @@ def test_collect_failed_scheduled_actions_ignores_latest_success(monkeypatch): monkeypatch.setattr(failed_scheduled_actions, "paginate", fake_paginate) - assert failed_scheduled_actions.collect_failed_scheduled_actions(token="token") == [] + assert failed_scheduled_actions.collect_failed_actions(token="token") == [] -def test_collect_failed_scheduled_actions_respects_days_window(monkeypatch): - """Failed scheduled runs older than the requested window should be omitted.""" +def test_collect_failed_actions_respects_days_window(monkeypatch): + """Failed runs older than the requested window should be omitted.""" old_date = (datetime.now(timezone.utc) - timedelta(days=2)).strftime("%Y-%m-%dT%H:%M:%SZ") def fake_paginate(path, params=None, key=None, max_pages=100, token=None, allow_skip=False): @@ -148,8 +151,8 @@ def test_collect_failed_scheduled_actions_respects_days_window(monkeypatch): monkeypatch.setattr(failed_scheduled_actions, "paginate", fake_paginate) - assert failed_scheduled_actions.collect_failed_scheduled_actions(days=1, token="token") == [] - assert failed_scheduled_actions.collect_failed_scheduled_actions(days=3, token="token") + assert failed_scheduled_actions.collect_failed_actions(days=1, token="token") == [] + assert failed_scheduled_actions.collect_failed_actions(days=3, token="token") def test_format_report_links_failures(): @@ -158,8 +161,10 @@ def test_format_report_links_failures(): [ { "repo": "ultralytics/private-repo", + "repo_url": "https://github.com/ultralytics/private-repo", "visibility": "private", "workflow": "Nightly", + "event": "push", "branch": "main", "run_number": 7, "failed_at": "2026-06-30T01:02:03Z", @@ -170,16 +175,20 @@ def test_format_report_links_failures(): ] ) - assert "# Failed Scheduled Actions" in report - assert "**Nightly** on `main` failed at 2026-06-30 01:02:03 UTC" in report + assert "# Failed Default Branch Actions" in report + assert "**1 failing default-branch workflow run** across **1 repository**." in report + assert "**By Event:** `push` 1" in report + assert "## 📦 [ultralytics/private-repo](https://github.com/ultralytics/private-repo) - 1 failed run" in report + assert "(private)" not in report + assert "**Nightly** (`push`) on `main` failed at 2026-06-30 01:02:03 UTC" in report assert "[Run #7](https://github.com/ultralytics/private-repo/actions/runs/7)" in report def test_github_report_runs_enabled_sections(monkeypatch): - """The shared report driver gates PR and failed scheduled Actions sections by env flags.""" + """The shared report driver gates PR and failed Actions sections by env flags.""" calls = [] monkeypatch.setenv("REPORT_PRS", "true") - monkeypatch.setenv("REPORT_FAILED_SCHEDULED_ACTIONS", "false") + monkeypatch.setenv("REPORT_FAILED_ACTIONS", "false") monkeypatch.setattr(github_report.scan_prs, "run", lambda: calls.append("prs")) monkeypatch.setattr(github_report.failed_scheduled_actions, "run", lambda: calls.append("actions")) @@ -188,13 +197,26 @@ def test_github_report_runs_enabled_sections(monkeypatch): assert calls == ["prs"] +def test_github_report_keeps_failed_scheduled_actions_alias(monkeypatch): + """The old failed_scheduled_actions input remains a compatibility alias.""" + calls = [] + monkeypatch.setenv("REPORT_PRS", "false") + monkeypatch.setenv("REPORT_FAILED_SCHEDULED_ACTIONS", "false") + monkeypatch.setattr(github_report.scan_prs, "run", lambda: calls.append("prs")) + monkeypatch.setattr(github_report.failed_scheduled_actions, "run", lambda: calls.append("actions")) + + github_report.run() + + assert calls == [] + + def test_failed_scheduled_actions_summary_appends_section_break(tmp_path, monkeypatch): """Appending after the PR section should not concatenate Markdown headings.""" summary = tmp_path / "summary.md" summary.write_text("**Summary:** Found 0 | Merged 0 | Skipped 0\n") monkeypatch.setenv("GITHUB_STEP_SUMMARY", str(summary)) - monkeypatch.setattr(failed_scheduled_actions, "collect_failed_scheduled_actions", lambda *args, **kwargs: []) + monkeypatch.setattr(failed_scheduled_actions, "collect_failed_actions", lambda *args, **kwargs: []) failed_scheduled_actions.run() - assert "Skipped 0\n\n# Failed Scheduled Actions" in summary.read_text() + assert "Skipped 0\n\n# Failed Default Branch Actions" in summary.read_text()