Skip to content

Commit d24a964

Browse files
authored
Merge pull request #819 from github/ospo-workflows
add OSPO agentic workflow examples
2 parents 6b4da94 + 1f2afa4 commit d24a964

5 files changed

Lines changed: 603 additions & 0 deletions

File tree

docs/README.workflows.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-agentic-workflows) for guideline
3434
| Name | Description | Triggers |
3535
| ---- | ----------- | -------- |
3636
| [Daily Issues Report](../workflows/daily-issues-report.md) | Generates a daily summary of open issues and recent activity as a GitHub issue | schedule |
37+
| [OSPO Contributors Report](../workflows/ospo-contributors-report.md) | Monthly contributor activity metrics across an organization's repositories. | schedule, workflow_dispatch |
38+
| [OSPO Organization Health Report](../workflows/ospo-org-health.md) | Comprehensive weekly health report for a GitHub organization. Surfaces stale issues/PRs, merge time analysis, contributor leaderboards, and actionable items needing human attention. | schedule, workflow_dispatch |
39+
| [OSPO Stale Repository Report](../workflows/ospo-stale-repos.md) | Identifies inactive repositories in your organization and generates an archival recommendation report. | schedule, workflow_dispatch |
40+
| [OSS Release Compliance Checker](../workflows/ospo-release-compliance-checker.md) | Analyzes a target repository against open source release requirements and posts a detailed compliance report as an issue comment. | issues, workflow_dispatch |
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
name: 'OSPO Contributors Report'
3+
description: 'Monthly contributor activity metrics across an organization''s repositories.'
4+
labels: ['ospo', 'reporting', 'contributors']
5+
on:
6+
schedule:
7+
- cron: "3 2 1 * *"
8+
workflow_dispatch:
9+
inputs:
10+
organization:
11+
description: "GitHub organization to analyze (e.g. github)"
12+
required: false
13+
type: string
14+
repositories:
15+
description: "Comma-separated list of repos to analyze (e.g. owner/repo1,owner/repo2)"
16+
required: false
17+
type: string
18+
start_date:
19+
description: "Start date for the report period (YYYY-MM-DD)"
20+
required: false
21+
type: string
22+
end_date:
23+
description: "End date for the report period (YYYY-MM-DD)"
24+
required: false
25+
type: string
26+
sponsor_info:
27+
description: "Include GitHub Sponsors information for contributors"
28+
required: false
29+
type: boolean
30+
default: false
31+
32+
permissions:
33+
contents: read
34+
issues: read
35+
pull-requests: read
36+
37+
engine: copilot
38+
39+
tools:
40+
github:
41+
toolsets:
42+
- repos
43+
- issues
44+
- pull_requests
45+
- orgs
46+
- users
47+
bash: true
48+
49+
safe-outputs:
50+
create-issue:
51+
max: 1
52+
title-prefix: "[Contributors Report] "
53+
54+
timeout-minutes: 60
55+
---
56+
57+
# Contributors Report
58+
59+
Generate a contributors report for the specified organization or repositories.
60+
61+
## Step 1: Validate Configuration
62+
63+
Check the workflow inputs. Either `organization` or `repositories` must be provided.
64+
65+
- If **both** are empty and this is a **scheduled run**, default to analyzing all public repositories in the organization that owns the current repository. Determine the org from the `GITHUB_REPOSITORY` environment variable (the part before the `/`).
66+
- If **both** are empty and this is a **manual dispatch**, fail with a clear error message: "You must provide either an organization or a comma-separated list of repositories."
67+
- If **both** are provided, prefer `repositories` and ignore `organization`.
68+
69+
## Step 2: Determine Date Range
70+
71+
- If `start_date` and `end_date` are provided, use them.
72+
- Otherwise, default to the **previous calendar month**. For example, if today is 2025-03-15, the range is 2025-02-01 to 2025-02-28.
73+
- Use bash to compute the dates if needed. Store them as `START_DATE` and `END_DATE`.
74+
75+
## Step 3: Enumerate Repositories
76+
77+
- If `repositories` input was provided, split the comma-separated string into a list. Each entry should be in `owner/repo` format.
78+
- If `organization` input was provided (or defaulted from Step 1), list all **public, non-archived, non-fork** repositories in the organization using the GitHub API. Collect their `owner/repo` identifiers.
79+
80+
## Step 4: Collect Contributors from Commit History
81+
82+
For each repository in scope:
83+
84+
1. Use the GitHub API to list commits between `START_DATE` and `END_DATE` (use the `since` and `until` parameters on the commits endpoint).
85+
2. For each commit, extract the **author login** (from `author.login` on the commit object).
86+
3. **Exclude bot accounts**: skip any contributor whose username contains `[bot]` or whose `type` field is `"Bot"`.
87+
4. Track per-contributor:
88+
- Total number of commits across all repos.
89+
- The set of repos they contributed to.
90+
91+
Use bash to aggregate and deduplicate the contributor data across all repositories.
92+
93+
## Step 5: Determine New vs Returning Contributors
94+
95+
For each contributor found in Step 4, check whether they have **any commits before `START_DATE`** in any of the in-scope repositories.
96+
97+
- If a contributor has **no commits before `START_DATE`**, mark them as a **New Contributor**.
98+
- Otherwise, mark them as a **Returning Contributor**.
99+
100+
## Step 6: Collect Sponsor Information (Optional)
101+
102+
If the `sponsor_info` input is `true`:
103+
104+
1. For each contributor, check whether they have a GitHub Sponsors profile by querying the user's profile via the GitHub API.
105+
2. If the user has sponsorship enabled, record their sponsor URL as `https://github.com/sponsors/<username>`.
106+
3. If not, leave the sponsor field empty.
107+
108+
## Step 7: Generate Markdown Report
109+
110+
Build a markdown report with the following structure:
111+
112+
### Summary Table
113+
114+
| Metric | Value |
115+
|---|---|
116+
| Total Contributors | count |
117+
| Total Contributions (Commits) | count |
118+
| New Contributors | count |
119+
| Returning Contributors | count |
120+
| % New Contributors | percentage |
121+
122+
### Contributors Detail Table
123+
124+
Sort contributors by commit count descending.
125+
126+
| # | Username | Contribution Count | New Contributor | Sponsor URL | Commits |
127+
|---|---|---|---|---|---|
128+
| 1 | @username | 42 | Yes | [Sponsor](url) | [View](commits-url) |
129+
130+
- The **Username** column should link to the contributor's GitHub profile.
131+
- The **Sponsor URL** column should show "N/A" if `sponsor_info` is false or the user has no Sponsors page.
132+
- The **Commits** column should link to a filtered commits view.
133+
134+
## Step 8: Create Issue with Report
135+
136+
Create an issue in the **current repository** with:
137+
138+
- **Title:** `[Contributors Report] <ORG_OR_REPO_SCOPE> — START_DATE to END_DATE`
139+
- **Body:** The full markdown report from Step 7.
140+
- **Labels:** Add the label `contributors-report` if it exists; do not fail if it does not.

