Skip to content

Commit

Permalink
Add file LoC selector.
Browse files Browse the repository at this point in the history
Signed-off-by: Nashwan Azhari <[email protected]>
  • Loading branch information
aznashwan committed Jul 31, 2023
1 parent 160ab01 commit 46f61eb
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 17 deletions.
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ runs:
- ${{ inputs.token }}
- --label-definitions-file
- ${{ inputs.config_file_path }}
- ${{ inputs.command }}
- ${{ inputs.target }}
- ${{ inputs.command }}

branding:
icon: 'bookmark'
Expand Down
6 changes: 4 additions & 2 deletions autolabeler/labelers.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ def _get_labels_for_selector_matches(
except Exception as ex:
msg = (
f"Failed to format match data into '{self._name}' and "
f"'{self._description}'. Selector match value were: {match}")
f"'{self._description}'. Selector match value were: "
f"{match}: {ex}")
LOG.error(msg)
continue

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

Expand Down
71 changes: 70 additions & 1 deletion autolabeler/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def match(self, obj: Repository|PullRequest,
# NOTE(aznashwan): Only Repositories/PRs have associated files.
if not isinstance(obj, (Repository, PullRequest)):
LOG.warn(
f"FileSelector got unsupported object type {type(obj)}: {obj}")
f"{self.__class__}.match() got unsupported object type {type(obj)}: {obj}")
return []

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


class LinesChangedSelector(Selector):

def __init__(
self, min: int|None=None, max: int|None=None,
change_type: str="total"):
if min is None and max is None:
raise ValueError(
f"{self.__class__}: at least one of min/max is required.")
self._min = min
self._max = max
supported_change_types = [
"additions", "deletions", "total", "net"]
if change_type not in supported_change_types:
raise ValueError(
f"Unsupported change type {change_type}. "
f"Must be one of: {supported_change_types}")
self._change_type = change_type

@classmethod
def from_dict(cls, val: dict):
supported_keys = ["min", "max", "type"]
unsupported_keys = [k for k in val if k not in supported_keys]
if unsupported_keys:
raise ValueError(
f"{cls}.from_dict() got unsupported keys: {unsupported_keys}")
return cls(
min=val.get("min"), max=val.get("max"),
change_type=val.get("type", "total"))

def match(self, obj: Repository|PullRequest,
file_cache: list[str]=[]) -> list[dict]:
_ = file_cache

# NOTE(aznashwan): Only Repositories/PRs have associated files.
if not isinstance(obj, (PullRequest, Repository)):
LOG.warn(
f"{self.__class__}.match() got unsupported object type {type(obj)}: {obj}")
return []

res = {
"lines-changed-min":
self._min if self._min is not None else "-Inf",
"lines-changed-max":
self._max if self._max is not None else "+Inf"}
if isinstance(obj, Repository):
return [res]

changes = 0
match self._change_type:
case "total":
changes = obj.additions + obj.deletions
case "additions":
changes = obj.additions
case "deletions":
changes = obj.deletions
case "net":
changes = obj.additions - obj.deletions
case other:
raise ValueError(
f"Got unsupported change totalling scheme: {other}")

if self._min is not None and changes < self._min:
return []
if self._max is not None and changes >= self._max:
return []
return [res]


class FileLister():

def __init__(self, obj: Repository|PullRequest):
Expand Down Expand Up @@ -231,6 +299,7 @@ def list_file_paths(self) -> list[str]:
SELECTORS_NAME_MAP = {
"regex": RegexSelector,
"files": FilesSelector,
"lines-changed": LinesChangedSelector,
}


Expand Down
23 changes: 20 additions & 3 deletions autolabels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ pr-size-measure:
This PR is between {lines-changed-min} and {lines-changed-max} lines of code.
selectors:
lines-changed:
min: 0
type: net # Can be: additions/deletions/total/net (net = additions - deletions)
min: 1
max: 1000

large:
Expand All @@ -80,5 +81,21 @@ pr-size-measure:
selectors:
lines-changed:
min: 1000
# NOTE: setting max to zero will remove any upper bound.
max: 0
max: 10000

too-big:
label-color: d73a4a
label-description: |
This PR has more than {lines-changed-min} lines and must be broken down into smaller PRs.
selectors:
lines-changed:
# NOTE: omitting 'max/min' will set the bounds to +/- Infinity respectively.
min: 10000
action:
close:
# Will only trigger for PRs larger than 10k lines.
on: "{lines-changed-min}"
with-comment: |
Thank you for your contribution, but this PR is too large to be reviewed effectively.
Please break down the changes into individual PRs no larger than {lines-changed-min} lines.
32 changes: 22 additions & 10 deletions samples/101-showcase.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# This defines a static label named 'example-label' with the given props.
# This defines a static static label named 'example-label' with the given props.
example-label:
# NOTE: 'label-color' and 'label-description' are always required.
label-color: 0075ca # blue
label-description: This is a simple example static label.


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


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


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

large:
Expand All @@ -85,4 +82,19 @@ pr-size-measure:
lines-changed:
min: 1000
# NOTE: setting max to zero will remove any upper bound.
max: 0
max: 10000

too-big:
label-color: d73a4a
label-description: |
This PR has more than {lines-changed-min} lines and must be broken down into smaller PRs.
action:
close:
# Will only trigger for PRs larger than 10k lines.
on: "{lines-changed-min}"
with-comment: |
Thank you for your contribution, but this PR is too large to be reviewed efficiently.
Please break down the changes into individual PRs no larger than {lines-changed-min} lines.
selectors:
lines-changed:
min: 10000

0 comments on commit 46f61eb

Please sign in to comment.