add params to actions data

This commit is contained in:
Alex Gorevski 2026-02-18 21:23:31 -08:00
parent 44725da08c
commit a17c35679e

View file

@ -1,10 +1,47 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Fetch GitHub Actions workflow runs for a given date and summarize costs.""" """Fetch GitHub Actions workflow runs for a given date and summarize costs.
Usage:
python fetch_actions_data.py [OPTIONS]
Options:
--date YYYY-MM-DD Date to query (default: yesterday)
--mode brief|full Output mode (default: full)
brief: billable minutes/hours table only
full: detailed breakdown with per-run list
--repo OWNER/NAME Repository (default: zeroclaw-labs/zeroclaw)
-h, --help Show this help message
"""
import argparse
import json import json
import subprocess import subprocess
import sys from datetime import datetime, timedelta, timezone
from datetime import datetime, timezone
def parse_args():
"""Parse command-line arguments."""
parser = argparse.ArgumentParser(
description="Fetch GitHub Actions workflow runs and summarize costs.",
)
yesterday = (datetime.now(timezone.utc) - timedelta(days=1)).strftime("%Y-%m-%d")
parser.add_argument(
"--date",
default=yesterday,
help="Date to query in YYYY-MM-DD format (default: yesterday)",
)
parser.add_argument(
"--mode",
choices=["brief", "full"],
default="full",
help="Output mode: 'brief' for billable hours only, 'full' for detailed breakdown (default: full)",
)
parser.add_argument(
"--repo",
default="zeroclaw-labs/zeroclaw",
help="Repository in OWNER/NAME format (default: zeroclaw-labs/zeroclaw)",
)
return parser.parse_args()
def fetch_runs(repo, date_str, page=1, per_page=100): def fetch_runs(repo, date_str, page=1, per_page=100):
@ -43,8 +80,10 @@ def parse_duration(started, completed):
def main(): def main():
repo = "zeroclaw-labs/zeroclaw" args = parse_args()
date_str = "2026-02-17" repo = args.repo
date_str = args.date
brief = args.mode == "brief"
print(f"Fetching workflow runs for {repo} on {date_str}...") print(f"Fetching workflow runs for {repo} on {date_str}...")
print("=" * 100) print("=" * 100)
@ -117,39 +156,53 @@ def main():
reverse=True reverse=True
) )
print("=" * 100) if brief:
print(f"{'Workflow':<40} {'Runs':>5} {'SampledJobs':>12} {'SampledMins':>12} {'Est.TotalMins':>14} {'Events'}") # Brief mode: compact billable hours table
print("-" * 100) print(f"{'Workflow':<40} {'Runs':>5} {'Est.Mins':>9} {'Est.Hours':>10}")
print("-" * 68)
grand_total_minutes = 0
for name, stats in sorted_workflows:
est_mins = stats["estimated_total_seconds"] / 60
grand_total_minutes += est_mins
print(f"{name:<40} {stats['count']:>5} {est_mins:>9.1f} {est_mins/60:>10.2f}")
print("-" * 68)
print(f"{'TOTAL':<40} {len(all_runs):>5} {grand_total_minutes:>9.0f} {grand_total_minutes/60:>10.1f}")
print(f"\nProjected monthly: ~{grand_total_minutes/60*30:.0f} hours")
else:
# Full mode: detailed breakdown with per-run list
print("=" * 100)
print(f"{'Workflow':<40} {'Runs':>5} {'SampledJobs':>12} {'SampledMins':>12} {'Est.TotalMins':>14} {'Events'}")
print("-" * 100)
grand_total_minutes = 0 grand_total_minutes = 0
for name, stats in sorted_workflows: for name, stats in sorted_workflows:
sampled_mins = stats["total_job_seconds"] / 60 sampled_mins = stats["total_job_seconds"] / 60
est_total_mins = stats["estimated_total_seconds"] / 60 est_total_mins = stats["estimated_total_seconds"] / 60
grand_total_minutes += est_total_mins grand_total_minutes += est_total_mins
events_str = ", ".join(f"{k}={v}" for k, v in stats["events"].items()) events_str = ", ".join(f"{k}={v}" for k, v in stats["events"].items())
conclusions_str = ", ".join(f"{k}={v}" for k, v in stats["conclusions"].items()) conclusions_str = ", ".join(f"{k}={v}" for k, v in stats["conclusions"].items())
print( print(
f"{name:<40} {stats['count']:>5} {stats['total_jobs']:>12} " f"{name:<40} {stats['count']:>5} {stats['total_jobs']:>12} "
f"{sampled_mins:>12.1f} {est_total_mins:>14.1f} {events_str}" f"{sampled_mins:>12.1f} {est_total_mins:>14.1f} {events_str}"
) )
print(f"{'':>40} {'':>5} {'':>12} {'':>12} {'':>14} outcomes: {conclusions_str}") print(f"{'':>40} {'':>5} {'':>12} {'':>12} {'':>14} outcomes: {conclusions_str}")
print("-" * 100) print("-" * 100)
print(f"{'GRAND TOTAL':>40} {len(all_runs):>5} {'':>12} {'':>12} {grand_total_minutes:>14.1f}") print(f"{'GRAND TOTAL':>40} {len(all_runs):>5} {'':>12} {'':>12} {grand_total_minutes:>14.1f}")
print(f"\nEstimated total billable minutes on {date_str}: {grand_total_minutes:.0f} min ({grand_total_minutes/60:.1f} hours)") print(f"\nEstimated total billable minutes on {date_str}: {grand_total_minutes:.0f} min ({grand_total_minutes/60:.1f} hours)")
print() print()
# Also show raw run list # Also show raw run list
print("\n" + "=" * 100) print("\n" + "=" * 100)
print("DETAILED RUN LIST") print("DETAILED RUN LIST")
print("=" * 100) print("=" * 100)
for run in all_runs: for run in all_runs:
name = run.get("name", "Unknown") name = run.get("name", "Unknown")
event = run.get("event", "unknown") event = run.get("event", "unknown")
conclusion = run.get("conclusion", "unknown") conclusion = run.get("conclusion", "unknown")
run_id = run.get("id") run_id = run.get("id")
started = run.get("run_started_at", "?") started = run.get("run_started_at", "?")
print(f" [{run_id}] {name:<40} conclusion={conclusion:<12} event={event:<20} started={started}") print(f" [{run_id}] {name:<40} conclusion={conclusion:<12} event={event:<20} started={started}")
if __name__ == "__main__": if __name__ == "__main__":