| name | write-report-json |
| description | Compile PR and issue metrics into a report.json file at /app/report.json using the required schema for the December community pulse write-up. |
Writing report.json
After computing all metrics, write the result to /app/report.json.
Required JSON structure
{
"pr": {
"total": <int>,
"merged": <int>,
"closed": <int>,
"avg_merge_days": <float>,
"top_contributor": <str>
},
"issue": {
"total": <int>,
"bug": <int>,
"resolved_bugs": <int>
}
}
Full end-to-end script
import json
import subprocess
from collections import Counter
from datetime import datetime, timezone
START = datetime(2024, 12, 1, tzinfo=timezone.utc)
END = datetime(2024, 12, 31, 23, 59, 59, tzinfo=timezone.utc)
def parse_dt(s):
return datetime.fromisoformat(s.replace("Z", "+00:00"))
def in_range(dt_str):
return START <= parse_dt(dt_str) <= END
pr_result = subprocess.run([
"gh", "search", "prs",
"--repo", "cli/cli",
"--created", "2024-12-01..2024-12-31",
"--limit", "200",
"--json", "number,state,createdAt,mergedAt,closedAt,author"
], capture_output=True, text=True, check=True)
all_prs = json.loads(pr_result.stdout)
prs = [pr for pr in all_prs if in_range(pr["createdAt"])]
merged_prs = [pr for pr in prs if pr.get("mergedAt")]
closed_prs = [pr pr prs pr[].lower() == pr.get()]
merged_prs:
avg_merge_days = (
(
(parse_dt(pr[]) - parse_dt(pr[])).total_seconds() /
pr merged_prs
) / (merged_prs),
)
:
avg_merge_days =
author_counts = Counter(
pr[][] pr prs pr.get()
)
top_contributor = author_counts.most_common()[][] author_counts
issue_result = subprocess.run([
, , ,
, ,
, ,
, ,
,
], capture_output=, text=, check=)
all_issues = json.loads(issue_result.stdout)
issues = [i i all_issues in_range(i[])]
():
( label[].lower() label issue.get(, []))
bug_issues = [i i issues is_bug(i)]
resolved_bugs = [
i i bug_issues
i.get() in_range(i[])
]
report = {
: {
: (prs),
: (merged_prs),
: (closed_prs),
: avg_merge_days,
: top_contributor
},
: {
: (issues),
: (bug_issues),
: (resolved_bugs)
}
}
(, ) f:
json.dump(report, f, indent=)
(json.dumps(report, indent=))
Notes
- Always post-filter by
createdAt range even when using gh search with --created, as a safety net.
resolved_bugs counts bug issues whose closedAt also falls within December 2024.
closed PRs are those with state == "closed" and no mergedAt (i.e., not merged, just closed).