Skip to content

Commit 46f61eb

Browse files
committed
Add file LoC selector.
Signed-off-by: Nashwan Azhari <nazhari@cloudbasesolutions.com>
1 parent 160ab01 commit 46f61eb

5 files changed

Lines changed: 117 additions & 17 deletions

File tree

action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ runs:
3434
- ${{ inputs.token }}
3535
- --label-definitions-file
3636
- ${{ inputs.config_file_path }}
37-
- ${{ inputs.command }}
3837
- ${{ inputs.target }}
38+
- ${{ inputs.command }}
3939

4040
branding:
4141
icon: 'bookmark'

autolabeler/labelers.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ def _get_labels_for_selector_matches(
164164
except Exception as ex:
165165
msg = (
166166
f"Failed to format match data into '{self._name}' and "
167-
f"'{self._description}'. Selector match value were: {match}")
167+
f"'{self._description}'. Selector match value were: "
168+
f"{match}: {ex}")
168169
LOG.error(msg)
169170
continue
170171

@@ -183,9 +184,10 @@ def get_labels_for_repo(self, repo: Repository) -> list[LabelParams]:
183184
# If this a simple label with a static name, it always applies to the repo.
184185
try:
185186
self._name.format()
187+
self._description.format()
186188
return [LabelParams(self._name, self._color, self._description)]
187189
except KeyError:
188-
# Else, we must run an generate the selector:
190+
# Else, we must run and generate the selectors:
189191
return self._get_labels_for_selector_matches(
190192
self._run_selectors(repo))
191193

autolabeler/selectors.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def match(self, obj: Repository|PullRequest,
181181
# NOTE(aznashwan): Only Repositories/PRs have associated files.
182182
if not isinstance(obj, (Repository, PullRequest)):
183183
LOG.warn(
184-
f"FileSelector got unsupported object type {type(obj)}: {obj}")
184+
f"{self.__class__}.match() got unsupported object type {type(obj)}: {obj}")
185185
return []
186186

