Skip to content

Commit c89e8da

Browse files
committed
✨ feat: add approval automation for special interest groups
1 parent 796349d commit c89e8da

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed

.github/workflows/sig_approval.yml

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: SIG approval automation
2+
3+
on:
4+
issues:
5+
types: [opened, closed, labeled, unlabeled]
6+
issue_comment:
7+
types: [created, edited]
8+
9+
jobs:
10+
sig_approval:
11+
# Only run for SIG proposal issues
12+
if: startsWith(github.event.issue.title, 'New special interest group')
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Handle SIG approval logic
17+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea #v7.0.1
18+
with:
19+
github-token: ${{ secrets.nf_core_bot_auth_token }}
20+
script: |
21+
const ApprovalManager = require('./.github/workflows/lib/approval.js');
22+
23+
const issueNumber = context.issue.number;
24+
const org = context.repo.owner;
25+
const repo = context.repo.repo;
26+
27+
// Initialize approval manager
28+
const approvalManager = await new ApprovalManager(github, org, repo, issueNumber).initialize();
29+
30+
// Ignore comments on closed issues
31+
if (context.eventName === 'issue_comment' && context.payload.issue.state === 'closed') {
32+
console.log('Comment event on closed issue, ignoring.');
33+
return;
34+
}
35+
36+
const requiredCoreApprovals = 2;
37+
console.log(`Required core team approvals: ${requiredCoreApprovals}`);
38+
39+
function generateStatusBody(status) {
40+
const approvers = [...approvalManager.coreApprovals];
41+
const rejecters = [...approvalManager.coreRejections];
42+
const awaiting = approvalManager.awaitingCore;
43+
44+
let body = `## Approval status: ${status}\n\nSIG proposal requires approvals from ${requiredCoreApprovals} core team members.\n\n`;
45+
body += `Current approvals: ${approvalManager.coreApprovals.size}/${requiredCoreApprovals}\n\n`;
46+
47+
if (approvers.length > 0 || rejecters.length > 0 || awaiting.length > 0) {
48+
body += `|Review Status|Core Team members|\n|--|--|\n`;
49+
if (approvers.length > 0) {
50+
body += `| ✅ Approved | ${approvalManager.formatUserList(approvers)} |\n`;
51+
}
52+
if (rejecters.length > 0) {
53+
body += `| ❌ Rejected | ${approvalManager.formatUserList(rejecters)} |\n`;
54+
}
55+
if (awaiting.length > 0) {
56+
body += `| 🕐 Pending | ${approvalManager.formatUserList(awaiting)} |\n`;
57+
}
58+
}
59+
return body;
60+
}
61+
62+
// Handle label changes
63+
if (context.eventName === 'issues' && (context.payload.action === 'labeled' || context.payload.action === 'unlabeled')) {
64+
const label = context.payload.label.name;
65+
if (label === 'timed-out') {
66+
console.log('Timed-out label detected, updating status');
67+
const statusBody = generateStatusBody('⏰ Timed Out');
68+
await approvalManager.updateStatusComment(statusBody);
69+
return;
70+
}
71+
}
72+
73+
// Handle new issue creation
74+
if (context.eventName === 'issues' && context.payload.action === 'opened') {
75+
const body = generateStatusBody('🕐 Pending');
76+
console.log('Creating initial comment for review status');
77+
await approvalManager.updateStatusComment(body);
78+
await approvalManager.updateIssueStatus('🕐 Pending');
79+
return;
80+
}
81+
82+
// Determine status
83+
let status = '🕐 Pending';
84+
85+
if (context.eventName === 'issues' && context.payload.action === 'closed' && context.payload.issue.state_reason === 'not_planned' && (approvalManager.coreRejections.size > 0)) {
86+
status = '❌ Rejected';
87+
} else if (approvalManager.coreApprovals.size >= requiredCoreApprovals) {
88+
status = '✅ Approved';
89+
}
90+
91+
const statusBody = generateStatusBody(status);
92+
console.log('New status body to post:\n', statusBody);
93+
94+
await approvalManager.updateStatusComment(statusBody);
95+
await approvalManager.updateIssueStatus(status);

0 commit comments

Comments
 (0)