workflows/ospo-org-health.md

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
---
2+
name: 'OSPO Organization Health Report'
3+
description: 'Comprehensive weekly health report for a GitHub organization. Surfaces stale issues/PRs, merge time analysis, contributor leaderboards, and actionable items needing human attention.'
4+
labels: ['ospo', 'reporting', 'org-health']
5+
on:
6+
schedule:
7+
- cron: "0 10 * * 1"
8+
workflow_dispatch:
9+
inputs:
10+
organization:
11+
description: "GitHub organization to report on"
12+
type: string
13+
required: true
14+
15+
permissions:
16+
contents: read
17+
issues: read
18+
pull-requests: read
19+
actions: read
20+
21+
engine: copilot
22+
23+
tools:
24+
github:
25+
toolsets:
26+
- repos
27+
- issues
28+
- pull_requests
29+
- orgs
30+
bash: true
31+
32+
safe-outputs:
33+
create-issue:
34+
max: 1
35+
title-prefix: "[Org Health] "
36+
37+
timeout-minutes: 60
38+
39+
network:
40+
allowed:
41+
- defaults
42+
- python
43+
---
44+
45+
You are an expert GitHub organization analyst. Your job is to produce a
46+
comprehensive weekly health report for your GitHub organization
47+
(provided via workflow input).
48+
49+
## Primary Goal
50+
51+
**Surface issues and PRs that need human attention**, celebrate wins, and
52+
provide actionable metrics so maintainers can prioritize their time.
53+
54+
---
55+
56+
## Step 1 — Determine the Organization
57+
58+
```
59+
ORG = inputs.organization OR "my-org"
60+
PERIOD_DAYS = 30
61+
SINCE = date 30 days ago (ISO 8601)
62+
STALE_ISSUE_DAYS = 60
63+
STALE_PR_DAYS = 30
64+
60_DAYS_AGO = date 60 days ago (ISO 8601)
65+
30_DAYS_AGO = date 30 days ago (ISO 8601, same as SINCE)
66+
```
67+
68+
## Step 2 — Gather Organization-Wide Aggregates (Search API)
69+
70+
Use GitHub search APIs for fast org-wide counts. These are efficient and
71+
avoid per-repo iteration for basic aggregates.
72+
73+
Collect the following using search queries:
74+
75+
| Metric | Search Query |
76+
|--------|-------------|
77+
| Total open issues | `org:<ORG> is:issue is:open` |
78+
| Total open PRs | `org:<ORG> is:pr is:open` |
79+
| Issues opened (last 30d) | `org:<ORG> is:issue created:>={SINCE}` |
80+
| Issues closed (last 30d) | `org:<ORG> is:issue is:closed closed:>={SINCE}` |
81+
| PRs opened (last 30d) | `org:<ORG> is:pr created:>={SINCE}` |
82+
| PRs merged (last 30d) | `org:<ORG> is:pr is:merged merged:>={SINCE}` |
83+
| PRs closed unmerged (last 30d) | `org:<ORG> is:pr is:closed is:unmerged closed:>={SINCE}` |
84+
| Stale issues (60+ days) | `org:<ORG> is:issue is:open updated:<={60_DAYS_AGO}` |
85+
| Stale PRs (30+ days) | `org:<ORG> is:pr is:open updated:<={30_DAYS_AGO}` |
86+
87+
**Performance tip:** Add 1–2 second delays between search API calls to
88+
stay well within rate limits.
89+
90+
## Step 3 — Stale Issues & PRs (Heat Scores)
91+
92+
For stale issues and stale PRs found above, retrieve the top results and
93+
sort them by **heat score** (comment count). The heat score helps
94+
maintainers prioritize: a stale issue with many comments signals community
95+
interest that is going unaddressed.
96+
97+
- **Stale issues**: Retrieve up to 50, sort by `comments` descending,
98+
keep top 10. For each, record: repo, number, title, days since last
99+
update, comment count (heat score), author, labels.
100+
- **Stale PRs**: Same approach — retrieve up to 50, sort by `comments`
101+
descending, keep top 10.
102+
103+
## Step 4 — PR Merge Time Analysis
104+
105+
From the PRs merged in the last 30 days (Step 2), retrieve a sample of
106+
recently merged PRs (up to 100). For each, calculate:
107+
108+
```
109+
merge_time = merged_at - created_at (in hours)
110+
```
111+
112+
Then compute percentiles:
113+
- **p50** (median merge time)
114+
- **p75**
115+
- **p95**
116+
117+
Use bash with Python for percentile calculations:
118+
119+
```bash
120+
python3 -c "
121+
import json, sys
122+
times = json.loads(sys.stdin.read())
123+
times.sort()
124+
n = len(times)
125+
if n == 0:
126+
print('No data')
127+
else:
128+
p50 = times[int(n * 0.50)]
129+
p75 = times[int(n * 0.75)]
130+
p95 = times[int(n * 0.95)] if n >= 20 else times[-1]
131+
print(f'p50={p50:.1f}h, p75={p75:.1f}h, p95={p95:.1f}h')
132+
"
133+
```
134+
135+
## Step 5 — First Response Time
136+
137+
For issues and PRs opened in the last 30 days, sample up to 50 of each.
138+
For each item, find the first comment (excluding the author). Calculate:
139+
140+
```
141+
first_response_time = first_comment.created_at - item.created_at (in hours)
142+
```
143+
144+
Report median first response time for issues and PRs separately.
145+
146+
## Step 6 — Repository Activity & Contributor Leaderboard
147+
148+
### Top 10 Active Repos
149+
List all non-archived repos in the org. For each, count pushes / commits /
150+
issues+PRs opened in the last 30 days. Sort by total activity, keep top 10.
151+
152+
### Contributor Leaderboard
153+
From the top 10 active repos, aggregate commit authors over the last 30
154+
days. Rank by commit count, keep top 10. Award:
155+
- 🥇 for #1
156+
- 🥈 for #2
157+
- 🥉 for #3
158+
159+
### Inactive Repos
160+
Repos with 0 pushes, 0 issues, 0 PRs in the last 30 days. List them
161+
(name + last push date) so the org can decide whether to archive.
162+
163+
## Step 7 — Health Alerts & Trends
164+
165+
Compute velocity indicators and assign status:
166+
167+
| Indicator | 🟢 Green | 🟡 Yellow | 🔴 Red |
168+
|-----------|----------|-----------|--------|
169+
| Issue close rate | closed ≥ opened | closed ≥ 70% opened | closed < 70% opened |
170+
| PR merge rate | merged ≥ opened | merged ≥ 60% opened | merged < 60% opened |
171+
| Median merge time | < 24h | 24–72h | > 72h |
172+
| Median first response | < 24h | 24–72h | > 72h |
173+
| Stale issue count | < 10 | 10–50 | > 50 |
174+
| Stale PR count | < 5 | 5–20 | > 20 |
175+
176+
## Step 8 — Wins & Shoutouts
177+
178+
Celebrate positive signals:
179+
- PRs merged with fast turnaround (< 4 hours)
180+
- Issues closed quickly (< 24 hours from open to close)
181+
- Top contributors (from leaderboard)
182+
- Repos with zero stale items
183+
184+
## Step 9 — Compose the Report
185+
186+
Create a single issue in the org's `.github` repository (or the most
187+
appropriate central repo) with the title:
188+
189+
```
190+
[Org Health] Weekly Report — <DATE>
191+
```
192+
193+
The issue body should include these sections in order:
194+
195+
1. **Header** — org name, period, generation date
196+
2. **🚨 Health Alerts** — table of indicators with 🟢/🟡/🔴 status and values
197+
3. **🏆 Wins & Shoutouts** — fast merges, quick closes, top contributors
198+
4. **📋 Stale Issues** — top 10 by heat score (repo, issue, days stale, comment count, labels)
199+
5. **📋 Stale PRs** — top 10 by heat score (repo, PR, days stale, comment count, author)
200+
6. **⏱️ PR Merge Time** — p50, p75, p95 percentiles
201+
7. **⚡ First Response Time** — median for issues and PRs
202+
8. **📊 Top 10 Active Repos** — sorted by total activity (issues + PRs + commits)
203+
9. **👥 Contributor Leaderboard** — top 10 by commits with 🥇🥈🥉
204+
10. **😴 Inactive Repos** — repos with 0 activity in 30 days
205+
206+
Use markdown tables for all data sections.
207+
208+
## Important Notes
209+
210+
- **Update the organization name** in the frontmatter before use.
211+
- If any API call fails, note it in the report and continue with available
212+
data. Do not let a single failure block the entire report.
213+
- Keep the issue body under 65,000 characters (GitHub issue body limit).
214+
- All times should be reported in hours. Convert to days only if > 72 hours.
215+
- Use the `safe-outputs` constraint: only create 1 issue, with title
216+
prefixed `[Org Health] `.

0 commit comments

Comments
 (0)