python-semantic-release 10.2.0__py3-none-any.whl → 10.3.1__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.4
2
2
  Name: python-semantic-release
3
- Version: 10.2.0
3
+ Version: 10.3.1
4
4
  Summary: Automatic Semantic Versioning for Python projects
5
5
  Author-email: Rolf Erik Lekang <me@rolflekang.com>
6
6
  License: MIT
@@ -88,5 +88,5 @@ The usage information and examples for this GitHub Action is available under
88
88
  the `GitHub Actions section`_ of `python-semantic-release.readthedocs.io`_.
89
89
 
90
90
  .. _python-semantic-release: https://pypi.org/project/python-semantic-release/
91
- .. _python-semantic-release.readthedocs.io: https://python-semantic-release.readthedocs.io/en/latest/
92
- .. _GitHub Actions section: https://python-semantic-release.readthedocs.io/en/latest/automatic-releases/github-actions.html
91
+ .. _python-semantic-release.readthedocs.io: https://python-semantic-release.readthedocs.io/en/stable/
92
+ .. _GitHub Actions section: https://python-semantic-release.readthedocs.io/en/stable/configuration/automatic-releases/github-actions.html
@@ -1,4 +1,4 @@
1
- python_semantic_release-10.2.0.dist-info/licenses/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
1
+ python_semantic_release-10.3.1.dist-info/licenses/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
2
2
  semantic_release/__init__.py,sha256=tRJWhrn_dUt0QycXD2DoJSfEP5uwmxngH7jvbG2i-hA,1317
3
3
  semantic_release/__main__.py,sha256=pksxr6g1vkKq98Q1lShsxG8tk55IMiSMHzAHKyFU5x0,1704
4
4
  semantic_release/const.py,sha256=wInJR7vcOgT1ysm5VuJQ6lD_ZGYnCwRVKz7Uz3htQc4,861
@@ -17,15 +17,15 @@ semantic_release/cli/changelog_writer.py,sha256=2jP5b0LK61Y2tb22GQSFFmwHwXd2Wjbo
17
17
  semantic_release/cli/cli_context.py,sha256=Nop71LdVCJOeSUHgTXunMyK3xAu_QKQC2cRp1QBVkX0,4134
18
18
  semantic_release/cli/config.py,sha256=4UCx4-jzKpggb7dZM3fnZoi8rPiUGWtEmLiCfWlro7I,33458
19
19
  semantic_release/cli/const.py,sha256=h7XE2D0D__TAZSrUUtVszwvzpkHTMOiQCf97XQNbEvA,163
20
- semantic_release/cli/github_actions_output.py,sha256=5m0IwIUu-Ntse_Uwrxhn5lYmaVN16JUyOADvmEgsYxg,2293
20
+ semantic_release/cli/github_actions_output.py,sha256=uiIZKVq3Rr2Q_Xt8UlpCszrc-HBB-2f9HGxB7KoF2qE,5209
21
21
  semantic_release/cli/masking_filter.py,sha256=GsTyaoZbUVJLXVMqeXhCttXK84UnBQ8cNDSHxd52sOc,3218
22
- semantic_release/cli/util.py,sha256=4hiqvMzuUzMJ0-vYn-mYoB5nctBHjL45EhLqApbc2DQ,3729
22
+ semantic_release/cli/util.py,sha256=4rf4xDHb7l-XpdcFNtslFz1xHslnBu6cTgWHXVnQXQs,3746
23
23
  semantic_release/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  semantic_release/cli/commands/changelog.py,sha256=wJfd4VVfrGnu2jnIpG25cdVcbXIX-ElKl3b2jdtPPa0,5391
25
25
  semantic_release/cli/commands/generate_config.py,sha256=2xZOu3NpyhBp0pWr7d8ugKl_kjqQgpSsSMHq5wHTfrE,1699
26
26
  semantic_release/cli/commands/main.py,sha256=u1zhkkvKCZ2TtUqjzvdFTe5UZsvfws_pjqqo6CY0bBo,4351
27
27
  semantic_release/cli/commands/publish.py,sha256=CE_LJTxFnc337MfpsfdJopi7QCwwE13GqGNQ-dNgWis,2871
28
- semantic_release/cli/commands/version.py,sha256=d72k2YVkynbBcjFPbWJr80PjjYE7JMd_btaL4baByaM,25762
28
+ semantic_release/cli/commands/version.py,sha256=Mf0lulFWKp4VIGAq-sTBnLGO60xpe6skmUbVdhUUKqg,26681
29
29
  semantic_release/commit_parser/__init__.py,sha256=6euiDgj9bwOx1rP96vUjq090usviXkbo7OVOnRBGfcw,742
30
30
  semantic_release/commit_parser/_base.py,sha256=DLsHnbXG-39JkUbcnsBCSV2GmV35w1rasyoMhK8G0UE,3058
