-
Notifications
You must be signed in to change notification settings - Fork 377
171 lines (161 loc) · 7.42 KB
/
Copy pathissue-labeler-assigner.yml
File metadata and controls
171 lines (161 loc) · 7.42 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
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
name: Issue Labeler / Assigner
# Three add-only triage steps that run when a reporter opens or edits an issue:
#
# 1. Label: maps the "Affected area / component" dropdown from the bug/feature
# issue forms (.github/ISSUE_TEMPLATE/) onto real repo labels, so reporters
# who lack triage permission still get their issue categorized at creation.
#
# 2. Assign: if the reporter ticked the "Contribution" checkbox ("I'm willing
# to submit a pull request ..."), assign the issue to its author so the
# volunteer is on record. addAssignees is idempotent and issue authors are
# always assignable, even as outside contributors.
#
# 3. Good first issue: if the reporter ticked the "Good first issue" checkbox,
# self-tag the issue with the `good first issue` label. Maintainers remove it
# during triage if they disagree (add-only here, see below).
#
# Add-only by design: no step removes anything. Reporters rarely deselect, and
# removing would fight maintainers who labeled or reassigned by hand. Every API
# call is idempotent, so re-running on `edited` is harmless.
#
# SECURITY: the issue body is attacker-controlled text. The label step parses
# it only for exact matches against the fixed AREA_LABELS allowlist below. The
# assign and good-first-issue steps use the body only as a boolean gate; the
# assignee comes from the trusted `payload.issue.user.login` and the label is a
# fixed constant, never body-derived, so the body cannot inject either. No
# checkout, no exec of issue contents, no token export.
on:
issues:
types: [opened, edited]
permissions:
issues: write
contents: read
jobs:
triage:
runs-on: ubuntu-24.04-arm
timeout-minutes: 5
steps:
- name: Apply area labels from issue form
uses: actions/github-script@v9
with:
script: |
// Dropdown option text -> repo label. Keep in sync with the
// `Affected area / component` options in
// .github/ISSUE_TEMPLATE/{bug_report,feature_request}.yml.
const AREA_LABELS = {
'Iggy server': 'server',
'Rust SDK': 'rust',
'Go SDK': 'go',
'Java SDK': 'java',
'Python SDK': 'python',
'C# SDK': 'csharp',
'Node.js SDK': 'javascript',
'C++ SDK': 'cpp',
'PHP SDK': 'php',
'CLI': 'tui',
'Web UI': 'web',
'Connectors': 'connectors',
'MCP server': 'mcp',
'Configuration': 'config',
'Wire protocol / API': 'api',
'Metadata': 'metadata',
'Clustering / replication': 'cluster',
'Performance': 'performance',
'Documentation': 'docs',
'CI / build / tooling': 'CI/CD',
// 'Other / not sure' intentionally maps to no label.
};
const HEADING = 'Affected area / component';
const body = context.payload.issue.body || '';
// Issue-form bodies render as `### <label>\n\n<value>`; capture
// the dropdown's value block, up to the next `###` or the end.
const esc = HEADING.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const section = body.match(
new RegExp('###[ \\t]+' + esc + '[ \\t]*\\n+([\\s\\S]*?)(?:\\n###|$)'),
);
// Multi-select renders the picks comma-separated on one line.
// Option text never contains a comma, so split on it safely.
const picks = (section ? section[1] : '')
.split(',')
.map(s => s.trim())
.filter(Boolean);
const labels = [...new Set(
picks.map(p => AREA_LABELS[p]).filter(Boolean),
)];
if (labels.length === 0) {
core.info('no mappable area selected, nothing to label');
return;
}
core.info(`applying labels: ${labels.join(', ')}`);
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
labels,
});
- name: Assign author who volunteered to contribute
uses: actions/github-script@v9
with:
script: |
// The "Contribution" checkbox in both issue forms renders as a
// task-list line. Checked, it reads `- [x] I'm willing to submit a
// pull request ...`; the tail differs per template (bug: "to fix
// this bug", feature: "to implement this feature"), so match the
// shared prefix only.
const body = context.payload.issue.body || '';
const volunteered =
/(^|\n)\s*-\s*\[[xX]\]\s+I'm willing to submit a pull request/.test(body);
if (!volunteered) {
core.info('author did not volunteer to contribute, nothing to assign');
return;
}
// Assignee is the trusted author from the event payload, never the
// body. addAssignees is a no-op if already assigned and silently
// ignores non-assignable users; issue authors are always assignable.
const author = context.payload.issue.user.login;
core.info(`assigning volunteering author: ${author}`);
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
assignees: [author],
});
- name: Self-tag good first issue
uses: actions/github-script@v9
with:
script: |
// The "Good first issue" checkbox in both issue forms renders as a
// task-list line. Checked, it reads `- [x] I think this could be a
// good first issue ...`. Reporters self-tag; maintainers may remove
// the label during triage if they disagree. addLabels is idempotent
// and add-only, consistent with the other triage steps here.
const body = context.payload.issue.body || '';
const suggested =
/(^|\n)\s*-\s*\[[xX]\]\s+I think this could be a good first issue/.test(body);
if (!suggested) {
core.info('reporter did not suggest good first issue, nothing to label');
return;
}
core.info('applying label: good first issue');
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
labels: ['good first issue'],
});