Unify GitHub report repo headings (#779)
Co-authored-by: UltralyticsAssistant <[email protected]>
This commit is contained in:
co-authored by
UltralyticsAssistant
parent
c4961b4dd0
commit
d55a0546a5
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+4
-17
@@ -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/` 目录
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
+8
-1
@@ -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]:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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"
|
||||
|
||||
+38
-16
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user