31
31
  semantic_release/commit_parser/angular.py,sha256=MY_fo9F4EZ-ac8wYzBR0uD94O5Li2D-8zEMR01wss4c,18534
@@ -73,8 +73,8 @@ semantic_release/version/declarations/enum.py,sha256=3n5Py9DoFkmItIdsmtQrJgmAhep
73
73
  semantic_release/version/declarations/i_version_replacer.py,sha256=oP6BxJuxwI44roI6448tomShv1sMoy9ry8TlhhIQtfc,2416
74
74
  semantic_release/version/declarations/pattern.py,sha256=MpUmsHYGAVAuFSKSb29FLcWeUCEHG_TRyhMO-2DWAAs,8308
75
75
  semantic_release/version/declarations/toml.py,sha256=2K4DtX5Qq1iHT8cG8mISPTMmp50w6Av0KmLAKZPYqq8,4931
76
- python_semantic_release-10.2.0.dist-info/METADATA,sha256=_wRj0vN8sxY4OE7ls1MbS9kWhFLnT1gD2AWwTjp-D-A,3913
77
- python_semantic_release-10.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
78
- python_semantic_release-10.2.0.dist-info/entry_points.txt,sha256=kzkCyDJsMOwgpFwEWKE9wxN1tXaUP6g6GIO4xtc0QuE,162
79
- python_semantic_release-10.2.0.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
80
- python_semantic_release-10.2.0.dist-info/RECORD,,
76
+ python_semantic_release-10.3.1.dist-info/METADATA,sha256=ALVaRulT0o1-464ffA0WQMrjKorzp-2GJCIg0z9B-8Q,3927
77
+ python_semantic_release-10.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
78
+ python_semantic_release-10.3.1.dist-info/entry_points.txt,sha256=kzkCyDJsMOwgpFwEWKE9wxN1tXaUP6g6GIO4xtc0QuE,162
79
+ python_semantic_release-10.3.1.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
80
+ python_semantic_release-10.3.1.dist-info/RECORD,,
@@ -18,7 +18,10 @@ from semantic_release.cli.changelog_writer import (
18
18
  generate_release_notes,
19
19
  write_changelog_files,
20
20
  )
21
- from semantic_release.cli.github_actions_output import VersionGitHubActionsOutput
21
+ from semantic_release.cli.github_actions_output import (
22
+ PersistenceMode,
23
+ VersionGitHubActionsOutput,
24
+ )
22
25
  from semantic_release.cli.util import noop_report, rprint
23
26
  from semantic_release.const import DEFAULT_SHELL, DEFAULT_VERSION
24
27
  from semantic_release.enums import LevelBump
@@ -30,6 +33,7 @@ from semantic_release.errors import (
30
33
  )
31
34
  from semantic_release.gitproject import GitProject
32
35
  from semantic_release.globals import logger
36
+ from semantic_release.hvcs.github import Github
33
37
  from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase
