python-semantic-release 9.8.4__tar.gz → 9.8.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. {python_semantic_release-9.8.4/python_semantic_release.egg-info → python_semantic_release-9.8.5}/PKG-INFO +1 -1
  2. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/pyproject.toml +1 -1
  3. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5/python_semantic_release.egg-info}/PKG-INFO +1 -1
  4. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/__init__.py +1 -1
  5. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/changelog/release_history.py +44 -39
  6. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/cli_context.py +29 -9
  7. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/commands/version.py +18 -9
  8. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/command_line/test_main.py +2 -2
  9. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/command_line/test_version.py +130 -2
  10. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/AUTHORS.rst +0 -0
  11. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/LICENSE +0 -0
  12. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/MANIFEST.in +0 -0
  13. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/README.rst +0 -0
  14. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/Makefile +0 -0
  15. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/algorithm.rst +0 -0
  16. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/automatic-releases/cronjobs.rst +0 -0
  17. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/automatic-releases/github-actions.rst +0 -0
  18. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/automatic-releases/index.rst +0 -0
  19. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/automatic-releases/travis.rst +0 -0
  20. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/changelog_templates.rst +0 -0
  21. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/commands.rst +0 -0
  22. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/commit-parsing.rst +0 -0
  23. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/conf.py +0 -0
  24. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/configuration.rst +0 -0
  25. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/contributing.rst +0 -0
  26. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/contributors.rst +0 -0
  27. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/github-action.rst +0 -0
  28. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/index.rst +0 -0
  29. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/make.bat +0 -0
  30. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/migrating_from_v7.rst +0 -0
  31. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/multibranch_releases.rst +0 -0
  32. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/strict_mode.rst +0 -0
  33. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/docs/troubleshooting.rst +0 -0
  34. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/python_semantic_release.egg-info/SOURCES.txt +0 -0
  35. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/python_semantic_release.egg-info/dependency_links.txt +0 -0
  36. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/python_semantic_release.egg-info/entry_points.txt +0 -0
  37. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/python_semantic_release.egg-info/requires.txt +0 -0
  38. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/python_semantic_release.egg-info/top_level.txt +0 -0
  39. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/__main__.py +0 -0
  40. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/changelog/__init__.py +0 -0
  41. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/changelog/context.py +0 -0
  42. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/changelog/template.py +0 -0
  43. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/__init__.py +0 -0
  44. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/changelog_writer.py +0 -0
  45. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/commands/__init__.py +0 -0
  46. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/commands/changelog.py +0 -0
  47. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/commands/generate_config.py +0 -0
  48. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/commands/main.py +0 -0
  49. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/commands/publish.py +0 -0
  50. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/config.py +0 -0
  51. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/const.py +0 -0
  52. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/github_actions_output.py +0 -0
  53. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/masking_filter.py +0 -0
  54. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/cli/util.py +0 -0
  55. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/commit_parser/__init__.py +0 -0
  56. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/commit_parser/_base.py +0 -0
  57. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/commit_parser/angular.py +0 -0
  58. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/commit_parser/emoji.py +0 -0
  59. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/commit_parser/scipy.py +0 -0
  60. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/commit_parser/tag.py +0 -0
  61. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/commit_parser/token.py +0 -0
  62. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/commit_parser/util.py +0 -0
  63. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/const.py +0 -0
  64. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/data/templates/CHANGELOG.md.j2 +0 -0
  65. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/data/templates/release_notes.md.j2 +0 -0
  66. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/enums.py +0 -0
  67. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/errors.py +0 -0
  68. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/gitproject.py +0 -0
  69. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/helpers.py +0 -0
  70. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/hvcs/__init__.py +0 -0
  71. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/hvcs/_base.py +0 -0
  72. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/hvcs/bitbucket.py +0 -0
  73. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/hvcs/gitea.py +0 -0
  74. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/hvcs/github.py +0 -0
  75. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/hvcs/gitlab.py +0 -0
  76. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/hvcs/remote_hvcs_base.py +0 -0
  77. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/hvcs/token_auth.py +0 -0
  78. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/hvcs/util.py +0 -0
  79. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/version/__init__.py +0 -0
  80. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/version/algorithm.py +0 -0
  81. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/version/declaration.py +0 -0
  82. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/version/translator.py +0 -0
  83. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/semantic_release/version/version.py +0 -0
  84. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/setup.cfg +0 -0
  85. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/__init__.py +0 -0
  86. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/command_line/__init__.py +0 -0
  87. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/command_line/conftest.py +0 -0
  88. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/command_line/test_changelog.py +0 -0
  89. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/command_line/test_generate_config.py +0 -0
  90. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/command_line/test_help.py +0 -0
  91. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/command_line/test_publish.py +0 -0
  92. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/conftest.py +0 -0
  93. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/const.py +0 -0
  94. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/__init__.py +0 -0
  95. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/commit_parsers.py +0 -0
  96. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/example_project.py +0 -0
  97. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/git_repo.py +0 -0
  98. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/__init__.py +0 -0
  99. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/git_flow/__init__.py +0 -0
  100. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +0 -0
  101. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +0 -0
  102. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/github_flow/__init__.py +0 -0
  103. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/github_flow/repo_w_release_channels.py +0 -0
  104. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/trunk_based_dev/__init__.py +0 -0
  105. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +0 -0
  106. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +0 -0
  107. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +0 -0
  108. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/fixtures/scipy.py +0 -0
  109. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/scenario/__init__.py +0 -0
  110. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/scenario/test_next_version.py +0 -0
  111. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/scenario/test_release_history.py +0 -0
  112. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/scenario/test_template_render.py +0 -0
  113. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/__init__.py +0 -0
  114. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/__init__.py +0 -0
  115. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/changelog/__init__.py +0 -0
  116. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/changelog/test_changelog_context.py +0 -0
  117. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/changelog/test_default_changelog.py +0 -0
  118. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/changelog/test_release_notes.py +0 -0
  119. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/changelog/test_template.py +0 -0
  120. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/cli/__init__.py +0 -0
  121. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/cli/test_config.py +0 -0
  122. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/cli/test_github_actions_output.py +0 -0
  123. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/cli/test_masking_filter.py +0 -0
  124. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/cli/test_util.py +0 -0
  125. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/cli/test_version.py +0 -0
  126. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/commit_parser/__init__.py +0 -0
  127. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/commit_parser/test_angular.py +0 -0
  128. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/commit_parser/test_emoji.py +0 -0
  129. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/commit_parser/test_parsed_commit.py +0 -0
  130. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/commit_parser/test_scipy.py +0 -0
  131. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/commit_parser/test_tag.py +0 -0
  132. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/commit_parser/test_util.py +0 -0
  133. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/hvcs/__init__.py +0 -0
  134. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/hvcs/test__base.py +0 -0
  135. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/hvcs/test_bitbucket.py +0 -0
  136. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/hvcs/test_gitea.py +0 -0
  137. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/hvcs/test_github.py +0 -0
  138. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/hvcs/test_gitlab.py +0 -0
  139. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/hvcs/test_token_auth.py +0 -0
  140. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/hvcs/test_util.py +0 -0
  141. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/test_helpers.py +0 -0
  142. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/version/__init__.py +0 -0
  143. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/version/test_algorithm.py +0 -0
  144. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/version/test_declaration.py +0 -0
  145. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/version/test_translator.py +0 -0
  146. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/unit/semantic_release/version/test_version.py +0 -0
  147. {python_semantic_release-9.8.4 → python_semantic_release-9.8.5}/tests/util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-semantic-release
