Skip to content

Commit 0965dec

Browse files
committed
Added ability to use the current branch in release hint rules
1 parent d9c1d55 commit 0965dec

8 files changed

Lines changed: 137 additions & 34 deletions

File tree

.changelog-config.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,29 @@ valid_author_tokens:
9999
- helped-by
100100
- improved-by
101101
- original-patch-by
102+
103+
# Rules applied to commits to determine the type of release to suggest.
104+
release_hint_rules:
105+
- match_result: dev
106+
no_match_result: no-release
107+
branch: ^((?!master|main).)*$
108+
- match_result: patch
109+
no_match_result: no-release
110+
grouping: Other
111+
branch: master|main
112+
- match_result: patch
113+
no_match_result: no-release
114+
grouping: Fixes
115+
branch: master|main
116+
- match_result: minor
117+
no_match_result: no-release
118+
grouping: Updates
119+
branch: master|main
120+
- match_result: minor
121+
no_match_result:
122+
grouping: New
123+
branch: master|main
124+
- match_result: major
125+
no_match_result:
126+
grouping: Breaking Changes
127+
branch: master|main

docsrc/release-hinting.md

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ patch
1919

2020
## Release rules
2121

22-
There are four parts to a release rule: `match_result`, `no_match_result`, `grouping` and `path`. Only `match_result` is required, but without a `grouping` or `path` value, the rule is ignored. `no_match_result` defaults to `no-release`.
22+
There are five parts to a release rule: `match_result`, `no_match_result`, `grouping`, `path`, and `branch`. Only `match_result` is required, but without a `grouping`, `path`, or `branch` value, the rule is ignored. `no_match_result` defaults to `no-release`.
2323

2424
### Match result
2525

26-
This is the hint returned if both the `grouping` and `path` values match. This is required.
26+
This is the hint returned if the `grouping`, `path` and `branch` evaluations return `True`. This is required.
2727

2828
### No match result
2929

30-
This is the hint returned if either the `grouping` or `path` values do not match. By default, this is `no-release`.
30+
This is the hint returned if any of the `grouping`, `path`, or `branch` evaluations return `False`. By default, this is `no-release`.
3131

3232
### Grouping
3333

