Skip to content

Commit 9786504

Browse files
authored
ci: add monthly issues report (#13081)
* ci: add monthly issues report * chore: show avg + median * chore: update column label * chore: remove avg time and Other
1 parent 971bee6 commit 9786504

1 file changed

Lines changed: 169 additions & 0 deletions

File tree

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
name: Monthly Issue Report
2+
3+
on:
4+
schedule:
5+
# Run on the 1st of every month at 9:00 AM UTC
6+
- cron: "0 9 1 * *"
7+
workflow_dispatch:
8+
inputs:
9+
dry_run:
10+
description: 'Dry run (print report without updating issue)'
11+
type: boolean
12+
default: false
13+
14+
jobs:
15+
generate-report:
16+
runs-on: ubuntu-latest
17+
permissions:
18+
issues: write
19+
steps:
20+
- uses: actions/github-script@v7
21+
with:
22+
github-token: ${{ secrets.UI5_WEBCOMP_BOT_GH_TOKEN }}
23+
script: |
24+
const REPORT_ISSUE_NUMBER = 13080; // Monthly Issues Report tracking issue
25+
const isDryRun = context.eventName === 'workflow_dispatch' && ${{ inputs.dry_run || false }};
26+
27+
// All TOPIC labels to track (team labels)
28+
const topics = [
29+
'TOPIC B',
30+
'TOPIC Core',
31+
'TOPIC P',
32+
'TOPIC RD',
33+
'TOPIC RL',
34+
'TOPIC SKR',
35+
'TOPIC TBL'
36+
];
37+
38+
// Helper to check if issue is a bug (by label OR issue type)
39+
const isBug = (issue) => {
40+
const labels = issue.labels.map(l => l.name.toLowerCase());
41+
const typeName = issue.type?.name?.toLowerCase() || '';
42+
return labels.includes('bug') || typeName === 'bug';
43+
};
44+
45+
// Helper to check if issue is a feature request (by label OR issue type)
46+
const isFeatureRequest = (issue) => {
47+
const labels = issue.labels.map(l => l.name.toLowerCase());
48+
const typeName = issue.type?.name?.toLowerCase() || '';
49+
return labels.includes('feature request') || labels.includes('enhancement') || typeName === 'feature';
50+
};
51+
52+
// Calculate date range for last month
53+
const now = new Date();
54+
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
55+
const lastMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59);
56+
const monthName = lastMonth.toLocaleString('en-US', { month: 'long', year: 'numeric' });
57+
58+
const sinceDate = lastMonth.toISOString();
59+
const untilDate = lastMonthEnd.toISOString();
60+
61+
console.log(`Generating report for ${monthName}`);
62+
console.log(`Date range: ${sinceDate} to ${untilDate}`);
63+
64+
const results = [];
65+
66+
for (const topic of topics) {
67+
// Get issues with this label using raw API to include 'type' field
68+
const allIssues = await github.paginate('GET /repos/{owner}/{repo}/issues', {
69+
owner: context.repo.owner,
70+
repo: context.repo.repo,
71+
labels: topic,
72+
state: 'all',
73+
since: sinceDate,
74+
per_page: 100
75+
});
76+
77+
// Filter to issues created within the date range (exclude PRs)
78+
const opened = allIssues.filter(issue => {
79+
const created = new Date(issue.created_at);
80+
return created >= lastMonth && created <= lastMonthEnd && !issue.pull_request;
81+
});
82+
83+
// Get closed issues - filter those closed in the period
84+
const closed = allIssues.filter(issue => {
85+
if (!issue.closed_at || issue.pull_request) return false;
86+
const closedDate = new Date(issue.closed_at);
87+
return closedDate >= lastMonth && closedDate <= lastMonthEnd;
88+
});
89+
90+
// Split by type
91+
const openedBugs = opened.filter(isBug);
92+
const openedFRs = opened.filter(isFeatureRequest);
93+
const openedOther = opened.filter(i => !isBug(i) && !isFeatureRequest(i));
94+
95+
const closedBugs = closed.filter(isBug);
96+
const closedFRs = closed.filter(isFeatureRequest);
97+
const closedOther = closed.filter(i => !isBug(i) && !isFeatureRequest(i));
98+
99+
results.push({
100+
topic: topic,
101+
bugs: { opened: openedBugs.length, closed: closedBugs.length },
102+
frs: { opened: openedFRs.length, closed: closedFRs.length },
103+
other: { opened: openedOther.length, closed: closedOther.length }
104+
});
105+
}
106+
107+
// Build markdown report
108+
let report = `## ${monthName} Issue Report\n\n`;
109+
110+
// Bugs table
111+
report += `### Bugs\n\n`;
112+
report += `| Topic | Opened | Closed |\n`;
113+
report += `|-------|--------|--------|\n`;
114+
115+
let totalBugsOpened = 0, totalBugsClosed = 0;
116+
for (const r of results) {
117+
report += `| ${r.topic} | ${r.bugs.opened} | ${r.bugs.closed} |\n`;
118+
totalBugsOpened += r.bugs.opened;
119+
totalBugsClosed += r.bugs.closed;
120+
}
121+
report += `| **Total** | **${totalBugsOpened}** | **${totalBugsClosed}** |\n\n`;
122+
123+
// Feature Requests table
124+
report += `### Feature Requests\n\n`;
125+
report += `| Topic | Opened | Closed |\n`;
126+
report += `|-------|--------|--------|\n`;
127+
128+
let totalFRsOpened = 0, totalFRsClosed = 0;
129+
for (const r of results) {
130+
report += `| ${r.topic} | ${r.frs.opened} | ${r.frs.closed} |\n`;
131+
totalFRsOpened += r.frs.opened;
132+
totalFRsClosed += r.frs.closed;
133+
}
134+
report += `| **Total** | **${totalFRsOpened}** | **${totalFRsClosed}** |\n\n`;
135+
136+
// Summary
137+
report += `### Summary\n\n`;
138+
report += `| Type | Opened | Closed |\n`;
139+
report += `|------|--------|--------|\n`;
140+
report += `| Bugs | ${totalBugsOpened} | ${totalBugsClosed} |\n`;
141+
report += `| Feature Requests | ${totalFRsOpened} | ${totalFRsClosed} |\n`;
142+
const grandOpened = totalBugsOpened + totalFRsOpened;
143+
const grandClosed = totalBugsClosed + totalFRsClosed;
144+
report += `| **Total** | **${grandOpened}** | **${grandClosed}** |\n`;
145+
146+
report += `\n_Report generated: ${now.toISOString()}_\n`;
147+
148+
console.log('\n' + report);
149+
150+
if (isDryRun) {
151+
console.log('\nDry run - not updating issue');
152+
return;
153+
}
154+
155+
if (REPORT_ISSUE_NUMBER === 0) {
156+
console.log('\nREPORT_ISSUE_NUMBER not set - skipping issue update');
157+
console.log('Create a tracking issue and set REPORT_ISSUE_NUMBER to its number');
158+
return;
159+
}
160+
161+
// Add report as a comment on the tracking issue
162+
await github.rest.issues.createComment({
163+
owner: context.repo.owner,
164+
repo: context.repo.repo,
165+
issue_number: REPORT_ISSUE_NUMBER,
166+
body: report
167+
});
168+
169+
console.log(`\nAdded comment to issue #${REPORT_ISSUE_NUMBER} with new report`);

0 commit comments

Comments
 (0)