Description
Bug Report
Description
Different / breaking behavior between 9.16.1 and 9.17.0 in algorithm.py
Step 5. Parse commits to determine the bump level that should be applied.
Expected behavior
I would have expected the same behavior between 9.16.1 and 9.17.0 as minor version changes should not exhibit breaking behavior. This seems to be a regression.
Actual behavior
Specifically, in 9.16.1, parsed_levels will contain the results of the parsed levels in the commits_since_last_release, but 9.17.0 will not. It is not entirely clear to me where this is failing due to the complexity of the mapped/filtered set.
Environment
- Operating System (w/ version): MacOS
- Python version: 3.12
- Pip version: 25.0 (using Poetry)
- Semantic-release version: 9.17.0 has the regression
- Build tool (w/ version): poetry 1.8.3
poetry show python-semantic-release
name : python-semantic-release
version : 9.16.1
description : Automatic Semantic Versioning for Python projects
dependencies
- click >=8.0,<9.0
- click-option-group >=0.5,<1.0
- dotty-dict >=1.3,<2.0
- gitpython >=3.0,<4.0
- importlib-resources >=6.0,<7.0
- jinja2 >=3.1,<4.0
- pydantic >=2.0,<3.0
- python-gitlab >=4.0,<5.0
- requests >=2.25,<3.0
- rich >=13.0,<14.0
- shellingham >=1.5,<2.0
- tomlkit >=0.11,<1.0
git log --oneline --decorate --graph --all -n 50
"""
* 5947a27 (origin/main, origin/HEAD) Merged PR 34224: some description
|\
| * 484adac (HEAD -> fix/950777-something) fix(XYZ-950777): some description
|/
* 6a320ac (main) Merged PR 34214: feat(XYZ-950230): some description
|\
| * fdc2c4b (feat/950230-ruff-version-for-check) feat(XYZ-950230): some description
|/
* b3d8aab (tag: v3.17.0) chore(release): release v3.17.0 [skip ci]
* 3ba9e71 Merged PR 33609: feat(XYZ-938710): some description
|\
| * eed8b07 feat(XYZ-938710): some description
|/
* 1886245 (tag: v3.16.1, fix/947167-something) chore(release): release v3.16.1 [skip ci]
* a4ca462 Merged PR 33438: fix(XYZ-945996): some description
|\
| * d3096b5 fix(XYZ-945996): some description
| * 89e7a32 fix(XYZ-945996): some description
| * 241cee5 fix(XYZ-945996): some description
|/
* eb53d60 Merged PR 32668: docs(XYZ-936992): some description
|\
| * ed1823c docs(XYZ-936992): some description
|/
* 1f4aeac (tag: v3.16.0) chore(release): release v3.16.0 [skip ci]
* e944328 Merged PR 32255: feat(XYZ-877699): some description
* b8f74f1 (tag: v3.15.0) chore(release): release v3.15.0 [skip ci]
* b16df6e Merged PR 32425: some description
|\
| * dcbcb90 feat(XYZ-929489): some description
| | * 22ecbd7 (origin/feat/877699-something) feat(XYZ-877699): some description
| | * 19a171e feat(XYZ-877699): some description
| | * cc1a1ed feat(XYZ-877699): some description
| | * 2d2b874 feat(XYZ-877699): some description
| | * b734132 feat(XYZ-877699): some description
| | * fbcbab3 feat(XYZ-877699): some description
| | * 6c1b9f1 feat(XYZ-877699): some description
| |/
|/|
* | 44e166e (tag: v3.14.1) chore(release): release v3.14.1 [skip ci]
* | d3d9c78 Merged PR 32406: fix(XYZ-929227): some description
|\ \
| * | fb90a03 fix(XYZ-929227): some description
|/ /
* | 60427a2 (tag: v3.14.0) chore(release): release v3.14.0 [skip ci]
* | c9e0ac6 Merged PR 32349: feat: some description
|\ \
| |/
|/|
| * 921073b feat: update something
|/
* eab864b (tag: v3.13.1) chore(release): release v3.13.1 [skip ci]
* 4ab8ff1 Merged PR 32022: increasing minimum aws-cdk-lib version
|\
| * 4552a43 fix(933941): some description
* | 5fc2b5c (tag: v3.13.0) chore(release): release v3.13.0 [skip ci]
* | 651895b Merged PR 31947: feat(XYZ-933508): some description
|\ \
| * | 6a67df4 feat(XYZ-933508): some description
|/ /
| | * 028a9dd (origin/feat/933508-merge-coverage) Initial commit
| | * 99a7bcb feat(XYZ-933508): some description
| |/
|/|
| | * 46f6fb3 (origin/feat/877699-something, feat/877699-something) feat(XYZ-877699): some description
| |/
|/|
* | ad8093f (tag: v3.12.0) chore(release): release v3.12.0 [skip ci]
* | 8dd80e3 Merged PR 31794: feat(XYZ-932586): some description
|\ \
| * | 2ad1331 (feature/932586-something) feat(XYZ-932586): some description
* | | e456008 (tag: v3.11.0) chore(release): release v3.11.0 [skip ci]
* | | f04be2d Merged PR 31765: XYZ-930364: some description
|\ \ \
| * | | 17a541e (origin/feature/XYZ-930364-something) feat(XYZ-930364): some description
|/ / /
| | | * f103abc (refs/stash) WIP on feature/XYZ-930364-something: 2bc9a7e some description
| | | |\
| | | | * a4d443c index on feature/XYZ-930364-something: 2bc9a7e some description
| | | |/
| | | * 2bc9a7e (feature/XYZ-930364-something) some description
Configuration
Semantic Release Configuration
I use a custom commit parser to support working with Azure Devops.import re
from typing import Any, Optional
import git
import toml
from semantic_release import (
CommitParser,
LevelBump,
ParsedCommit,
ParseError,
ParseResult,
ParserOptions,
)
class MyCommitParserOptions(ParserOptions):
def __init__(self, **_: Any):
# Load whole configuration
super().__init__(**_)
pyproject = toml.load("pyproject.toml")
# Extract commit parsers options
commit_parser_options = pyproject["tool"]["semantic_release"][
"commit_parser_options"
]
# Load options as instance variables
self.allowed_tags = commit_parser_options["allowed_tags"]
self.minor_tags = commit_parser_options["minor_tags"]
self.patch_tags = commit_parser_options["patch_tags"]
self.default_bump_level = LevelBump(commit_parser_options["default_bump_level"])
class MyCommitParser(CommitParser[ParseResult, MyCommitParserOptions]):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.options = ADOCommitParserOptions()
def parse(self, commit: git.objects.commit.Commit) -> Optional[ParseResult]:
sentences = commit.message.strip().split("\n\n")
unique_sentences = list(dict.fromkeys(sentences))
message = "\n\n".join(unique_sentences)
allowed_tags_regex_part = "|".join(map(re.escape, self.options.allowed_tags))
match = re.match(
rf"^.*({allowed_tags_regex_part})(?:\(([^)]+)\))?: (.+)(?:\n|$)([\s\S]*)",
message,
)
if match:
type_ = match.group(1)
scope = match.group(2) or ""
description = match.group(3)
rest_of_message = match.group(4)
descriptions = [description]
breaking_descriptions = []
for line in rest_of_message.split("\n"):
line = line.strip()
if line.startswith("BREAKING CHANGE: "):
breaking_descriptions.append(line.replace("BREAKING CHANGE: ", ""))
elif line != "":
descriptions.append(line)
bump = self.options.default_bump_level
if breaking_descriptions:
bump = LevelBump.MAJOR
elif type_ in self.options.minor_tags:
bump = LevelBump.MINOR
elif type_ in self.options.patch_tags:
bump = LevelBump.PATCH
return ParsedCommit(
bump=bump,
type=type_,
scope=scope,
descriptions=descriptions,
breaking_descriptions=breaking_descriptions,
commit=commit,
)
else:
return ParseError(commit=commit, error="Could not parse commit message")
[tool.semantic_release]
assets = []
build_command_env = []
commit_message = "chore(release): release v{version} [skip ci]"
commit_parser = "commit_parser:MyCommitParser"
logging_use_named_masks = false
major_on_zero = true
allow_zero_version = true
no_git_verify = false
tag_format = "v{version}"
version_toml = [
"pyproject.toml:tool.poetry.version",
]
version_variables = [
"calabrio_cdk/_version.py:__version__"
]
[tool.semantic_release.branches.main]
match = "(main|master)"
prerelease_token = "rc"
prerelease = false
[tool.semantic_release.changelog]
template_dir = "templates"
changelog_file = "CHANGELOG.md"
exclude_commit_patterns = [
'''chore(?:\([^)]*?\))?: .+''',
'''ci(?:\([^)]*?\))?: .+''',
'''refactor(?:\([^)]*?\))?: .+''',
'''style(?:\([^)]*?\))?: .+''',
'''test(?:\([^)]*?\))?: .+''',
'''build\((?!deps\): .+)''',
'''Merged? .*''',
]
[tool.semantic_release.changelog.environment]
block_start_string = "{%"
block_end_string = "%}"
variable_start_string = "{{"
variable_end_string = "}}"
comment_start_string = "{#"
comment_end_string = "#}"
trim_blocks = false
lstrip_blocks = false
newline_sequence = "\n"
keep_trailing_newline = false
extensions = []
autoescape = true
[tool.semantic_release.commit_author]
env = "GIT_COMMIT_AUTHOR"
default = "semantic-release <semantic-release>"
[tool.semantic_release.commit_parser_options]
allowed_tags = ["build", "chore", "ci", "docs", "feat", "fix", "perf", "style", "refactor", "test"]
minor_tags = ["feat"]
patch_tags = ["fix", "perf"]
default_bump_level = 0
[tool.semantic_release.remote]
name = "origin"
type = "github"
domain = "dev.azure.com"
api_domain = "dev.azure.com"
ignore_token_for_push = true
insecure = false
url = { env = "AZURE_REPOSITORY_URL" }
[tool.semantic_release.remote.token]
env = "AZURE_TOKEN"
[tool.semantic_release.publish]
dist_glob_patterns = ["dist/*"]
upload_to_vcs_release = false
Build System Configuration
GitHub Actions Job Definition
Execution Log
poetry run semantic-release -vv version --no-vcs-release
9.17.1
DEBUG [cmd.execute] Popen(['git', 'rev-list', 'v3.17.0', '--'], cwd=/Users/me/project, stdin=None, shell=False, universal_newlines=False) cmd.py:1253
INFO [algorithm._traverse_graph_for_commits] Found 3 commits since the last release! algorithm.py:112
DEBUG [algorithm.next_version] parsed the following distinct levels from the commits since the last release: set()
9.16.1
DEBUG [cmd.execute] Popen(['git', 'rev-list', 'v3.17.0', '--'], cwd=/Users/me/project, stdin=None, shell=False, universal_newlines=False) cmd.py:1253
DEBUG [algorithm.dfs] queuing parent commit 6a320ac algorithm.py:92
DEBUG [algorithm.dfs] queuing parent commit b3d8aab algorithm.py:92
DEBUG [algorithm.dfs] queuing parent commit fdc2c4b algorithm.py:92
DEBUG [algorithm.dfs] queuing parent commit b3d8aab algorithm.py:92
INFO [algorithm._traverse_graph_for_commits] Found 3 commits since the last release! algorithm.py:112
DEBUG [algorithm.next_version] parsed the following distinct levels from the commits since the last release: {<LevelBump.PATCH: 2>, <LevelBump.MINOR: 3>}
Additional context
Debugging suggests that perhaps the result of the filter on isinstance for ParsedCommit might be failing, since at this point the parsed_result is already a LevelBump. Though this is not entirely clear due to the complexity of step 5.
The commit parser returns the correct ParsedCommit(s) in both versions of PSR, however, the filtered list in parsed_levels differs between versions.