@@ -51,13 +51,32 @@ The value of {attr}`.ReleaseHint.grouping` will match the {attr}`.CommitContext.
5151

5252
The {attr}`.CommitContext.files` attribute is a `set` of paths relative to the repository root. The {attr}`.ReleaseHint.path` uses [globbing patterns](https://www.malikbrowne.com/blog/a-beginners-guide-glob-patterns) to match against {attr}`.CommitContext.files`.
5353

54+
### Branch
55+
56+
The {attr}`.ReleaseHint.branch` is a regular expression matched against the current branch. You can limit some release types to your primary branch, and others to non-primary branches.
57+
5458
## Examples
5559

56-
This will provide a "patch" hint if the grouping contains "Other", but only when a modified file is within the `src` directory. Otherwise it will provide a "no-release" hint.
60+
This will provide a `patch` hint if the grouping contains "Other", but only when a modified file is within the `src` directory and the current branch is either `master` or `main`. Otherwise, it will provide a "no-release" hint.
5761

5862
```yaml
59-
- match_result: "patch"
60-
no_match_result: "no-release"
61-
grouping: "Other"
63+
- match_result: patch
64+
no_match_result: no-release
65+
grouping: Other
6266
path: src/*
67+
branch: master|main
68+
```
69+
This will provide a `dev` hint if the current branch is not `master` or `main`. For this to work all other rules must also specify a `branch` attribute. For example, `branch: master|main`.
70+
71+
```yaml
72+
- match_result: dev
73+
no_match_result: no-release
74+
branch: ^((?!master|main).)*$
75+
```
76+
This will prevent any type of release if the current branch is not `master` or `main`. For this to work all other rules must also specify a `branch` attribute. For example, `branch: master|main`.
77+
78+
```yaml
79+
- match_result: no-release
80+
no_match_result: no-release
81+
branch: ^((?!master|main).)*$
6382
```

generate_changelog/cli.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ def main(
8484
else:
8585
repository = Repo(search_parent_directories=True)
8686

87+
current_branch = repository.active_branch
88+
8789
# get starting tag based configuration if not passed in
8890
if not starting_tag and config.starting_tag_pipeline:
8991
start_tag_pipeline = pipeline_factory(config.starting_tag_pipeline, **config.variables)
@@ -96,7 +98,7 @@ def main(
9698

9799
version_contexts = get_context_from_tags(repository, config, starting_tag)
98100

99-
release_hint = suggest_release_type(version_contexts, config)
101+
release_hint = suggest_release_type(current_branch.name, version_contexts, config)
100102

101103
# use the output pipeline to deal with the rendered change log.
102104
notes = templating.render_changelog(version_contexts, config, not starting_tag)

generate_changelog/configuration.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,30 +136,40 @@
136136
DEFAULT_TEMPLATE_DIRS = [".github/changelog_templates/"]
137137

138138
DEFAULT_RELEASE_RULES = [
139+
{
140+
"match_result": "dev",
141+
"no_match_result": "no-release",
142+
"branch": "^((?!master|main).)*$",
143+
},
139144
{
140145
"match_result": "patch",
141146
"no_match_result": "no-release",
142147
"grouping": "Other",
148+
"branch": "master|main",
143149
},
144150
{
145151
"match_result": "patch",
146152
"no_match_result": "no-release",
147153
"grouping": "Fixes",
154+
"branch": "master|main",
148155
},
149156
{
150157
"match_result": "minor",
151158
"no_match_result": "no-release",
152159
"grouping": "Updates",
160+
"branch": "master|main",
153161
},
154162
{
155163
"match_result": "minor",
156164
"no_match_result": None,
157165
"grouping": "New",
166+
"branch": "master|main",
158167
},
159168
{
160169
"match_result": "major",
161170
"no_match_result": None,
162171
"grouping": "Breaking Changes",
172+
"branch": "master|main",
163173
},
164174
]
165175

generate_changelog/release_hint.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def __init__(
2323
no_match_result: Optional[str] = "no-release",
2424
grouping: Union[str, tuple, list, None] = None,
2525
path: Optional[str] = None,
26+
branch: Optional[str] = None,
2627
):
2728
"""
2829
Initialize the callable rule.
@@ -32,12 +33,14 @@ def __init__(
3233
no_match_result: Release type if a commit context doesn't match the rule.
3334
grouping: The partial or exact grouping of the commit context
3435
path: A globbing pattern that matches against files included in the commit
36+
branch: A regular expression pattern to match against the branch
3537
"""
3638
self.match_result = match_result
3739
self.no_match_result = no_match_result
3840
self.grouping = grouping if grouping != "*" else None
3941
self.path = path if path != "*" else None
40-
self.is_valid = any([self.path, self.grouping])
42+
self.branch = branch or None
43+
self.is_valid = any([self.path, self.grouping, self.branch])
4144

4245
def matches_grouping(self, commit: CommitContext) -> bool:
4346
"""
@@ -88,14 +91,27 @@ def matches_path(self, commit: CommitContext) -> bool:
8891
re_pattern = fnmatch.translate(self.path)
8992
return any(re.match(re_pattern, p) for p in commit.files)
9093

91-
def __call__(self, commit: CommitContext) -> Optional[str]:
94+
def matches_branch(self, current_branch: str) -> bool:
95+
"""
96+
Does the current branch match the rule?
97+
98+
Args:
99+
current_branch: The name of the current branch
100+
101+
Returns:
102+
``True`` if the current branch matches or if ``self.branch`` is ``None``
103+
"""
104+
return bool(re.match(self.branch, current_branch)) if self.branch else True
105+
106+
def __call__(self, commit: CommitContext, current_branch: str) -> Optional[str]:
92107
"""Evaluate the commit using this rule."""
93108
if not self.is_valid:
94109
raise InvalidRuleError()
95110

96111
matches_grouping = self.matches_grouping(commit)
97112
matches_path = self.matches_path(commit)
98-
return self.match_result if matches_grouping and matches_path else self.no_match_result
113+
matches_branch = self.matches_branch(current_branch)
114+
return self.match_result if all([matches_grouping, matches_path, matches_branch]) else self.no_match_result
99115

100116

101117
class RuleProcessor:
@@ -110,17 +126,18 @@ def __init__(self, rule_list: List[dict]):
110126
"""
111127
self.rules = [ReleaseRule(**kwargs) for kwargs in rule_list]
112128

113-
def __call__(self, commit: CommitContext) -> Optional[str]:
129+
def __call__(self, commit: CommitContext, current_branch: str) -> Optional[str]:
114130
"""
115131
Return the result of applying all the rules to a commit.
116132
117133
Args:
118134
commit: The commit context to apply rules to
135+
current_branch: The name of the current branch
119136
120137
Returns:
121138
The release hint
122139
"""
123-
suggestions = {rule(commit) for rule in self.rules}
140+
suggestions = {rule(commit, current_branch) for rule in self.rules}
124141
unknown_suggestions = suggestions - set(RELEASE_TYPE_ORDER)
125142
if unknown_suggestions:
126143
return unknown_suggestions.pop() # Return a random value from the unknowns
@@ -129,11 +146,12 @@ def __call__(self, commit: CommitContext) -> Optional[str]:
129146
return sorted_suggestions[-1]
130147

131148

132-
def suggest_release_type(version_contexts: List[VersionContext], config: Configuration) -> str:
149+
def suggest_release_type(current_branch: str, version_contexts: List[VersionContext], config: Configuration) -> str:
133150
"""
134151
Suggest the type of release based on the unreleased commits.
135152
136153
Args:
154+
current_branch: The name of the current branch
137155
version_contexts: The processed commits to process
138156
config: The current configuration
139157
@@ -148,7 +166,7 @@ def suggest_release_type(version_contexts: List[VersionContext], config: Configu
148166

149167
suggestions = set()
150168
for commit_group in version_contexts[0].grouped_commits:
151-
suggestions |= {rule_processor(commit) for commit in commit_group.commits}
169+
suggestions |= {rule_processor(commit, current_branch) for commit in commit_group.commits}
152170

153171
if not suggestions:
154172
return "no-release"

requirements/dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
-r test.txt
22
-r docs.txt
33
bump2version>=1.0.1
4+
generate-changelog
45
git-fame>=1.12.2

test/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def commit_factory(
106106
@pytest.fixture
107107
def bare_git_repo(tmp_path):
108108
"""Create a temporary bare git repository."""
109-
return Repo.init(tmp_path / "bare-repo", bare=True)
109+
return Repo.init(tmp_path / "bare-repo", bare=True, initial_branch="master")
110110

111111

112112
@pytest.fixture

0 commit comments

Comments
 (0)