Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,19 @@ _These lists are automatically generated, and may not be complete or may contain
duplicates._
"""

# Profiles that are excluded from the contributor list.
# Usernames that are excluded from the contributor list. This may also ignore
# pull request of these users (see field `ignore_prs_by_username`).
ignored_user_logins = [
"web-flow",
]

# Ignore pull requests authored by these users.
# If `{include = "ignored_user_logins"}` is included in this list (the default),
# usernames from the field `ignored_user_logins` are also included.
ignore_prs_by_username = [
{include = "ignored_user_logins"},
]

# If this regex matches a pull requests description, the captured content
# is included instead of the pull request title. E.g. the
# default regex below is matched by
Expand Down Expand Up @@ -228,7 +236,7 @@ jobs:
name: attach to PR
runs-on: ubuntu-latest
steps:
- uses: scientific-python/attach-next-milestone-action@bc07be829f693829263e57d5e8489f4e57d3d420
- uses: scientific-python/attach-next-milestone-action@c9cfab10ad0c67fed91b01103db26b7f16634639
with:
token: ${{ secrets.MILESTONE_LABELER_TOKEN }}
force: true
Expand Down
1 change: 1 addition & 0 deletions src/changelist/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ def main(
pull_requests,
pr_summary_regex=config["pr_summary_regex"],
pr_summary_label_regex=config["pr_summary_label_regex"],
ignore_prs_by_username=config["ignore_prs_by_username"],
)

Formatter = {"md": MdFormatter, "rst": RstFormatter}[format]
Expand Down
23 changes: 23 additions & 0 deletions src/changelist/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,28 @@
DEFAULT_CONFIG_PATH = Path(__file__).parent / "default_config.toml"


def _dereference_ignore_prs_by_username(config: dict) -> dict:
"""Dereference "include" in `ignore_prs_by_username` field.

Example:
>>> config = {
... "ignored_user_logins": ["bot"],
... "ignore_prs_by_username": [{"include": "ignored_user_logins"}, "web-flow"]
... }
>>> _dereference_ignore_prs_by_username(config)
{'ignored_user_logins': ['bot'], 'ignore_prs_by_username': ['web-flow', 'bot']}
"""
if "ignore_prs_by_username" not in config:
return config

include_sentinel = {"include": "ignored_user_logins"}
if include_sentinel in config["ignore_prs_by_username"]:
config["ignore_prs_by_username"].remove(include_sentinel)
config["ignore_prs_by_username"] += config.get("ignored_user_logins", [])

return config


def remote_config(gh: Github, org_repo: str, *, rev: str):
"""Return configuration options in remote pyproject.toml if they exist."""
repo = gh.get_repo(org_repo)
Expand Down Expand Up @@ -50,4 +72,5 @@ def add_config_defaults(
if key not in config:
config[key] = value
logger.debug("using default config value for %s", key)
config = _dereference_ignore_prs_by_username(config)
return config
16 changes: 15 additions & 1 deletion src/changelist/_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import re
from dataclasses import dataclass
from datetime import datetime
from typing import Union
from typing import Optional, Union

from github.NamedUser import NamedUser
from github.PullRequest import PullRequest
Expand All @@ -27,6 +27,7 @@ def from_pull_requests(
*,
pr_summary_regex: str,
pr_summary_label_regex: str,
ignore_prs_by_username: Optional[list[str]] = None,
) -> "set[ChangeNote]":
"""Create a set of notes from pull requests.

Expand All @@ -39,12 +40,25 @@ def from_pull_requests(
requests and notes somewhat. While ideally, a pull request introduces
a change that would be described in a single note, this is often not
the case.

`ignore_prs_by_username` is a list of user logins whose pull requests
should be excluded from the changelog entirely.
"""
if ignore_prs_by_username is None:
ignore_prs_by_username = []

pr_summary_regex = re.compile(pr_summary_regex, flags=re.MULTILINE)
pr_summary_label_regex = re.compile(pr_summary_label_regex)

notes = set()
for pr in pull_requests:
if pr.user and pr.user.login in ignore_prs_by_username:
logger.debug(
"skipping PR %s from ignored user %s",
pr.html_url,
pr.user.login,
)
continue
pr_labels = tuple(label.name for label in pr.labels)

if not pr.body or not (
Expand Down
10 changes: 9 additions & 1 deletion src/changelist/default_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,19 @@ _These lists are automatically generated, and may not be complete or may contain
duplicates._
"""

# Profiles that are excluded from the contributor list.
# Usernames that are excluded from the contributor list. This may also ignore
# pull request of these users (see field `ignore_prs_by_username`).
ignored_user_logins = [
"web-flow",
]

# Ignore pull requests authored by these users.
# If `{include = "ignored_user_logins"}` is included in this list (the default),
# usernames from the field `ignored_user_logins` are also included.
ignore_prs_by_username = [
{include = "ignored_user_logins"},
]

# If this regex matches a pull requests description, the captured content
# is included instead of the pull request title. E.g. the
# default regex below is matched by
Expand Down
44 changes: 43 additions & 1 deletion test/test_objects.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from dataclasses import dataclass
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Union
Expand All @@ -12,6 +12,13 @@
DEFAULT_CONFIG = local_config(DEFAULT_CONFIG_PATH)


@dataclass
class _MockUser:
"""Mocks github.User partially."""

login: str


@dataclass
class _MockLabel:
"""Mocks github.Label.Label partially."""
Expand All @@ -26,6 +33,7 @@ class _MockPullRequest:
title: str
body: Union[str, None]
labels: list[_MockLabel]
user: _MockUser = field(default_factory=lambda: _MockUser(login="friendlyDev"))
number: int = (42,)
html_url: str = "https://github.com/scientific-python/changelist/pull/53"
merged_at: datetime = datetime(2024, 1, 1)
Expand Down Expand Up @@ -73,6 +81,40 @@ def test_from_pull_requests_multiple(self, caplog):
assert caplog.records[0].levelname == "DEBUG"
assert "falling back to PR labels for summary" in caplog.records[0].msg

def test_from_pull_requests_ignore_by_username(self, caplog):
caplog.set_level("DEBUG")
pull_requests = [
_MockPullRequest(
title="PR from user",
body=None,
labels=[_MockLabel("Documentation")],
user=_MockUser("someDev"),
),
_MockPullRequest(
title="PR from bot",
body=None,
labels=[_MockLabel("Documentation")],
user=_MockUser("bot"),
),
]
notes = ChangeNote.from_pull_requests(
pull_requests,
pr_summary_regex=DEFAULT_CONFIG["pr_summary_regex"],
pr_summary_label_regex=DEFAULT_CONFIG["pr_summary_label_regex"],
ignore_prs_by_username=["bot"],
)
assert len(notes) == 1
notes = list(notes)
assert notes[0].content == "PR from user"
assert notes[0].labels == ("Documentation",)

assert len(caplog.records) == 2
assert caplog.records[0].levelname == "DEBUG"
assert "falling back to title" in caplog.records[0].msg

assert caplog.records[1].levelname == "DEBUG"
assert "skipping PR %s from ignored user %s" in caplog.records[1].msg

def test_from_pull_requests_fallback_title(self, caplog):
caplog.set_level("DEBUG")
pull_request = _MockPullRequest(
Expand Down
Loading