4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
118 lines
4.5 KiB
Python
118 lines
4.5 KiB
Python
"""Report composition helpers for GitHub workflow tools."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Sequence
|
|
from typing import Any
|
|
|
|
from integrations.github.tools.workflow.models import PullRequestStatus, WorkItem, WorkStatusReport
|
|
|
|
|
|
def _as_dict(item: WorkItem | PullRequestStatus | dict[str, Any]) -> dict[str, Any]:
|
|
if isinstance(item, WorkItem | PullRequestStatus):
|
|
return item.to_dict()
|
|
return item
|
|
|
|
|
|
def _item_line(item: dict[str, Any]) -> str:
|
|
assignees = item.get("assignees") or []
|
|
owner = f" — @{', @'.join(assignees)}" if assignees else ""
|
|
return f"• #{item.get('number', '?')} {item.get('title', '')}{owner}"
|
|
|
|
|
|
def _pr_line(pr: dict[str, Any]) -> str:
|
|
reasons = pr.get("blocking_reasons") or []
|
|
reason_text = f" — {', '.join(str(reason) for reason in reasons)}" if reasons else ""
|
|
return f"• PR #{pr.get('number', '?')} {pr.get('title', '')}{reason_text}"
|
|
|
|
|
|
def _recommended_actions(
|
|
*,
|
|
up_for_grabs: list[dict[str, Any]],
|
|
unassigned: list[dict[str, Any]],
|
|
blocked_prs: list[dict[str, Any]],
|
|
mergeable_prs: list[dict[str, Any]],
|
|
errors: list[str],
|
|
) -> list[str]:
|
|
actions: list[str] = []
|
|
if errors:
|
|
actions.append("• Re-run failed GitHub reads before trusting this status report.")
|
|
if blocked_prs:
|
|
actions.append(f"• Unblock {len(blocked_prs)} PR(s) before starting new work.")
|
|
if mergeable_prs:
|
|
actions.append(f"• Review or merge {len(mergeable_prs)} ready PR(s).")
|
|
if up_for_grabs:
|
|
actions.append(f"• Assign {len(up_for_grabs)} up-for-grabs task(s).")
|
|
if unassigned:
|
|
actions.append(f"• Triage {len(unassigned)} unassigned issue(s).")
|
|
if not actions:
|
|
actions.append("• No obvious blockers from the supplied data.")
|
|
return actions
|
|
|
|
|
|
def build_work_status_report(
|
|
*,
|
|
work_items: Sequence[WorkItem | dict[str, Any]],
|
|
pull_requests: Sequence[PullRequestStatus | dict[str, Any]],
|
|
context: str = "today",
|
|
errors: list[str] | None = None,
|
|
) -> WorkStatusReport:
|
|
"""Build a Slack-ready report from an already-read GitHub snapshot."""
|
|
|
|
errors = list(errors or [])
|
|
item_dicts = [_as_dict(item) for item in work_items]
|
|
pr_dicts = [_as_dict(pr) for pr in pull_requests]
|
|
up_for_grabs = [item for item in item_dicts if item.get("work_status") == "up_for_grabs"]
|
|
unassigned = [item for item in item_dicts if item.get("work_status") == "unassigned"]
|
|
taken = [item for item in item_dicts if item.get("work_status") == "taken"]
|
|
blocked_prs = [pr for pr in pr_dicts if pr.get("status") == "blocked"]
|
|
mergeable_prs = [pr for pr in pr_dicts if pr.get("status") == "mergeable"]
|
|
unknown_prs = [pr for pr in pr_dicts if pr.get("status") == "unknown"]
|
|
|
|
sections = [f"*Engineering status — {context}*", ""]
|
|
if errors:
|
|
sections.extend(["*Incomplete report:*", *[f"• {error}" for error in errors], ""])
|
|
sections.append(
|
|
f"*Open work:* {len(item_dicts)} total ({len(taken)} taken, "
|
|
f"{len(up_for_grabs)} up for grabs, {len(unassigned)} unassigned)"
|
|
)
|
|
if up_for_grabs:
|
|
sections.extend(["", "*Up for grabs:*", *[_item_line(item) for item in up_for_grabs[:10]]])
|
|
if unassigned:
|
|
sections.extend(["", "*Unassigned:*", *[_item_line(item) for item in unassigned[:10]]])
|
|
if blocked_prs:
|
|
sections.extend(["", "*Blocked PRs:*", *[_pr_line(pr) for pr in blocked_prs[:10]]])
|
|
if unknown_prs:
|
|
sections.extend(["", "*Unknown PR status:*", *[_pr_line(pr) for pr in unknown_prs[:10]]])
|
|
if mergeable_prs:
|
|
sections.extend(["", "*Ready to merge:*", *[_pr_line(pr) for pr in mergeable_prs[:10]]])
|
|
sections.extend(
|
|
[
|
|
"",
|
|
"*Recommended next actions:*",
|
|
*_recommended_actions(
|
|
up_for_grabs=up_for_grabs,
|
|
unassigned=unassigned,
|
|
blocked_prs=blocked_prs,
|
|
mergeable_prs=mergeable_prs,
|
|
errors=errors,
|
|
),
|
|
]
|
|
)
|
|
|
|
return WorkStatusReport(
|
|
counts={
|
|
"open_work": len(item_dicts),
|
|
"taken": len(taken),
|
|
"up_for_grabs": len(up_for_grabs),
|
|
"unassigned": len(unassigned),
|
|
"blocked_prs": len(blocked_prs),
|
|
"mergeable_prs": len(mergeable_prs),
|
|
"unknown_prs": len(unknown_prs),
|
|
},
|
|
slack_markdown="\n".join(sections).strip(),
|
|
available=not errors,
|
|
incomplete=bool(errors),
|
|
errors=errors,
|
|
)
|