3
- Version: 9.8.4
3
+ Version: 9.8.5
4
4
  Summary: Automatic Semantic Versioning for Python projects
5
5
  Author-email: Rolf Erik Lekang <me@rolflekang.com>
6
6
  License: MIT
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "python-semantic-release"
9
- version = "9.8.4"
9
+ version = "9.8.5"
10
10
  description = "Automatic Semantic Versioning for Python projects"
11
11
  requires-python = ">=3.8"
12
12
  license = { text = "MIT" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-semantic-release
3
- Version: 9.8.4
3
+ Version: 9.8.5
4
4
  Summary: Automatic Semantic Versioning for Python projects
5
5
  Author-email: Rolf Erik Lekang <me@rolflekang.com>
6
6
  License: MIT
@@ -24,7 +24,7 @@ from semantic_release.version import (
24
24
  tags_and_versions,
25
25
  )
26
26
 
27
- __version__ = "9.8.4"
27
+ __version__ = "9.8.5"
28
28
 
29
29
  __all__ = [
30
30
  "CommitParser",
@@ -41,6 +41,13 @@ class ReleaseHistory:
41
41
  unreleased: dict[str, list[ParseResult]] = defaultdict(list)
42
42
  released: dict[Version, Release] = {}
43
43
 
44
+ # Performance optimization: create a mapping of tag sha to version
45
+ # so we can quickly look up the version for a given commit based on sha
46
+ tag_sha_2_version_lookup = {
47
+ tag.commit.hexsha: (tag, version)
48
+ for tag, version in all_git_tags_and_versions
49
+ }
50
+
44
51
  # Strategy:
45
52
  # Loop through commits in history, parsing as we go.
46
53
  # Add these commits to `unreleased` as a key-value mapping
@@ -54,7 +61,7 @@ class ReleaseHistory:
54
61
  is_commit_released = False
55
62
  the_version: Version | None = None
56
63
 
57
- for commit in repo.iter_commits():
64
+ for commit in repo.iter_commits("HEAD", topo_order=True):
58
65
  # mypy will be happy if we make this an explicit string
59
66
  commit_message = str(commit.message)
60
67
 
@@ -64,46 +71,44 @@ class ReleaseHistory:
64
71
  )
65
72
  log.debug("commit has type %s", commit_type)
66
73
 
67
- for tag, version in all_git_tags_and_versions:
68
- if tag.commit == commit:
69
- # we have found the latest commit introduced by this tag
70
- # so we create a new Release entry
71
- log.debug("found commit %s for tag %s", commit.hexsha, tag.name)
72
- is_commit_released = True
73
- the_version = version
74
-
75
- # tag.object is a Commit if the tag is lightweight, otherwise
76
- # it is a TagObject with additional metadata about the tag
77
- if isinstance(tag.object, TagObject):
78
- tagger = tag.object.tagger
79
- committer = tag.object.tagger.committer()
80
- _tz = timezone(
81
- timedelta(seconds=-1 * tag.object.tagger_tz_offset)
82
- )
83
- tagged_date = datetime.fromtimestamp(
84
- tag.object.tagged_date, tz=_tz
85
- )
86
- else:
87
- # For some reason, sometimes tag.object is a Commit
88
- tagger = tag.object.author
89
- committer = tag.object.author
90
- _tz = timezone(
91
- timedelta(seconds=-1 * tag.object.author_tz_offset)
92
- )
93
- tagged_date = datetime.fromtimestamp(
94
- tag.object.committed_date, tz=_tz
95
- )
96
-
97
- release = Release(
98
- tagger=tagger,
99
- committer=committer,
100
- tagged_date=tagged_date,
101
- elements=defaultdict(list),
102
- version=the_version,
74
+ log.debug("checking if commit %s matches any tags", commit.hexsha)
75
+ t_v = tag_sha_2_version_lookup.get(commit.hexsha, None)
76
+
77
+ if t_v is None:
78
+ log.debug("no tags correspond to commit %s", commit.hexsha)
79
+ else:
80
+ # Unpack the tuple (overriding the current version)
81
+ tag, the_version = t_v
82
+ # we have found the latest commit introduced by this tag
83
+ # so we create a new Release entry
84
+ log.debug("found commit %s for tag %s", commit.hexsha, tag.name)
85
+ is_commit_released = True
86
+
87
+ # tag.object is a Commit if the tag is lightweight, otherwise
88
+ # it is a TagObject with additional metadata about the tag
89
+ if isinstance(tag.object, TagObject):
90
+ tagger = tag.object.tagger
91
+ committer = tag.object.tagger.committer()
92
+ _tz = timezone(timedelta(seconds=-1 * tag.object.tagger_tz_offset))
93
+ tagged_date = datetime.fromtimestamp(tag.object.tagged_date, tz=_tz)
94
+ else:
95
+ # For some reason, sometimes tag.object is a Commit
96
+ tagger = tag.object.author
97
+ committer = tag.object.author
98
+ _tz = timezone(timedelta(seconds=-1 * tag.object.author_tz_offset))
99
+ tagged_date = datetime.fromtimestamp(
100
+ tag.object.committed_date, tz=_tz
103
101
  )
104
102
 
105
- released.setdefault(the_version, release)
106
- break
103
+ release = Release(
104
+ tagger=tagger,
105
+ committer=committer,
106
+ tagged_date=tagged_date,
107
+ elements=defaultdict(list),
108
+ version=the_version,
109
+ )
110
+
111
+ released.setdefault(the_version, release)
107
112
 
108
113
  if any(pat.match(commit_message) for pat in exclude_commit_patterns):
109
114
  log.debug(
@@ -34,10 +34,17 @@ class CliContextObj:
34
34
  logger: logging.Logger,
35
35
  global_opts: GlobalCommandLineOptions,
36
36
  ) -> None:
37
- self._runtime_ctx: RuntimeContext | None = None
38
37
  self.ctx = ctx
39
38
  self.logger = logger
40
39
  self.global_opts = global_opts
40
+ self._raw_config: RawConfig | None = None
41
+ self._runtime_ctx: RuntimeContext | None = None
42
+
43
+ @property
44
+ def raw_config(self) -> RawConfig:
45
+ if self._raw_config is None:
46
+ self._raw_config = self._init_raw_config()
47
+ return self._raw_config
41
48
 
42
49
  @property
43
50
  def runtime_ctx(self) -> RuntimeContext:
@@ -49,7 +56,7 @@ class CliContextObj:
49
56
  self._runtime_ctx = self._init_runtime_ctx()
50
57
  return self._runtime_ctx
51
58
 
52
- def _init_runtime_ctx(self) -> RuntimeContext:
59
+ def _init_raw_config(self) -> RawConfig:
53
60
  config_path = Path(self.global_opts.config_file)
54
61
  conf_file_exists = config_path.exists()
55
62
  was_conf_file_user_provided = bool(
@@ -60,6 +67,7 @@ class CliContextObj:
60
67
  )
61
68
  )
62
69
 
70
+ # TODO: Evaluate Exeception catches
63
71
  try:
64
72
  if was_conf_file_user_provided and not conf_file_exists:
65
73
  raise FileNotFoundError( # noqa: TRY301
@@ -74,24 +82,36 @@ class CliContextObj:
74
82
  "configuration empty, falling back to default configuration"
75
83
  )
76
84
 
77
- raw_config = RawConfig.model_validate(config_obj)
85
+ return RawConfig.model_validate(config_obj)
86
+ except FileNotFoundError as exc:
87
+ click.echo(str(exc), err=True)
88
+ self.ctx.exit(2)
89
+ except (
90
+ ValidationError,
91
+ InvalidConfiguration,
92
+ InvalidGitRepositoryError,
93
+ ) as exc:
94
+ click.echo(str(exc), err=True)
95
+ self.ctx.exit(1)
96
+
97
+ def _init_runtime_ctx(self) -> RuntimeContext:
98
+ # TODO: Evaluate Exception catches
99
+ try:
78
100
  runtime = RuntimeContext.from_raw_config(
79
- raw_config,
101
+ self.raw_config,
80
102
  global_cli_options=self.global_opts,
81
103
  )
82
- except (DetachedHeadGitError, NotAReleaseBranch) as exc:
104
+ except NotAReleaseBranch as exc:
83
105
  rprint(f"[bold {'red' if self.global_opts.strict else 'orange1'}]{exc!s}")
84
106
  # If not strict, exit 0 so other processes can continue. For example, in
85
107
  # multibranch CI it might be desirable to run a non-release branch's pipeline
86
108
  # without specifying conditional execution of PSR based on branch name
87
109
  self.ctx.exit(2 if self.global_opts.strict else 0)
88
- except FileNotFoundError as exc:
89
- click.echo(str(exc), err=True)
90
- self.ctx.exit(2)
91
110
  except (
92
- ValidationError,
111
+ DetachedHeadGitError,
93
112
  InvalidConfiguration,
94
113
  InvalidGitRepositoryError,
114
+ ValidationError,
95
115
  ) as exc:
96
116
  click.echo(str(exc), err=True)
97
117
  self.ctx.exit(1)
@@ -413,26 +413,28 @@ def version( # noqa: C901
413
413
  * Create a release (if supported) in the remote VCS for this tag
414
414
  """
415
415
  ctx = click.get_current_context()
416
- runtime = cli_ctx.runtime_ctx
417
- translator = runtime.version_translator
416
+
417
+ # Enable any cli overrides of configuration before asking for the runtime context
418
+ config = cli_ctx.raw_config
418
419
 
419
420
  # We can short circuit updating the release if we are only printing the last released version
420
421
  if print_last_released or print_last_released_tag:
421
422
  # TODO: get tag format a better way
422
- if not (last_release := last_released(runtime.repo_dir, translator.tag_format)):
423
+ if not (
424
+ last_release := last_released(config.repo_dir, tag_format=config.tag_format)
425
+ ):
423
426
  log.warning("No release tags found.")
424
427
  return
425
428
 
426
429
  click.echo(last_release[0] if print_last_released_tag else last_release[1])
427
430
  return
428
431
 
432
+ # TODO: figure out --print of next version with & without branch validation
433
+ # do you always need a prerelease token if its not --as-prerelease?
434
+ runtime = cli_ctx.runtime_ctx
435
+ translator = runtime.version_translator
436
+
429
437
  parser = runtime.commit_parser
430
- forced_level_bump = None if not force_level else LevelBump.from_string(force_level)
431
- prerelease = is_forced_prerelease(
432
- as_prerelease=as_prerelease,
433
- forced_level_bump=forced_level_bump,
434
- prerelease=runtime.prerelease,
435
- )
436
438
  hvcs_client = runtime.hvcs_client
437
439
  assets = runtime.assets
438
440
  commit_author = runtime.commit_author
@@ -442,6 +444,13 @@ def version( # noqa: C901
442
444
  opts = runtime.global_cli_options
443
445
  gha_output = VersionGitHubActionsOutput(released=False)
444
446
 
447
+ forced_level_bump = None if not force_level else LevelBump.from_string(force_level)
448
+ prerelease = is_forced_prerelease(
449
+ as_prerelease=as_prerelease,
450
+ forced_level_bump=forced_level_bump,
451
+ prerelease=runtime.prerelease,
452
+ )
453
+
445
454
  if prerelease_token:
446
455
  log.info("Forcing use of %s as the prerelease token", prerelease_token)
447
456
  translator.prerelease_token = prerelease_token
@@ -85,8 +85,8 @@ def test_not_a_release_branch_detached_head_exit_code(
85
85
  cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--no-commit"]
86
86
  result = cli_runner.invoke(main, cli_cmd[1:])
87
87
 
88
- # as non-strict, this will return success exit code
89
- assert_successful_exit_code(result, cli_cmd)
88
+ # detached head states should throw an error as release branches cannot be determined
89
+ assert_exit_code(1, result, cli_cmd)
90
90
  assert expected_err_msg in result.stderr
91
91
 
92
92
 
@@ -16,6 +16,7 @@ import tomlkit
16
16
  from pytest_lazyfixture import lazy_fixture
17
17
 
18
18
  from semantic_release.cli.commands.main import main
19
+ from semantic_release.hvcs.github import Github
19
20
 
20
21
  from tests.const import (
21
22
  EXAMPLE_PROJECT_NAME,
@@ -53,6 +54,7 @@ if TYPE_CHECKING:
53
54
  UpdatePyprojectTomlFn,
54
55
  UseReleaseNotesTemplateFn,
55
56
  )
57
+ from tests.fixtures.git_repo import SimulateChangeCommitsNReturnChangelogEntryFn
56
58
 
57
59
 
58
60
  @pytest.mark.parametrize(
@@ -1001,6 +1003,33 @@ def test_version_exit_code_when_not_strict(
1001
1003
  assert_successful_exit_code(result, cli_cmd)
1002
1004
 
1003
1005
 
1006
+ @pytest.mark.parametrize(
1007
+ "is_strict, exit_code", [(True, 2), (False, 0)], ids=["strict", "non-strict"]
1008
+ )
1009
+ def test_version_on_nonrelease_branch(
1010
+ repo_with_single_branch_angular_commits: Repo,
1011
+ cli_runner: CliRunner,
1012
+ is_strict: bool,
1013
+ exit_code: int,
1014
+ ):
1015
+ branch = repo_with_single_branch_angular_commits.create_head("next")
1016
+ branch.checkout()
1017
+ expected_error_msg = (
1018
+ f"branch '{branch.name}' isn't in any release groups; no release will be made\n"
1019
+ )
1020
+
1021
+ # Act
1022
+ cli_cmd = list(
1023
+ filter(None, [MAIN_PROG_NAME, "--strict" if is_strict else "", VERSION_SUBCMD])
1024
+ )
1025
+ result = cli_runner.invoke(main, cli_cmd[1:])
1026
+
1027
+ # Evaluate (expected -> actual)
1028
+ assert_exit_code(exit_code, result, cli_cmd)
1029
+ assert not result.stdout
1030
+ assert expected_error_msg == result.stderr
1031
+
1032
+
1004
1033
  def test_custom_release_notes_template(
1005
1034
  mocked_git_push: MagicMock,
1006
1035
  repo_with_no_tags_angular_commits: Repo,
@@ -1017,7 +1046,7 @@ def test_custom_release_notes_template(
1017
1046
  runtime_context_with_no_tags = retrieve_runtime_context(
1018
1047
  repo_with_no_tags_angular_commits
1019
1048
  )
1020
- cli_cmd = ["semantic-release", VERSION_SUBCMD, "--skip-build", "--vcs-release"]
1049
+ cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--skip-build", "--vcs-release"]
1021
1050
 
1022
1051
  # Act
1023
1052
  result = cli_runner.invoke(main, cli_cmd[1:])
@@ -1056,7 +1085,7 @@ def test_version_tag_only_push(
1056
1085
 
1057
1086
  # Act
1058
1087
  cli_cmd = [
1059
- "semantic-release",
1088
+ MAIN_PROG_NAME,
1060
1089
  VERSION_SUBCMD,
1061
1090
  "--tag",
1062
1091
  "--no-commit",
@@ -1219,3 +1248,102 @@ def test_version_print_last_released_prints_nothing_if_no_tags(
1219
1248
  assert_successful_exit_code(result, cli_cmd)
1220
1249
  assert result.stdout == ""
1221
1250
  assert "No release tags found." in caplog.text
1251
+
1252
+
1253
+ def test_version_print_last_released_on_detached_head(
1254
+ cli_runner: CliRunner,
1255
+ repo_with_single_branch_tag_commits: Repo,
1256
+ ):
1257
+ last_version = "0.1.1"
1258
+ repo_with_single_branch_tag_commits.git.checkout("HEAD", detach=True)
1259
+
1260
+ cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released"]
1261
+
1262
+ # Act
1263
+ result = cli_runner.invoke(main, cli_cmd[1:])
1264
+
1265
+ # Evaluate (expected -> actual)
1266
+ assert_successful_exit_code(result, cli_cmd)
1267
+ assert not result.stderr
1268
+ assert last_version == result.stdout.rstrip()
1269
+
1270
+
1271
+ def test_version_print_last_released_on_nonrelease_branch(
1272
+ cli_runner: CliRunner,
1273
+ repo_with_single_branch_tag_commits: Repo,
1274
+ ):
1275
+ last_version = "0.1.1"
1276
+ repo_with_single_branch_tag_commits.create_head("next").checkout()
1277
+
1278
+ cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released"]
1279
+
1280
+ # Act
1281
+ result = cli_runner.invoke(main, cli_cmd[1:])
1282
+
1283
+ # Evaluate (expected -> actual)
1284
+ assert_successful_exit_code(result, cli_cmd)
1285
+ assert not result.stderr
1286
+ assert last_version == result.stdout.rstrip()
1287
+
1288
+
1289
+ def test_version_print_last_released_tag_on_detached_head(
1290
+ cli_runner: CliRunner,
1291
+ repo_with_single_branch_tag_commits: Repo,
1292
+ ):
1293
+ last_version = "v0.1.1"
1294
+ repo_with_single_branch_tag_commits.git.checkout("HEAD", detach=True)
1295
+
1296
+ cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released-tag"]
1297
+
1298
+ # Act
1299
+ result = cli_runner.invoke(main, cli_cmd[1:])
1300
+
1301
+ # Evaluate (expected -> actual)
1302
+ assert_successful_exit_code(result, cli_cmd)
1303
+ assert not result.stderr
1304
+ assert last_version == result.stdout.rstrip()
1305
+
1306
+
1307
+ def test_version_print_last_released_tag_on_nonrelease_branch(
1308
+ cli_runner: CliRunner,
1309
+ repo_with_single_branch_tag_commits: Repo,
1310
+ ):
1311
+ last_version_tag = "v0.1.1"
1312
+ repo_with_single_branch_tag_commits.create_head("next").checkout()
1313
+
1314
+ cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print-last-released-tag"]
1315
+
1316
+ # Act
1317
+ result = cli_runner.invoke(main, cli_cmd[1:])
1318
+
1319
+ # Evaluate (expected -> actual)
1320
+ assert_successful_exit_code(result, cli_cmd)
1321
+ assert not result.stderr
1322
+ assert last_version_tag == result.stdout.rstrip()
1323
+
1324
+
1325
+ def test_version_print_next_version_fails_on_detached_head(
1326
+ cli_runner: CliRunner,
1327
+ example_git_ssh_url: str,
1328
+ repo_with_single_branch_tag_commits: Repo,
1329
+ simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn,
1330
+ ):
1331
+ # Setup
1332
+ repo_with_single_branch_tag_commits.git.checkout("HEAD", detach=True)
1333
+ simulate_change_commits_n_rtn_changelog_entry(
1334
+ repo_with_single_branch_tag_commits,
1335
+ ["fix: make a patch fix to codebase"],
1336
+ Github(example_git_ssh_url),
1337
+ )
1338
+ expected_error_msg = (
1339
+ "Detached HEAD state cannot match any release groups; no release will be made\n"
1340
+ )
1341
+
1342
+ # Act
1343
+ cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--print"]
1344
+ result = cli_runner.invoke(main, cli_cmd[1:])
1345
+
1346
+ # Evaluate (expected -> actual)
1347
+ assert_exit_code(1, result, cli_cmd)
1348
+ assert not result.stdout
1349
+ assert expected_error_msg == result.stderr