-
Notifications
You must be signed in to change notification settings - Fork 493
201 lines (179 loc) · 7.64 KB
/
Copy pathevaluate-submission.yml
File metadata and controls
201 lines (179 loc) · 7.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
name: Evaluate Submission
on:
issues:
types: [edited, labeled]
issue_comment:
types: [created]
jobs:
evaluate:
if: github.event_name == 'issues' && github.event.action == 'labeled' && github.event.label.name == 'suggestion'
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
issues: write
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- run: pip install pyyaml
- name: Extract GitHub repo from issue body
id: extract
uses: actions/github-script@v9
with:
script: |
const body = context.payload.issue.body || '';
function normalizeGitHubRepo(value) {
if (!value) return '';
const trimmed = value.trim();
const slugMatch = trimmed.match(/^(?:https?:\/\/github\.com\/)?([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+?)(?:\.git|\/)?$/);
return slugMatch ? slugMatch[1] : '';
}
// Try the structured "github" field first (from issue template)
const ghFieldMatch = body.match(/### GitHub Repository\s*\n\s*\n?\s*([\s\S]*?)(?=\n###|$)/);
const ghRepo = normalizeGitHubRepo(ghFieldMatch ? ghFieldMatch[1] : '');
if (ghRepo) {
core.setOutput('repo', ghRepo);
return;
}
// Fallback: extract from any GitHub URL in the body
const urlMatch = body.match(/github\.com\/([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+)/);
if (urlMatch) {
core.setOutput('repo', urlMatch[1]);
return;
}
core.setOutput('repo', '');
- name: Run evaluation
if: steps.extract.outputs.repo != ''
id: eval
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
REPORT=$(python3 scripts/evaluate_entry.py --data-dir data/ "${{ steps.extract.outputs.repo }}" 2>&1) || true
echo "$REPORT" > /tmp/eval-report.md
python3 scripts/evaluate_entry.py --data-dir data/ --json "${{ steps.extract.outputs.repo }}" 2>/dev/null > /tmp/eval-result.json || true
REC=$(python3 -c "import sys,json; print(json.load(open('/tmp/eval-result.json')).get('recommendation','error'))" 2>/dev/null || echo "error")
ARCHIVED=$(python3 -c "import sys,json; print(str(json.load(open('/tmp/eval-result.json')).get('archived',False)).lower())" 2>/dev/null || echo "false")
echo "recommendation=$REC" >> "$GITHUB_OUTPUT"
echo "archived=$ARCHIVED" >> "$GITHUB_OUTPUT"
- name: Post evaluation comment
if: steps.extract.outputs.repo != ''
uses: actions/github-script@v9
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('/tmp/eval-report.md', 'utf8');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: report
});
- name: Apply label and handle outcome
if: steps.extract.outputs.repo != ''
uses: actions/github-script@v9
with:
script: |
const rec = '${{ steps.eval.outputs.recommendation }}';
const archived = '${{ steps.eval.outputs.archived }}' === 'true';
const issue = context.issue.number;
const owner = context.repo.owner;
const repo = context.repo.repo;
// Archived rejects close immediately; 0-score rejects stay in incubator queue
let label;
if (rec === 'reject' && archived) {
label = 'rejected';
} else {
const labelMap = {
'accept': 'accepted',
'likely_accept': 'accepted',
'incubator': 'incubator',
'reject': 'incubator',
'duplicate': 'duplicate',
'error': 'needs-review',
};
label = labelMap[rec] || 'needs-review';
}
await github.rest.issues.addLabels({ owner, repo, issue_number: issue, labels: [label] });
if (label === 'accepted') {
await github.rest.actions.createWorkflowDispatch({
owner,
repo,
workflow_id: 'add-accepted-entry.yml',
ref: context.payload.repository.default_branch,
inputs: {
issue_number: String(issue),
},
});
}
// Auto-close duplicates and archived rejects
if (rec === 'duplicate' || (rec === 'reject' && archived)) {
await github.rest.issues.update({
owner, repo, issue_number: issue,
state: 'closed',
state_reason: 'not_planned',
});
}
- name: Post fallback comment (no repo found)
if: steps.extract.outputs.repo == ''
uses: actions/github-script@v9
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: [
'👋 Thanks for the suggestion!',
'',
'I couldn\'t find a GitHub repository in this issue. Could you add the **GitHub Repository** field ',
'(e.g., `owner/repo`) so I can run an automated evaluation?',
'',
'If the project isn\'t on GitHub, please reply confirming that this should be reviewed as a non-GitHub resource and include any public repository or documentation link maintainers should use.',
'',
'This issue is labeled `question` while waiting for author input. If there is no update for about two weeks, automation will mark it stale; after another two weeks without activity, it may close automatically.',
].join('\n')
});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['needs-review', 'question']
});
clear-author-question:
if: |
(github.event_name == 'issues' && github.event.action == 'edited') ||
(github.event_name == 'issue_comment' && !github.event.issue.pull_request)
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Clear author-action labels after author update
uses: actions/github-script@v9
with:
script: |
const issue = context.payload.issue;
const labels = (issue.labels || []).map(label => typeof label === 'string' ? label : label.name);
if (!labels.includes('suggestion') || !labels.includes('question')) {
return;
}
const author = issue.user.login;
const sender = context.payload.sender.login;
if (sender !== author) {
return;
}
const owner = context.repo.owner;
const repo = context.repo.repo;
const issue_number = issue.number;
async function removeLabel(label) {
try {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label });
} catch (error) {
if (error.status !== 404) {
throw error;
}
}
}
await removeLabel('question');
await removeLabel('stale');