python-semantic-release 10.2.0__py3-none-any.whl → 10.3.0__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.0
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.0.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=3oWCUfOqa2qVV9DBgwYx2pvhYecVBF6DkzILNnDBikQ,4965
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=lV4GiDbo27-du_pF1ImxmdX569BgyzryhP9UZxDKYAk,26434
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.0.dist-info/METADATA,sha256=KC3FGBwckcF-gL-W3C-Xnvd-T1XHSQjwAgIinftCf4o,3927
77
+ python_semantic_release-10.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
78
+ python_semantic_release-10.3.0.dist-info/entry_points.txt,sha256=kzkCyDJsMOwgpFwEWKE9wxN1tXaUP6g6GIO4xtc0QuE,162
79
+ python_semantic_release-10.3.0.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
80
+ python_semantic_release-10.3.0.dist-info/RECORD,,
@@ -30,6 +30,7 @@ from semantic_release.errors import (
30
30
  )
31
31
  from semantic_release.gitproject import GitProject
32
32
  from semantic_release.globals import logger
33
+ from semantic_release.hvcs.github import Github
33
34
  from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase
34
35
  from semantic_release.version.algorithm import (
35
36
  next_version,
@@ -466,7 +467,12 @@ def version( # noqa: C901
466
467
  major_on_zero = runtime.major_on_zero
467
468
  no_verify = runtime.no_git_verify
468
469
  opts = runtime.global_cli_options
469
- gha_output = VersionGitHubActionsOutput(released=False)
470
+ gha_output = VersionGitHubActionsOutput(
471
+ hvcs_client
472
+ if isinstance(hvcs_client, Github)
473
+ else Github(hvcs_client.remote_url(use_token=False)),
474
+ released=False,
475
+ )
470
476
 
471
477
  forced_level_bump = None if not force_level else LevelBump.from_string(force_level)
472
478
  prerelease = is_forced_prerelease(
@@ -572,6 +578,12 @@ def version( # noqa: C901
572
578
  if print_only or print_only_tag:
573
579
  return
574
580
 
581
+ # TODO: need a better way as this is inconsistent if releasing older version patches
582
+ if last_release := last_released(config.repo_dir, tag_format=config.tag_format):
583
+ # If we have a last release, we can set the previous version for the
584
+ # GitHub Actions output
585
+ gha_output.prev_version = last_release[1]
586
+
575
587
  with Repo(str(runtime.repo_dir)) as git_repo:
576
588
  release_history = ReleaseHistory.from_git_history(
577
589
  repo=git_repo,
@@ -641,6 +653,29 @@ def version( # noqa: C901
641
653
  click.echo("Build failed, aborting release", err=True)
642
654
  ctx.exit(1)
643
655
 
656
+ license_cfg = runtime.project_metadata.get(
657
+ "license-expression",
658
+ runtime.project_metadata.get(
659
+ "license",
660
+ "",
661
+ ),
662
+ )
663
+
664
+ license_cfg = "" if not isinstance(license_cfg, (str, dict)) else license_cfg
665
+ license_cfg = (
666
+ license_cfg.get("text", "") if isinstance(license_cfg, dict) else license_cfg
667
+ )
668
+
669
+ gha_output.release_notes = release_notes = generate_release_notes(
670
+ hvcs_client,
671
+ release=release_history.released[new_version],
672
+ template_dir=runtime.template_dir,
673
+ history=release_history,
674
+ style=runtime.changelog_style,
675
+ mask_initial_release=runtime.changelog_mask_initial_release,
676
+ license_name="" if not isinstance(license_cfg, str) else license_cfg,
677
+ )
678
+
644
679
  project = GitProject(
645
680
  directory=runtime.repo_dir,
646
681
  commit_author=runtime.commit_author,
@@ -676,6 +711,9 @@ def version( # noqa: C901
676
711
  noop=opts.noop,
677
712
  )
678
713
 
714
+ with Repo(str(runtime.repo_dir)) as git_repo:
715
+ gha_output.commit_sha = git_repo.head.commit.hexsha
716
+
679
717
  if push_changes:
680
718
  remote_url = runtime.hvcs_client.remote_url(
681
719
  use_token=not runtime.ignore_token_for_push
@@ -710,33 +748,6 @@ def version( # noqa: C901
710
748
  logger.info("Remote does not support releases. Skipping release creation...")
711
749
  return
712
750
 
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
751
  exception: Exception | None = None
741
752
  help_message = ""
742
753
  try:
@@ -1,21 +1,36 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ from re import compile as regexp
5
+ from typing import TYPE_CHECKING
4
6
 
5
7
  from semantic_release.globals import logger
6
8
  from semantic_release.version.version import Version
7
9
 
10
+ if TYPE_CHECKING:
11
+ from typing import Any
12
+
13
+ from semantic_release.hvcs.github import Github
14
+
8
15
 
9
16
  class VersionGitHubActionsOutput:
10
17
  OUTPUT_ENV_VAR = "GITHUB_OUTPUT"
11
18
 
12
19
  def __init__(
13
20
  self,
21
+ gh_client: Github,
14
22
  released: bool | None = None,
15
23
  version: Version | None = None,
24
+ commit_sha: str | None = None,
25
+ release_notes: str | None = None,
26
+ prev_version: Version | None = None,
16
27
  ) -> None:
28
+ self._gh_client = gh_client
17
29
  self._released = released
18
30
  self._version = version
31
+ self._commit_sha = commit_sha
32
+ self._release_notes = release_notes
33
+ self._prev_version = prev_version
19
34
 
20
35
  @property
21
36
  def released(self) -> bool | None:
@@ -23,7 +38,7 @@ class VersionGitHubActionsOutput:
23
38
 
24
39
  @released.setter
25
40
  def released(self, value: bool) -> None:
26
- if type(value) is not bool:
41
+ if not isinstance(value, bool):
27
42
  raise TypeError("output 'released' is boolean")
28
43
  self._released = value
29
44
 
@@ -33,7 +48,7 @@ class VersionGitHubActionsOutput:
33
48
 
34
49
  @version.setter
35
50
  def version(self, value: Version) -> None:
36
- if type(value) is not Version:
51
+ if not isinstance(value, Version):
37
52
  raise TypeError("output 'released' should be a Version")
38
53
  self._version = value
39
54
 
@@ -45,26 +60,85 @@ class VersionGitHubActionsOutput:
45
60
  def is_prerelease(self) -> bool | None:
46
61
  return self.version.is_prerelease if self.version is not None else None
47
62
 
63
+ @property
64
+ def commit_sha(self) -> str | None:
65
+ return self._commit_sha if self._commit_sha else None
66
+
67
+ @commit_sha.setter
68
+ def commit_sha(self, value: str) -> None:
69
+ if not isinstance(value, str):
70
+ raise TypeError("output 'commit_sha' should be a string")
71
+
72
+ if not regexp(r"^[0-9a-f]{40}$").match(value):
73
+ raise ValueError(
74
+ "output 'commit_sha' should be a valid 40-hex-character SHA"
75
+ )
76
+
77
+ self._commit_sha = value
78
+
79
+ @property
80
+ def release_notes(self) -> str | None:
81
+ return self._release_notes if self._release_notes else None
82
+
83
+ @release_notes.setter
84
+ def release_notes(self, value: str) -> None:
85
+ if not isinstance(value, str):
86
+ raise TypeError("output 'release_notes' should be a string")
87
+ self._release_notes = value
88
+
89
+ @property
90
+ def prev_version(self) -> Version | None:
91
+ if not self.released:
92
+ return self.version
93
+ return self._prev_version if self._prev_version else None
94
+
95
+ @prev_version.setter
96
+ def prev_version(self, value: Version) -> None:
97
+ if not isinstance(value, Version):
98
+ raise TypeError("output 'prev_version' should be a Version")
99
+ self._prev_version = value
100
+
48
101
  def to_output_text(self) -> str:
49
- missing = set()
102
+ missing: set[str] = set()
50
103
  if self.version is None:
51
104
  missing.add("version")
52
105
  if self.released is None:
53
106
  missing.add("released")
107
+ if self.released and self.commit_sha is None:
108
+ missing.add("commit_sha")
109
+ if self.released and self.release_notes is None:
110
+ missing.add("release_notes")
54
111
 
55
112
  if missing:
56
113
  raise ValueError(
57
114
  f"some required outputs were not set: {', '.join(missing)}"
58
115
  )
59
116
 
60
- outputs = {
117
+ output_values: dict[str, Any] = {
61
118
  "released": str(self.released).lower(),
62
119
  "version": str(self.version),
63
120
  "tag": self.tag,
64
121
  "is_prerelease": str(self.is_prerelease).lower(),
122
+ "link": self._gh_client.create_release_url(self.tag) if self.tag else "",
123
+ "previous_version": str(self.prev_version) if self.prev_version else "",
124
+ "commit_sha": self.commit_sha if self.commit_sha else "",
125
+ }
126
+
127
+ multiline_output_values: dict[str, str] = {
128
+ "release_notes": self.release_notes if self.release_notes else "",
65
129
  }
66
130
 
67
- return str.join("", [f"{key}={value!s}\n" for key, value in outputs.items()])
131
+ output_lines = [
132
+ *[f"{key}={value!s}{os.linesep}" for key, value in output_values.items()],
133
+ *[
134
+ f"{key}<<EOF{os.linesep}{value}EOF{os.linesep}"
135
+ if value
136
+ else f"{key}={os.linesep}"
137
+ for key, value in multiline_output_values.items()
138
+ ],
139
+ ]
140
+
141
+ return str.join("", output_lines)
68
142
 
69
143
  def write_if_possible(self, filename: str | None = None) -> None:
70
144
  output_file = filename or os.getenv(self.OUTPUT_ENV_VAR)
@@ -72,5 +146,5 @@ class VersionGitHubActionsOutput:
72
146
  logger.info("not writing GitHub Actions output, as no file specified")
73
147
  return
74
148
 
75
- with open(output_file, "a", encoding="utf-8") as f:
76
- f.write(self.to_output_text())
149
+ with open(output_file, "ab") as f:
150
+ 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: