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
15 changes: 8 additions & 7 deletions docs/strategies/branch-prefix.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ to a new major version manually, or switch to

The strategy only fires on commits whose subject contains the literal
string `Merge branch` (the default `git merge` subject). Commits
without that text return `none` regardless of prefix. This means:
without one of those marks return `none` regardless of prefix. This
means:

- Standard `git merge feature/foo` → subject `Merge branch 'feature/foo' into main` → bump = minor ✓
- GitHub's "Merge pull request #N from user/feature/foo" → does NOT
contain `Merge branch` → bump = none under defaults.
- GitHub's `Merge pull request #N from user/feature/foo` → bump = minor ✓
- Direct pushes to the default branch → bump = none.

The `merge_mark_text` is configurable (defaults to `Merge branch`);
adapt it for non-default merge-commit conventions.
The `merge_mark_texts` tuple is configurable (defaults to
`("Merge branch", "Merge pull request")`); adapt it for non-default
merge-commit conventions (e.g. squash-merge prefixes).

## Customizing the prefixes

Expand All @@ -43,8 +44,8 @@ The strategy reads its prefixes from the application's settings layer:
`("feature/",)`).
- `patch` — tuple of prefixes that trigger a patch bump (default
`("bugfix/", "hotfix/")`).
- `merge_mark_text` — substring that marks a subject as a merge
commit (default `Merge branch`).
- `merge_mark_texts` — tuple of substrings that mark a subject as a
merge commit (default `("Merge branch", "Merge pull request")`).

These are set via the same pydantic-settings env-var mechanism used
for tokens / endpoints — see the provider docs for the variable
Expand Down
7 changes: 5 additions & 2 deletions semvertag/strategies/branch_prefix.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ class BranchPrefixConfig(pydantic.BaseModel):

minor: tuple[_NonEmptyStr, ...] = pydantic.Field(default=("feature/",), min_length=1)
patch: tuple[_NonEmptyStr, ...] = pydantic.Field(default=("bugfix/", "hotfix/"), min_length=1)
merge_mark_text: _NonEmptyStr = "Merge branch"
merge_mark_texts: tuple[_NonEmptyStr, ...] = pydantic.Field(
default=("Merge branch", "Merge pull request"),
min_length=1,
)


@dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
Expand All @@ -27,7 +30,7 @@ class BranchPrefixStrategy:

def decide(self, commit: Commit) -> Bump:
subject: typing.Final = subject_line(commit.message)
if self.config.merge_mark_text not in subject:
if not any(mark in subject for mark in self.config.merge_mark_texts):
return Bump.NONE
if any(prefix in subject for prefix in self.config.minor):
return Bump.MINOR
Expand Down
19 changes: 17 additions & 2 deletions tests/unit/test_branch_prefix_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def test_honors_custom_minor_prefix_when_config_overrides_default() -> None:
config=BranchPrefixConfig(
minor=("feat/",),
patch=("fix/",),
merge_mark_text="Auto-merge:",
merge_mark_texts=("Auto-merge:",),
),
)
assert custom.decide(_commit("Auto-merge: feat/new-thing")) is Bump.MINOR
Expand All @@ -99,10 +99,25 @@ def test_honors_custom_minor_prefix_when_config_overrides_default() -> None:
assert custom.decide(_commit("Merge branch 'feat/x' into main")) is Bump.NONE


def test_recognizes_github_pr_merge_subject_under_defaults() -> None:
default: typing.Final = BranchPrefixStrategy(config=BranchPrefixConfig())
assert default.decide(_commit("Merge pull request #42 from org/feature/new-thing")) is Bump.MINOR
assert default.decide(_commit("Merge pull request #43 from org/bugfix/bug-123")) is Bump.PATCH
assert default.decide(_commit("Merge pull request #44 from org/hotfix/urgent")) is Bump.PATCH
assert default.decide(_commit("Merge pull request #45 from org/chore/cleanup")) is Bump.NONE


def test_recognizes_gitlab_merge_branch_subject_under_defaults() -> None:
default: typing.Final = BranchPrefixStrategy(config=BranchPrefixConfig())
assert default.decide(_commit("Merge branch 'feature/new-thing' into main")) is Bump.MINOR
assert default.decide(_commit("Merge branch 'bugfix/bug-123' into main")) is Bump.PATCH


_INVALID_CONFIG_CASES: typing.Final = [
{"minor": ()},
{"patch": ()},
{"merge_mark_text": ""},
{"merge_mark_texts": ()},
{"merge_mark_texts": ("",)},
{"minor": ("",)},
{"patch": ("",)},
{"minor": ("feature/", "")},
Expand Down
Loading