187187
all_files = file_cache
@@ -199,6 +199,74 @@ def match(self, obj: Repository|PullRequest,
199199
return res
200200

201201

202+
class LinesChangedSelector(Selector):
203+
204+
def __init__(
205+
self, min: int|None=None, max: int|None=None,
206+
change_type: str="total"):
207+
if min is None and max is None:
208+
raise ValueError(
209+
f"{self.__class__}: at least one of min/max is required.")
210+
self._min = min
211+
self._max = max
212+
supported_change_types = [
213+
"additions", "deletions", "total", "net"]
214+
if change_type not in supported_change_types:
215+
raise ValueError(
216+
f"Unsupported change type {change_type}. "
217+
f"Must be one of: {supported_change_types}")
218+
self._change_type = change_type
219+
220+
@classmethod
221+
def from_dict(cls, val: dict):
222+
supported_keys = ["min", "max", "type"]
223+
unsupported_keys = [k for k in val if k not in supported_keys]
224+
if unsupported_keys:
225+
raise ValueError(
226+
f"{cls}.from_dict() got unsupported keys: {unsupported_keys}")
227+
return cls(
228+
min=val.get("min"), max=val.get("max"),
229+
change_type=val.get("type", "total"))
230+
231+
def match(self, obj: Repository|PullRequest,
232+
file_cache: list[str]=[]) -> list[dict]:
233+
_ = file_cache
234+
235+
# NOTE(aznashwan): Only Repositories/PRs have associated files.
236+
if not isinstance(obj, (PullRequest, Repository)):
237+
LOG.warn(
238+
f"{self.__class__}.match() got unsupported object type {type(obj)}: {obj}")
239+
return []
240+
241+
res = {
242+
"lines-changed-min":
243+
self._min if self._min is not None else "-Inf",
244+
"lines-changed-max":
245+
self._max if self._max is not None else "+Inf"}
246+
if isinstance(obj, Repository):
247+
return [res]
248+
249+
changes = 0
250+
match self._change_type:
251+
case "total":
252+
changes = obj.additions + obj.deletions
253+
case "additions":
254+
changes = obj.additions
255+
case "deletions":
256+
changes = obj.deletions
257+
case "net":
258+
changes = obj.additions - obj.deletions
259+
case other:
260+
raise ValueError(
261+
f"Got unsupported change totalling scheme: {other}")
262+
263+
if self._min is not None and changes < self._min:
264+
return []
265+
if self._max is not None and changes >= self._max:
266+
return []
267+
return [res]
268+
269+
202270
class FileLister():
203271

204272
def __init__(self, obj: Repository|PullRequest):
@@ -231,6 +299,7 @@ def list_file_paths(self) -> list[str]:
231299
SELECTORS_NAME_MAP = {
232300
"regex": RegexSelector,
233301
"files": FilesSelector,
302+
"lines-changed": LinesChangedSelector,
234303
}
235304

236305

autolabels.yml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ pr-size-measure:
7070
This PR is between {lines-changed-min} and {lines-changed-max} lines of code.
7171
selectors:
7272
lines-changed:
73-
min: 0
73+
type: net # Can be: additions/deletions/total/net (net = additions - deletions)
74+
min: 1
7475
max: 1000
7576

7677
large:
@@ -80,5 +81,21 @@ pr-size-measure:
8081
selectors:
8182
lines-changed:
8283
min: 1000
83-
# NOTE: setting max to zero will remove any upper bound.
84-
max: 0
84+
max: 10000
85+
86+
too-big:
87+
label-color: d73a4a
88+
label-description: |
89+
This PR has more than {lines-changed-min} lines and must be broken down into smaller PRs.
90+
selectors:
91+
lines-changed:
92+
# NOTE: omitting 'max/min' will set the bounds to +/- Infinity respectively.
93+
min: 10000
94+
action:
95+
close:
96+
# Will only trigger for PRs larger than 10k lines.
97+
on: "{lines-changed-min}"
98+
with-comment: |
99+
Thank you for your contribution, but this PR is too large to be reviewed effectively.
100+
Please break down the changes into individual PRs no larger than {lines-changed-min} lines.
101+

samples/101-showcase.yml

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
# This defines a static label named 'example-label' with the given props.
1+
# This defines a static static label named 'example-label' with the given props.
22
example-label:
3-
# NOTE: 'label-color' and 'label-description' are always required.
43
label-color: 0075ca # blue
54
label-description: This is a simple example static label.
65

76

8-
# This defines two "namespaced" static labels named: label-prefix/sublabel-{1,2}.
7+
# This defines two "namespaced" labels named: label-prefix/sublabel-{1,2}.
98
label-prefix:
109
sublabel-1:
1110
label-color: 0075ca
@@ -15,7 +14,7 @@ label-prefix:
1514
label-description: This is a simple second sublabel.
1615

1716

18-
# This will define as many labels as there are file matches in the repo.
17+
# This will define as many labels as there are filepath regex matches.
1918
label-for-sample-file-{files-name-regex-group-1}:
2019
label-color: 0075ca
2120
# NOTE: "group-0" is the entire filepath.
@@ -53,12 +52,9 @@ bug:
5352
maintainer-comments-only: false
5453

5554

56-
# This will allow project maintainers to automatically generate and
57-
# set labels on Issues/PRs by commenting '/label-me-as $LABEL':
5855
manual-{regex-comments-group-2}:
5956
label-color: d73a4a
60-
label-description: |
61-
"This label was defined through a maintainer comment with: {regex-comments-group-1}"
57+
label-description: "This label was defined through a maintainer comment with: {regex-comments-group-1}"
6258
selectors:
6359
regex:
6460
maintainer-comments-only: true
@@ -74,7 +70,8 @@ pr-size-measure:
7470
This PR is between {lines-changed-min} and {lines-changed-max} lines of code.
7571
selectors:
7672
lines-changed:
77-
min: 0
73+
type: net # Can be: additions/deletions/total/net (net = additions - deletions)
74+
min: 1
7875
max: 1000
7976

8077
large:
@@ -85,4 +82,19 @@ pr-size-measure:
8582
lines-changed:
8683
min: 1000
8784
# NOTE: setting max to zero will remove any upper bound.
88-
max: 0
85+
max: 10000
86+
87+
too-big:
88+
label-color: d73a4a
89+
label-description: |
90+
This PR has more than {lines-changed-min} lines and must be broken down into smaller PRs.
91+
action:
92+
close:
93+
# Will only trigger for PRs larger than 10k lines.
94+
on: "{lines-changed-min}"
95+
with-comment: |
96+
Thank you for your contribution, but this PR is too large to be reviewed efficiently.
97+
Please break down the changes into individual PRs no larger than {lines-changed-min} lines.
98+
selectors:
99+
lines-changed:
100+
min: 10000

0 commit comments

Comments
 (0)