python-semantic-release 9.8.4__py3-none-any.whl → 9.8.5__py3-none-any.whl

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.
@@ -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
@@ -1,4 +1,4 @@
1
- semantic_release/__init__.py,sha256=2zPAOZ5IfYsvLnhab9ZWAjy_bunkNmm0OwE3CVcP6rI,1228
1
+ semantic_release/__init__.py,sha256=blL7pv_bzsszX08mfPgx5FgX1CIfqZ15ib-FGb8fxEA,1228
2
2
  semantic_release/__main__.py,sha256=blPn7CMpuSe4Z-GBIDv0QA0OEDTMGU-7edsLRkkhPR4,120
3
3
  semantic_release/const.py,sha256=Z1o2QNh60wSLeF-_1TemMBjU3ZXbV0XghnUFsbTVfOs,831
4
4
  semantic_release/enums.py,sha256=D5B_reQGGKQQT22HO5PUtvn2Bok3fkht6TfJtXkmAUg,1020
@@ -7,11 +7,11 @@ semantic_release/gitproject.py,sha256=izWc4NLdUzAwxGG_fJeqqHW9ivSrPcWBzSaOijQx4f
7
7
  semantic_release/helpers.py,sha256=d1jOX0SNyqPc_3wr14xR25FfpqhMd4Ev7MNBOWlScc0,5581
8
8
  semantic_release/changelog/__init__.py,sha256=Bg6Xe5Vt32rWoMscW-hd4sUwiZqzWmsg4CD1EhMesMY,262
9
9
  semantic_release/changelog/context.py,sha256=8cbOv9cB9Ji7xjRn4-YUUHBfg9LLfSwmmXuZpqEmvns,1653
10
- semantic_release/changelog/release_history.py,sha256=jfkfnlATBqRpVLzxVi7wf2fO_bH_GOifQbaMO-mqDeY,6781
10
+ semantic_release/changelog/release_history.py,sha256=9gJxqOKPF9HjmOdXW-k-dWbe2YGWxqZEOWSSef-qwoE,7050
11
11
  semantic_release/changelog/template.py,sha256=JqZcjdozM9-059Sa8PfiHxYNeyZFa0jxk_beyRLv5sU,4640
12
12
  semantic_release/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  semantic_release/cli/changelog_writer.py,sha256=Ga8QAq12kUD-fWWlrFoQCpn1pOzZmHnaCeZfFbBytFo,5027
14
- semantic_release/cli/cli_context.py,sha256=LApmhR6PTdHTPxs42KYMT_yYbaEXae-myuvio0mhI8w,3522
14
+ semantic_release/cli/cli_context.py,sha256=23eyV6KWIpUckUSWILEd7t9dixp-QCY5-kGZnyucEYY,4114
15
15
  semantic_release/cli/config.py,sha256=NAKOqLAc8NV0oL9XxAUPz2FWwS89Bjz8aKSCsRmQsvw,23661
16
16
  semantic_release/cli/const.py,sha256=S8oNNpSaKdZxcAs7PAKEjQHic0czoJBGd7XHo57FOzw,39
17
17
  semantic_release/cli/github_actions_output.py,sha256=Wy4jEIpSFgoMyJqq4FD7scIpgovivutFLZ7Fs4y2CUY,2110
@@ -22,7 +22,7 @@ semantic_release/cli/commands/changelog.py,sha256=zincrvCBwMb7S3K3udRcArtQCuGkre
22
22
  semantic_release/cli/commands/generate_config.py,sha256=2xZOu3NpyhBp0pWr7d8ugKl_kjqQgpSsSMHq5wHTfrE,1699
23
23
  semantic_release/cli/commands/main.py,sha256=kqO8bZw9Nv6T5QkIl53zzmH2rd4LuLoA8agweH0VYPY,4022
24
24
  semantic_release/cli/commands/publish.py,sha256=SZQlIewvqyIC14dkIIVVFetE0tPsKbO1cUyxnZsicrw,2845
25
- semantic_release/cli/commands/version.py,sha256=Saunh8juMDHJuPK8VcH0ZiUfYJduNODSwsAX2DjLki4,23809
25
+ semantic_release/cli/commands/version.py,sha256=sgyIGF62XR2hLAKuyXMdA8OI5OEaQky-kaSoWJYlBBQ,24110
26
26
  semantic_release/commit_parser/__init__.py,sha256=cv5HFBdw7OJd4Laj4Ex8ZZ5Tml8GwXgQcXW6Pasr2Ao,615
27
27
  semantic_release/commit_parser/_base.py,sha256=t-Z9ALgAe7aZpYXz1mk3Fe-uAvipgKdNrq4Okg_WW9c,3026
28
28
  semantic_release/commit_parser/angular.py,sha256=ERWeTBTFNIBOaTIopY4Vff0KbeFwrSQWbADe3smVW-8,4579
@@ -47,10 +47,10 @@ semantic_release/version/algorithm.py,sha256=ofx_bIWq6ptJVr-ekI11IzxzDEctDKFiVwa
47
47
  semantic_release/version/declaration.py,sha256=f6Ld7hIhrqvDrRBapJHr-KDimuyo-4IG8009Zu9BIgU,7357
48
48
  semantic_release/version/translator.py,sha256=P1noIsVBn8u6zNOFjG0xKYOWapxqf_PHSMvMeLJ9kXg,3050
49
49
  semantic_release/version/version.py,sha256=6PCtSbLP88U1daoxnCwHc--YguZo4waGNLqJ5JfeczE,14175
50
- python_semantic_release-9.8.4.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
51
- python_semantic_release-9.8.4.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
52
- python_semantic_release-9.8.4.dist-info/METADATA,sha256=pfsnObyVM-vMXf-fME2dZbgH1yyiboC8gH805RJLvVE,5404
53
- python_semantic_release-9.8.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
54
- python_semantic_release-9.8.4.dist-info/entry_points.txt,sha256=6bS6euSagjerp7onDtfI9_ZpczWreZF3gjHfGAegkbo,123
55
- python_semantic_release-9.8.4.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
56
- python_semantic_release-9.8.4.dist-info/RECORD,,
50
+ python_semantic_release-9.8.5.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
51
+ python_semantic_release-9.8.5.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
52
+ python_semantic_release-9.8.5.dist-info/METADATA,sha256=TjNY6oQvBuham-GTQSZyLa0uIXkrm4LZnPGwyqPF1SI,5404
53
+ python_semantic_release-9.8.5.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
54
+ python_semantic_release-9.8.5.dist-info/entry_points.txt,sha256=6bS6euSagjerp7onDtfI9_ZpczWreZF3gjHfGAegkbo,123
55
+ python_semantic_release-9.8.5.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
56
+ python_semantic_release-9.8.5.dist-info/RECORD,,
@@ -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