34
38
  from semantic_release.version.algorithm import (
35
39
  next_version,
@@ -466,7 +470,19 @@ def version( # noqa: C901
466
470
  major_on_zero = runtime.major_on_zero
467
471
  no_verify = runtime.no_git_verify
468
472
  opts = runtime.global_cli_options
469
- gha_output = VersionGitHubActionsOutput(released=False)
473
+ gha_output = VersionGitHubActionsOutput(
474
+ gh_client=(
475
+ hvcs_client
476
+ if isinstance(hvcs_client, Github)
477
+ else Github(hvcs_client.remote_url(use_token=False))
478
+ ),
479
+ mode=(
480
+ PersistenceMode.TEMPORARY
481
+ if opts.noop or (not commit_changes and not create_tag)
482
+ else PersistenceMode.PERMANENT
483
+ ),
484
+ released=False,
485
+ )
470
486
 
471
487
  forced_level_bump = None if not force_level else LevelBump.from_string(force_level)
472
488
  prerelease = is_forced_prerelease(
@@ -572,6 +588,12 @@ def version( # noqa: C901
572
588
  if print_only or print_only_tag:
573
589
  return
574
590
 
591
+ # TODO: need a better way as this is inconsistent if releasing older version patches
592
+ if last_release := last_released(config.repo_dir, tag_format=config.tag_format):
593
+ # If we have a last release, we can set the previous version for the
594
+ # GitHub Actions output
595
+ gha_output.prev_version = last_release[1]
596
+
575
597
  with Repo(str(runtime.repo_dir)) as git_repo:
576
598
  release_history = ReleaseHistory.from_git_history(
577
599
  repo=git_repo,
@@ -641,6 +663,29 @@ def version( # noqa: C901
641
663
  click.echo("Build failed, aborting release", err=True)
642
664
  ctx.exit(1)
643
665
 
666
+ license_cfg = runtime.project_metadata.get(
667
+ "license-expression",
668
+ runtime.project_metadata.get(
669
+ "license",
670
+ "",
671
+ ),
672
+ )
673
+
674
+ license_cfg = "" if not isinstance(license_cfg, (str, dict)) else license_cfg
675
+ license_cfg = (
676
+ license_cfg.get("text", "") if isinstance(license_cfg, dict) else license_cfg
677
+ )
678
+
679
+ gha_output.release_notes = release_notes = generate_release_notes(
680
+ hvcs_client,
681
+ release=release_history.released[new_version],
682
+ template_dir=runtime.template_dir,
683
+ history=release_history,
684
+ style=runtime.changelog_style,
685
+ mask_initial_release=runtime.changelog_mask_initial_release,
686
+ license_name="" if not isinstance(license_cfg, str) else license_cfg,
687
+ )
688
+
644
689
  project = GitProject(
645
690
  directory=runtime.repo_dir,
646
691
  commit_author=runtime.commit_author,
@@ -676,6 +721,9 @@ def version( # noqa: C901
676
721
  noop=opts.noop,
677
722
  )
678
723
 
724
+ with Repo(str(runtime.repo_dir)) as git_repo:
725
+ gha_output.commit_sha = git_repo.head.commit.hexsha
726
+
679
727
  if push_changes:
680
728
  remote_url = runtime.hvcs_client.remote_url(
681
729
  use_token=not runtime.ignore_token_for_push
@@ -710,33 +758,6 @@ def version( # noqa: C901
710
758
  logger.info("Remote does not support releases. Skipping release creation...")
711
759
  return
712
760
 
713
- license_cfg = runtime.project_metadata.get(
714
- "license-expression",
715
- runtime.project_metadata.get(
716
- "license",
717
- "",
718
- ),
719
- )
720
-
721
- if not isinstance(license_cfg, (str, dict)) or license_cfg is None:
722
- license_cfg = ""
723
-
724
- license_name = (
725
- license_cfg.get("text", "")
726
- if isinstance(license_cfg, dict)
727
- else license_cfg or ""
728
- )
729
-
730
- release_notes = generate_release_notes(
731
- hvcs_client,
732
- release=release_history.released[new_version],
733
- template_dir=runtime.template_dir,
734
- history=release_history,
735
- style=runtime.changelog_style,
736
- mask_initial_release=runtime.changelog_mask_initial_release,
737
- license_name=license_name,
738
- )
739
-
740
761
  exception: Exception | None = None
741
762
  help_message = ""
742
763
  try:
@@ -1,21 +1,44 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ from enum import Enum
5
+ from re import compile as regexp
6
+ from typing import TYPE_CHECKING
4
7
 
5
8
  from semantic_release.globals import logger
6
9
  from semantic_release.version.version import Version
7
10
 
11
+ if TYPE_CHECKING:
12
+ from typing import Any
13
+
14
+ from semantic_release.hvcs.github import Github
15
+
16
+
17
+ class PersistenceMode(Enum):
18
+ TEMPORARY = "temporary"
19
+ PERMANENT = "permanent"
20
+
8
21
 
9
22
  class VersionGitHubActionsOutput:
10
23
  OUTPUT_ENV_VAR = "GITHUB_OUTPUT"
11
24
 
12
25
  def __init__(
13
26
  self,
27
+ gh_client: Github,
28
+ mode: PersistenceMode = PersistenceMode.PERMANENT,
14
29
  released: bool | None = None,
15
30
  version: Version | None = None,
31
+ commit_sha: str | None = None,
32
+ release_notes: str | None = None,
33
+ prev_version: Version | None = None,
16
34
  ) -> None:
35
+ self._gh_client = gh_client
36
+ self._mode = mode
17
37
  self._released = released
18
38
  self._version = version
39
+ self._commit_sha = commit_sha
40
+ self._release_notes = release_notes
41
+ self._prev_version = prev_version
19
42
 
20
43
  @property
21
44
  def released(self) -> bool | None:
@@ -23,7 +46,7 @@ class VersionGitHubActionsOutput:
23
46
 
24
47
  @released.setter
25
48
  def released(self, value: bool) -> None:
26
- if type(value) is not bool:
49
+ if not isinstance(value, bool):
27
50
  raise TypeError("output 'released' is boolean")
28
51
  self._released = value
29
52
 
@@ -33,7 +56,7 @@ class VersionGitHubActionsOutput:
33
56
 
34
57
  @version.setter
35
58
  def version(self, value: Version) -> None:
36
- if type(value) is not Version:
59
+ if not isinstance(value, Version):
37
60
  raise TypeError("output 'released' should be a Version")
38
61
  self._version = value
39
62
 
@@ -45,26 +68,86 @@ class VersionGitHubActionsOutput:
45
68
  def is_prerelease(self) -> bool | None:
46
69
  return self.version.is_prerelease if self.version is not None else None
47
70
 
71
+ @property
72
+ def commit_sha(self) -> str | None:
73
+ return self._commit_sha if self._commit_sha else None
74
+
75
+ @commit_sha.setter
76
+ def commit_sha(self, value: str) -> None:
77
+ if not isinstance(value, str):
78
+ raise TypeError("output 'commit_sha' should be a string")
79
+
80
+ if not regexp(r"^[0-9a-f]{40}$").match(value):
81
+ raise ValueError(
82
+ "output 'commit_sha' should be a valid 40-hex-character SHA"
83
+ )
84
+
85
+ self._commit_sha = value
86
+
87
+ @property
88
+ def release_notes(self) -> str | None:
89
+ return self._release_notes if self._release_notes else None
90
+
91
+ @release_notes.setter
92
+ def release_notes(self, value: str) -> None:
93
+ if not isinstance(value, str):
94
+ raise TypeError("output 'release_notes' should be a string")
95
+ self._release_notes = value
96
+
97
+ @property
98
+ def prev_version(self) -> Version | None:
99
+ if not self.released:
100
+ return self.version
101
+ return self._prev_version if self._prev_version else None
102
+
103
+ @prev_version.setter
104
+ def prev_version(self, value: Version) -> None:
105
+ if not isinstance(value, Version):
106
+ raise TypeError("output 'prev_version' should be a Version")
107
+ self._prev_version = value
108
+
48
109
  def to_output_text(self) -> str:
49
- missing = set()
110
+ missing: set[str] = set()
50
111
  if self.version is None:
51
112
  missing.add("version")
52
113
  if self.released is None:
53
114
  missing.add("released")
115
+ if self.released:
116
+ if self.release_notes is None:
117
+ missing.add("release_notes")
118
+ if self._mode is PersistenceMode.PERMANENT and self.commit_sha is None:
119
+ missing.add("commit_sha")
54
120
 
55
121
  if missing:
56
122
  raise ValueError(
57
123
  f"some required outputs were not set: {', '.join(missing)}"
58
124
  )
59
125
 
60
- outputs = {
126
+ output_values: dict[str, Any] = {
61
127
  "released": str(self.released).lower(),
62
128
  "version": str(self.version),
63
129
  "tag": self.tag,
64
130
  "is_prerelease": str(self.is_prerelease).lower(),
131
+ "link": self._gh_client.create_release_url(self.tag) if self.tag else "",
132
+ "previous_version": str(self.prev_version) if self.prev_version else "",
133
+ "commit_sha": self.commit_sha if self.commit_sha else "",
65
134
  }
66
135
 
67
- return str.join("", [f"{key}={value!s}\n" for key, value in outputs.items()])
136
+ multiline_output_values: dict[str, str] = {
137
+ "release_notes": self.release_notes if self.release_notes else "",
138
+ }
139
+
140
+ output_lines = [
141
+ *[f"{key}={value!s}{os.linesep}" for key, value in output_values.items()],
142
+ *[
143
+ f"{key}<<EOF{os.linesep}{value}EOF{os.linesep}"
144
+ if value
145
+ else f"{key}={os.linesep}"
146
+ for key, value in multiline_output_values.items()
147
+ ],
148
+ ]
149
+
150
+ return str.join("", output_lines)
68
151
 
69
152
  def write_if_possible(self, filename: str | None = None) -> None:
70
153
  output_file = filename or os.getenv(self.OUTPUT_ENV_VAR)
@@ -72,5 +155,5 @@ class VersionGitHubActionsOutput:
72
155
  logger.info("not writing GitHub Actions output, as no file specified")
73
156
  return
74
157
 
75
- with open(output_file, "a", encoding="utf-8") as f:
76
- f.write(self.to_output_text())
158
+ with open(output_file, "ab") as f:
159
+ f.write(self.to_output_text().encode("utf-8"))
@@ -9,6 +9,7 @@ from textwrap import dedent, indent
9
9
  from typing import Any
10
10
 
11
11
  import rich
12
+ import rich.markup
12
13
  import tomlkit
13
14
  from tomlkit.exceptions import TOMLKitError
14
15
 
@@ -26,8 +27,7 @@ def noop_report(msg: str) -> None:
26
27
  Rich-prints a msg with a standard prefix to report when an action is not being
27
28
  taken due to a "noop" flag
28
29
  """
29
- fullmsg = "[bold cyan][:shield: NOP] " + msg
30
- rprint(fullmsg)
30
+ rprint(f"[bold cyan][:shield: NOP] {rich.markup.escape(msg)}")
31
31
 
32
32
 
33
33
  def indented(msg: str, prefix: str = " " * 4) -> str: