mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-12 12:15:12 +00:00
Adding the workflows after I broke #802
This commit is contained in:
216
workflows/ospo-org-health.md
Normal file
216
workflows/ospo-org-health.md
Normal file
@@ -0,0 +1,216 @@
|
||||
---
|
||||
name: 'OSPO Organization Health Report'
|
||||
description: 'Comprehensive weekly health report for a GitHub organization. Surfaces stale issues/PRs, merge time analysis, contributor leaderboards, and actionable items needing human attention.'
|
||||
labels: ['ospo', 'reporting', 'org-health']
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 10 * * 1"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
organization:
|
||||
description: "GitHub organization to report on"
|
||||
type: string
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
pull-requests: read
|
||||
actions: read
|
||||
|
||||
engine: copilot
|
||||
|
||||
tools:
|
||||
github:
|
||||
toolsets:
|
||||
- repos
|
||||
- issues
|
||||
- pull_requests
|
||||
- orgs
|
||||
bash: true
|
||||
|
||||
safe-outputs:
|
||||
create-issue:
|
||||
max: 1
|
||||
title-prefix: "[Org Health] "
|
||||
|
||||
timeout-minutes: 60
|
||||
|
||||
network:
|
||||
allowed:
|
||||
- defaults
|
||||
- python
|
||||
---
|
||||
|
||||
You are an expert GitHub organization analyst. Your job is to produce a
|
||||
comprehensive weekly health report for your GitHub organization
|
||||
(provided via workflow input).
|
||||
|
||||
## Primary Goal
|
||||
|
||||
**Surface issues and PRs that need human attention**, celebrate wins, and
|
||||
provide actionable metrics so maintainers can prioritize their time.
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Determine the Organization
|
||||
|
||||
```
|
||||
ORG = inputs.organization OR "my-org"
|
||||
PERIOD_DAYS = 30
|
||||
SINCE = date 30 days ago (ISO 8601)
|
||||
STALE_ISSUE_DAYS = 60
|
||||
STALE_PR_DAYS = 30
|
||||
60_DAYS_AGO = date 60 days ago (ISO 8601)
|
||||
30_DAYS_AGO = date 30 days ago (ISO 8601, same as SINCE)
|
||||
```
|
||||
|
||||
## Step 2 — Gather Organization-Wide Aggregates (Search API)
|
||||
|
||||
Use GitHub search APIs for fast org-wide counts. These are efficient and
|
||||
avoid per-repo iteration for basic aggregates.
|
||||
|
||||
Collect the following using search queries:
|
||||
|
||||
| Metric | Search Query |
|
||||
|--------|-------------|
|
||||
| Total open issues | `org:<ORG> is:issue is:open` |
|
||||
| Total open PRs | `org:<ORG> is:pr is:open` |
|
||||
| Issues opened (last 30d) | `org:<ORG> is:issue created:>={SINCE}` |
|
||||
| Issues closed (last 30d) | `org:<ORG> is:issue is:closed closed:>={SINCE}` |
|
||||
| PRs opened (last 30d) | `org:<ORG> is:pr created:>={SINCE}` |
|
||||
| PRs merged (last 30d) | `org:<ORG> is:pr is:merged merged:>={SINCE}` |
|
||||
| PRs closed unmerged (last 30d) | `org:<ORG> is:pr is:closed is:unmerged closed:>={SINCE}` |
|
||||
| Stale issues (60+ days) | `org:<ORG> is:issue is:open updated:<={60_DAYS_AGO}` |
|
||||
| Stale PRs (30+ days) | `org:<ORG> is:pr is:open updated:<={30_DAYS_AGO}` |
|
||||
|
||||
**Performance tip:** Add 1–2 second delays between search API calls to
|
||||
stay well within rate limits.
|
||||
|
||||
## Step 3 — Stale Issues & PRs (Heat Scores)
|
||||
|
||||
For stale issues and stale PRs found above, retrieve the top results and
|
||||
sort them by **heat score** (comment count). The heat score helps
|
||||
maintainers prioritize: a stale issue with many comments signals community
|
||||
interest that is going unaddressed.
|
||||
|
||||
- **Stale issues**: Retrieve up to 50, sort by `comments` descending,
|
||||
keep top 10. For each, record: repo, number, title, days since last
|
||||
update, comment count (heat score), author, labels.
|
||||
- **Stale PRs**: Same approach — retrieve up to 50, sort by `comments`
|
||||
descending, keep top 10.
|
||||
|
||||
## Step 4 — PR Merge Time Analysis
|
||||
|
||||
From the PRs merged in the last 30 days (Step 2), retrieve a sample of
|
||||
recently merged PRs (up to 100). For each, calculate:
|
||||
|
||||
```
|
||||
merge_time = merged_at - created_at (in hours)
|
||||
```
|
||||
|
||||
Then compute percentiles:
|
||||
- **p50** (median merge time)
|
||||
- **p75**
|
||||
- **p95**
|
||||
|
||||
Use bash with Python for percentile calculations:
|
||||
|
||||
```bash
|
||||
python3 -c "
|
||||
import json, sys
|
||||
times = json.loads(sys.stdin.read())
|
||||
times.sort()
|
||||
n = len(times)
|
||||
if n == 0:
|
||||
print('No data')
|
||||
else:
|
||||
p50 = times[int(n * 0.50)]
|
||||
p75 = times[int(n * 0.75)]
|
||||
p95 = times[int(n * 0.95)] if n >= 20 else times[-1]
|
||||
print(f'p50={p50:.1f}h, p75={p75:.1f}h, p95={p95:.1f}h')
|
||||
"
|
||||
```
|
||||
|
||||
## Step 5 — First Response Time
|
||||
|
||||
For issues and PRs opened in the last 30 days, sample up to 50 of each.
|
||||
For each item, find the first comment (excluding the author). Calculate:
|
||||
|
||||
```
|
||||
first_response_time = first_comment.created_at - item.created_at (in hours)
|
||||
```
|
||||
|
||||
Report median first response time for issues and PRs separately.
|
||||
|
||||
## Step 6 — Repository Activity & Contributor Leaderboard
|
||||
|
||||
### Top 10 Active Repos
|
||||
List all non-archived repos in the org. For each, count pushes / commits /
|
||||
issues+PRs opened in the last 30 days. Sort by total activity, keep top 10.
|
||||
|
||||
### Contributor Leaderboard
|
||||
From the top 10 active repos, aggregate commit authors over the last 30
|
||||
days. Rank by commit count, keep top 10. Award:
|
||||
- 🥇 for #1
|
||||
- 🥈 for #2
|
||||
- 🥉 for #3
|
||||
|
||||
### Inactive Repos
|
||||
Repos with 0 pushes, 0 issues, 0 PRs in the last 30 days. List them
|
||||
(name + last push date) so the org can decide whether to archive.
|
||||
|
||||
## Step 7 — Health Alerts & Trends
|
||||
|
||||
Compute velocity indicators and assign status:
|
||||
|
||||
| Indicator | 🟢 Green | 🟡 Yellow | 🔴 Red |
|
||||
|-----------|----------|-----------|--------|
|
||||
| Issue close rate | closed ≥ opened | closed ≥ 70% opened | closed < 70% opened |
|
||||
| PR merge rate | merged ≥ opened | merged ≥ 60% opened | merged < 60% opened |
|
||||
| Median merge time | < 24h | 24–72h | > 72h |
|
||||
| Median first response | < 24h | 24–72h | > 72h |
|
||||
| Stale issue count | < 10 | 10–50 | > 50 |
|
||||
| Stale PR count | < 5 | 5–20 | > 20 |
|
||||
|
||||
## Step 8 — Wins & Shoutouts
|
||||
|
||||
Celebrate positive signals:
|
||||
- PRs merged with fast turnaround (< 4 hours)
|
||||
- Issues closed quickly (< 24 hours from open to close)
|
||||
- Top contributors (from leaderboard)
|
||||
- Repos with zero stale items
|
||||
|
||||
## Step 9 — Compose the Report
|
||||
|
||||
Create a single issue in the org's `.github` repository (or the most
|
||||
appropriate central repo) with the title:
|
||||
|
||||
```
|
||||
[Org Health] Weekly Report — <DATE>
|
||||
```
|
||||
|
||||
The issue body should include these sections in order:
|
||||
|
||||
1. **Header** — org name, period, generation date
|
||||
2. **🚨 Health Alerts** — table of indicators with 🟢/🟡/🔴 status and values
|
||||
3. **🏆 Wins & Shoutouts** — fast merges, quick closes, top contributors
|
||||
4. **📋 Stale Issues** — top 10 by heat score (repo, issue, days stale, comment count, labels)
|
||||
5. **📋 Stale PRs** — top 10 by heat score (repo, PR, days stale, comment count, author)
|
||||
6. **⏱️ PR Merge Time** — p50, p75, p95 percentiles
|
||||
7. **⚡ First Response Time** — median for issues and PRs
|
||||
8. **📊 Top 10 Active Repos** — sorted by total activity (issues + PRs + commits)
|
||||
9. **👥 Contributor Leaderboard** — top 10 by commits with 🥇🥈🥉
|
||||
10. **😴 Inactive Repos** — repos with 0 activity in 30 days
|
||||
|
||||
Use markdown tables for all data sections.
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **Update the organization name** in the frontmatter before use.
|
||||
- If any API call fails, note it in the report and continue with available
|
||||
data. Do not let a single failure block the entire report.
|
||||
- Keep the issue body under 65,000 characters (GitHub issue body limit).
|
||||
- All times should be reported in hours. Convert to days only if > 72 hours.
|
||||
- Use the `safe-outputs` constraint: only create 1 issue, with title
|
||||
prefixed `[Org Health] `.
|
||||
Reference in New Issue
Block a user