python-semantic-release 9.7.3__py3-none-any.whl → 9.8.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.1
2
2
  Name: python-semantic-release
3
- Version: 9.7.3
3
+ Version: 9.8.1
4
4
  Summary: Automatic Semantic Versioning for Python projects
5
5
  Author-email: Rolf Erik Lekang <me@rolflekang.com>
6
6
  License: MIT
@@ -37,7 +37,7 @@ Requires-Dist: build ~=1.2 ; extra == 'build'
37
37
  Provides-Extra: dev
38
38
  Requires-Dist: pre-commit ~=3.5 ; extra == 'dev'
39
39
  Requires-Dist: tox ~=4.11 ; extra == 'dev'
40
- Requires-Dist: ruff ==0.4.3 ; extra == 'dev'
40
+ Requires-Dist: ruff ==0.4.4 ; extra == 'dev'
41
41
  Provides-Extra: docs
42
42
  Requires-Dist: Sphinx ~=6.0 ; extra == 'docs'
43
43
  Requires-Dist: sphinxcontrib-apidoc ==0.5.0 ; extra == 'docs'
@@ -1,4 +1,4 @@
1
- semantic_release/__init__.py,sha256=AZQWQT9mepV90IGGzHfKVqLS8S9iWbQ8KQInlRZaCAo,870
1
+ semantic_release/__init__.py,sha256=lXpPMb5uIiSyQhUCbxSLX5FuhekMzxQT4tW-ESrktBI,870
2
2
  semantic_release/__main__.py,sha256=vXOARLV7KHeVXgpOMU0Q-8wovpdsOrtTsycxulI5xIE,106
3
3
  semantic_release/const.py,sha256=Z1o2QNh60wSLeF-_1TemMBjU3ZXbV0XghnUFsbTVfOs,831
4
4
  semantic_release/enums.py,sha256=D5B_reQGGKQQT22HO5PUtvn2Bok3fkht6TfJtXkmAUg,1020
@@ -10,7 +10,7 @@ semantic_release/changelog/release_history.py,sha256=ShzZpxTjhxka6TMbzOzYzG3EuCH
10
10
  semantic_release/changelog/template.py,sha256=JqZcjdozM9-059Sa8PfiHxYNeyZFa0jxk_beyRLv5sU,4640
11
11
  semantic_release/cli/__init__.py,sha256=gwAYhZO-4TR0tKCkQ4GqutWsL--0CII2_dcbrElI4M4,419
12
12
  semantic_release/cli/common.py,sha256=cVQUI7Uaac_nJ7VEXuJ7Q9pVJpuMVRvE7bBl_i96g20,1439
13
- semantic_release/cli/config.py,sha256=NhXw_qi-KElU2xd_mw1MrBo3zgstzn1c0ZOCSQNTu8s,21769
13
+ semantic_release/cli/config.py,sha256=01BDXKi_LOMSozyTLcvztZnsu8ISwqea4cvHExQL4k8,21870
14
14
  semantic_release/cli/const.py,sha256=S8oNNpSaKdZxcAs7PAKEjQHic0czoJBGd7XHo57FOzw,39
15
15
  semantic_release/cli/github_actions_output.py,sha256=Wy4jEIpSFgoMyJqq4FD7scIpgovivutFLZ7Fs4y2CUY,2110
16
16
  semantic_release/cli/masking_filter.py,sha256=DxqjiJyABlzwwwZ1r8JGQpb6QrF00StJFm0-2-s5Fv0,3071
@@ -21,7 +21,7 @@ semantic_release/cli/commands/cli_context.py,sha256=fOLTUKb4UnZGQ6zVhftuvdfZ7fK7
21
21
  semantic_release/cli/commands/generate_config.py,sha256=2xZOu3NpyhBp0pWr7d8ugKl_kjqQgpSsSMHq5wHTfrE,1699
22
22
  semantic_release/cli/commands/main.py,sha256=LuL-O0LGonI5fQTZn0AUtYv2Ei_CYyNqYHMF_wN4InI,2900
23
23
  semantic_release/cli/commands/publish.py,sha256=ZA7DuEGxZdIT9wUwP1S24qi1RdcKV-1FnYci-PvyXIg,1958
24
- semantic_release/cli/commands/version.py,sha256=cURNIwQrdrENnUwMVpSQ42kD80TZ5uqy45Ia3nBLCQc,26160
24
+ semantic_release/cli/commands/version.py,sha256=lJ_DiD17dTXZ0hB8l_lBsL960dxuUmawR9dmr1KJjvA,27279
25
25
  semantic_release/commit_parser/__init__.py,sha256=cv5HFBdw7OJd4Laj4Ex8ZZ5Tml8GwXgQcXW6Pasr2Ao,615
26
26
  semantic_release/commit_parser/_base.py,sha256=D2dY8dALV3QPK0XOLZru9btMW5dlx9YSmDIe3v5T0QA,3004
27
27
  semantic_release/commit_parser/angular.py,sha256=ERWeTBTFNIBOaTIopY4Vff0KbeFwrSQWbADe3smVW-8,4579
@@ -36,8 +36,8 @@ semantic_release/hvcs/__init__.py,sha256=JwoaLOF-12L-OBo_9-tOXXhdiHKeVungA9865to
36
36
  semantic_release/hvcs/_base.py,sha256=9-iTqTPSbiEevKbCBP9K2hq4c-2T4wPbeLWe-kAxBzo,2607
37
37
  semantic_release/hvcs/bitbucket.py,sha256=tMpl69CbZSQ8cdgiqtIzf7WSz8OQwDInGpoq-9tQr3k,9216
38
38
  semantic_release/hvcs/gitea.py,sha256=CSItRSJU39hohO3oE-ohX65Er_s2nVrDqvfc7KQtmv8,11152
39
- semantic_release/hvcs/github.py,sha256=34ZMGttmqcTcY6_D-SRn-HxUJxNT4WwBNEwYlsHC420,18287
40
- semantic_release/hvcs/gitlab.py,sha256=JtHWBaNGSMB8MTiznEhvuMm_M8lLceZPaGndzOx39Vk,6249
39
+ semantic_release/hvcs/github.py,sha256=u-n1h3ivB8d-ca2Z8Jdgkhk-JTu-XONAqpiso8rKshk,18381
40
+ semantic_release/hvcs/gitlab.py,sha256=ZVfeA2Uzl--6vVMJfJWXniPFzrsZ001Guzuu7AVSLOI,9414
41
41
  semantic_release/hvcs/remote_hvcs_base.py,sha256=plZkc5cSaiTxgIR5HhlEWrYoyWzzSXr-Jw3aXbMK_iA,6055
42
42
  semantic_release/hvcs/token_auth.py,sha256=ZjT56-NIPB4OKIt1qwHCu1TavXnrWFIBl9ARlg56hgU,663
43
43
  semantic_release/hvcs/util.py,sha256=PNKXApgL2np9lpnKjAbo77toRgElLPXrbS1q7621waI,2886
@@ -46,10 +46,10 @@ semantic_release/version/algorithm.py,sha256=-9ulTy1z4u-jUvbc-FrvEdFL7Omc3O3HhaP
46
46
  semantic_release/version/declaration.py,sha256=f6Ld7hIhrqvDrRBapJHr-KDimuyo-4IG8009Zu9BIgU,7357
47
47
  semantic_release/version/translator.py,sha256=P1noIsVBn8u6zNOFjG0xKYOWapxqf_PHSMvMeLJ9kXg,3050
48
48
  semantic_release/version/version.py,sha256=6PCtSbLP88U1daoxnCwHc--YguZo4waGNLqJ5JfeczE,14175
49
- python_semantic_release-9.7.3.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
50
- python_semantic_release-9.7.3.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
51
- python_semantic_release-9.7.3.dist-info/METADATA,sha256=oTsw-ui914QMQuaBDHR-XOaqFBOL3l3zJ9wajIjGCtY,5404
52
- python_semantic_release-9.7.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
53
- python_semantic_release-9.7.3.dist-info/entry_points.txt,sha256=qojUBx_Ywf4u7KgP6NWlDaBDymZVUGQ7m5wF2duC2F0,95
54
- python_semantic_release-9.7.3.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
55
- python_semantic_release-9.7.3.dist-info/RECORD,,
49
+ python_semantic_release-9.8.1.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
50
+ python_semantic_release-9.8.1.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
51
+ python_semantic_release-9.8.1.dist-info/METADATA,sha256=umroH8O83cP7YCxNjNYcTkzyx2I2ASj3l36gqSZMkVY,5404
52
+ python_semantic_release-9.8.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
53
+ python_semantic_release-9.8.1.dist-info/entry_points.txt,sha256=qojUBx_Ywf4u7KgP6NWlDaBDymZVUGQ7m5wF2duC2F0,95
54
+ python_semantic_release-9.8.1.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
55
+ python_semantic_release-9.8.1.dist-info/RECORD,,
@@ -24,7 +24,7 @@ from semantic_release.version import (
24
24
  tags_and_versions,
25
25
  )
26
26
 
27
- __version__ = "9.7.3"
27
+ __version__ = "9.8.1"
28
28
 
29
29
 
30
30
  def setup_hook(argv: list[str]) -> None:
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import logging
4
4
  import os
5
5
  import subprocess
6
+ import sys
6
7
  from contextlib import nullcontext
7
8
  from datetime import datetime
8
9
  from typing import TYPE_CHECKING
@@ -156,6 +157,40 @@ def shell(
156
157
  )
157
158
 
158
159
 
160
+ def is_windows() -> bool:
161
+ return sys.platform == "win32"
162
+
163
+
164
+ def get_windows_env() -> Mapping[str, str | None]:
165
+ return {
166
+ environment_variable: os.getenv(environment_variable, None)
167
+ for environment_variable in (
168
+ "ALLUSERSAPPDATA",
169
+ "ALLUSERSPROFILE",
170
+ "APPDATA",
171
+ "COMMONPROGRAMFILES",
172
+ "COMMONPROGRAMFILES(x86)",
173
+ "DEFAULTUSERPROFILE",
174
+ "HOMEPATH",
175
+ "PATHEXT",
176
+ "PROFILESFOLDER",
177
+ "PROGRAMFILES",
178
+ "PROGRAMFILES(x86)",
179
+ "SYSTEM",
180
+ "SYSTEM16",
181
+ "SYSTEM32",
182
+ "SYSTEMDRIVE",
183
+ "SYSTEMPROFILE",
184
+ "SYSTEMROOT",
185
+ "TEMP",
186
+ "TMP",
187
+ "USERPROFILE",
188
+ "USERSID",
189
+ "WINDIR",
190
+ )
191
+ }
192
+
193
+
159
194
  @click.command(
160
195
  short_help="Detect and apply a new version",
161
196
  context_settings={
@@ -330,6 +365,7 @@ def version( # noqa: C901
330
365
  commit_author = runtime.commit_author
331
366
  commit_message = runtime.commit_message
332
367
  major_on_zero = runtime.major_on_zero
368
+ no_verify = runtime.no_git_verify
333
369
  build_command = runtime.build_command
334
370
  opts = runtime.global_cli_options
335
371
  gha_output = VersionGitHubActionsOutput()
@@ -521,6 +557,8 @@ def version( # noqa: C901
521
557
  "PATH": os.getenv("PATH", ""),
522
558
  "HOME": os.getenv("HOME", None),
523
559
  "VIRTUAL_ENV": os.getenv("VIRTUAL_ENV", None),
560
+ # Windows environment variables
561
+ **(get_windows_env() if is_windows() else {}),
524
562
  # affects build decisions
525
563
  "CI": os.getenv("CI", None),
526
564
  # Identifies which CI environment
@@ -613,6 +651,7 @@ def version( # noqa: C901
613
651
  )
614
652
 
615
653
  command += f"git commit -m '{indented_commit_message}'"
654
+ command += "--no-verify" if no_verify else ""
616
655
 
617
656
  noop_report(
618
657
  indented(
@@ -628,6 +667,7 @@ def version( # noqa: C901
628
667
  repo.git.commit(
629
668
  m=commit_message.format(version=new_version),
630
669
  date=int(commit_date.timestamp()),
670
+ no_verify=no_verify,
631
671
  )
632
672
 
633
673
  # Run the tagging after potentially creating a new HEAD commit.
@@ -225,6 +225,7 @@ class RawConfig(BaseModel):
225
225
  major_on_zero: bool = True
226
226
  allow_zero_version: bool = True
227
227
  remote: RemoteConfig = RemoteConfig()
228
+ no_git_verify: bool = False
228
229
  tag_format: str = "v{version}"
229
230
  publish: PublishConfig = PublishConfig()
230
231
  version_toml: Optional[Tuple[str, ...]] = None
@@ -347,6 +348,7 @@ class RuntimeContext:
347
348
  major_on_zero: bool
348
349
  allow_zero_version: bool
349
350
  prerelease: bool
351
+ no_git_verify: bool
350
352
  assets: List[str]
351
353
  commit_author: Actor
352
354
  commit_message: str
@@ -596,6 +598,7 @@ class RuntimeContext:
596
598
  upload_to_vcs_release=raw.publish.upload_to_vcs_release,
597
599
  global_cli_options=global_cli_options,
598
600
  masker=masker,
601
+ no_git_verify=raw.no_git_verify,
599
602
  )
600
603
  # credential masker
601
604
  self.apply_log_masking(self.masker)
@@ -304,7 +304,7 @@ class Github(RemoteHvcsBase):
304
304
  """
305
305
  Edit a release with updated change notes
306
306
  https://docs.github.com/rest/reference/repos#update-a-release
307
- :param id: ID of release to update
307
+ :param release_id: ID of release to update
308
308
  :param release_notes: The release notes for this version
309
309
  :return: The ID of the release that was edited
310
310
  """
@@ -329,8 +329,9 @@ class Github(RemoteHvcsBase):
329
329
  ) -> int:
330
330
  """
331
331
  Post release changelog
332
- :param version: The version number
332
+ :param tag: The version number
333
333
  :param release_notes: The release notes for this version
334
+ :param prerelease: Whether or not this release should be created as a prerelease
334
335
  :return: The status of the request
335
336
  """
336
337
  log.info("Creating release for %s", tag)
@@ -427,8 +428,8 @@ class Github(RemoteHvcsBase):
427
428
  def upload_dists(self, tag: str, dist_glob: str) -> int:
428
429
  """
429
430
  Upload distributions to a release
430
- :param version: Version to upload for
431
- :param path: Path to the dist directory
431
+ :param tag: Version to upload for
432
+ :param dist_glob: Path to the dist directory
432
433
  :return: The number of distributions successfully uploaded
433
434
  """
434
435
  # Find the release corresponding to this version
@@ -9,14 +9,21 @@ from pathlib import PurePosixPath
9
9
  from typing import TYPE_CHECKING
10
10
 
11
11
  import gitlab
12
+ import gitlab.exceptions
13
+ import gitlab.v4
14
+ import gitlab.v4.objects
12
15
  from urllib3.util.url import Url, parse_url
13
16
 
17
+ from semantic_release.errors import UnexpectedResponse
14
18
  from semantic_release.helpers import logged_function
15
19
  from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase
20
+ from semantic_release.hvcs.util import suppress_not_found
16
21
 
17
22
  if TYPE_CHECKING:
18
23
  from typing import Any, Callable
19
24
 
25
+ from gitlab.v4.objects import Project as GitLabProject
26
+
20
27
 
21
28
  log = logging.getLogger(__name__)
22
29
 
@@ -45,6 +52,8 @@ class Gitlab(RemoteHvcsBase):
45
52
  ) -> None:
46
53
  super().__init__(remote_url)
47
54
  self.token = token
55
+ self.project_namespace = f"{self.owner}/{self.repo_name}"
56
+ self._project: GitLabProject | None = None
48
57
 
49
58
  domain_url = self._normalize_url(
50
59
  hvcs_domain
@@ -63,8 +72,14 @@ class Gitlab(RemoteHvcsBase):
63
72
  ).url.rstrip("/")
64
73
  )
65
74
 
66
- self._client = gitlab.Gitlab(self.hvcs_domain.url)
67
- self._api_url = parse_url(self._client.url)
75
+ self._client = gitlab.Gitlab(self.hvcs_domain.url, private_token=self.token)
76
+ self._api_url = parse_url(self._client.api_url)
77
+
78
+ @property
79
+ def project(self) -> GitLabProject:
80
+ if self._project is None:
81
+ self._project = self._client.projects.get(self.project_namespace)
82
+ return self._project
68
83
 
69
84
  @lru_cache(maxsize=1)
70
85
  def _get_repository_owner_and_name(self) -> tuple[str, str]:
@@ -87,61 +102,146 @@ class Gitlab(RemoteHvcsBase):
87
102
  assets: list[str] | None = None, # noqa: ARG002
88
103
  ) -> str:
89
104
  """
90
- Post release changelog
91
- :param tag: Tag to create release for
92
- :param release_notes: The release notes for this version
93
- :param prerelease: This parameter has no effect
94
- :return: The tag of the release
105
+ Create a release in a remote VCS, adding any release notes and assets to it
106
+
107
+ Arguments:
108
+ ---------
109
+ tag(str): The tag to create the release for
110
+ release_notes(str): The changelog description for this version only
111
+ prerelease(bool): This parameter has no effect in GitLab
112
+ assets(list[str]): A list of paths to files to upload as assets (TODO: not implemented)
113
+
114
+ Returns:
115
+ -------
116
+ str: The tag of the release
117
+
118
+ Raises:
119
+ ------
120
+ GitlabAuthenticationError: If authentication is not correct
121
+ GitlabCreateError: If the server cannot perform the request
122
+
95
123
  """
96
- client = gitlab.Gitlab(self.hvcs_domain.url, private_token=self.token)
97
- client.auth()
98
124
  log.info("Creating release for %s", tag)
99
125
  # ref: https://docs.gitlab.com/ee/api/releases/index.html#create-a-release
100
- client.projects.get(self.owner + "/" + self.repo_name).releases.create(
126
+ self.project.releases.create(
101
127
  {
102
- "name": "Release " + tag,
128
+ "name": tag,
103
129
  "tag_name": tag,
130
+ "tag_message": tag,
104
131
  "description": release_notes,
105
132
  }
106
133
  )
107
134
  log.info("Successfully created release for %s", tag)
108
135
  return tag
109
136
 
110
- # TODO: make str types accepted here
137
+ @logged_function(log)
138
+ @suppress_not_found
139
+ def get_release_by_tag(self, tag: str) -> gitlab.v4.objects.ProjectRelease | None:
140
+ """
141
+ Get a release by its tag name
142
+
143
+ Arguments:
144
+ ---------
145
+ tag(str): The tag name to get the release for
146
+
147
+ Returns:
148
+ -------
149
+ gitlab.v4.objects.ProjectRelease | None: The release object or None if not found
150
+
151
+ Raises:
152
+ ------
153
+ gitlab.exceptions.GitlabAuthenticationError: If the user is not authenticated
154
+
155
+ """
156
+ try:
157
+ return self.project.releases.get(tag)
158
+ except gitlab.exceptions.GitlabGetError:
159
+ log.debug("Release %s not found", tag)
160
+ return None
161
+ except KeyError as err:
162
+ raise UnexpectedResponse("JSON response is missing commit.id") from err
163
+
111
164
  @logged_function(log)
112
165
  def edit_release_notes( # type: ignore[override]
113
166
  self,
114
- release_id: str,
167
+ release: gitlab.v4.objects.ProjectRelease,
115
168
  release_notes: str,
116
169
  ) -> str:
117
- client = gitlab.Gitlab(self.hvcs_domain.url, private_token=self.token)
118
- client.auth()
119
- log.info("Updating release %s", release_id)
170
+ """
171
+ Update the release notes for a given release
120
172
 
121
- client.projects.get(self.owner + "/" + self.repo_name).releases.update(
122
- release_id,
123
- {
124
- "description": release_notes,
125
- },
173
+ Arguments:
174
+ ---------
175
+ release(gitlab.v4.objects.ProjectRelease): The release object to update
176
+ release_notes(str): The new release notes
177
+
178
+ Returns:
179
+ -------
180
+ str: The release id
181
+
182
+ Raises:
183
+ ------
184
+ GitlabAuthenticationError: If authentication is not correct
185
+ GitlabUpdateError: If the server cannot perform the request
186
+
187
+ """
188
+ log.info(
189
+ "Updating release %s [%s]",
190
+ release.name,
191
+ release.attributes.get("commit", {}).get("id"),
126
192
  )
127
- return release_id
193
+ release.description = release_notes
194
+ release.save()
195
+ return str(release.get_id())
128
196
 
129
197
  @logged_function(log)
130
198
  def create_or_update_release(
131
199
  self, tag: str, release_notes: str, prerelease: bool = False
132
200
  ) -> str:
201
+ """
202
+ Create or update a release for the given tag in a remote VCS
203
+
204
+ Arguments:
205
+ ---------
206
+ tag(str): The tag to create or update the release for
207
+ release_notes(str): The changelog description for this version only
208
+ prerelease(bool): This parameter has no effect in GitLab
209
+
210
+ Returns:
211
+ -------
212
+ str: The release id
213
+
214
+ Raises:
215
+ ------
216
+ ValueError: If the release could not be created or updated
217
+ gitlab.exceptions.GitlabAuthenticationError: If the user is not authenticated
218
+ GitlabUpdateError: If the server cannot perform the request
219
+
220
+ """
133
221
  try:
134
222
  return self.create_release(
135
223
  tag=tag, release_notes=release_notes, prerelease=prerelease
136
224
  )
137
225
  except gitlab.GitlabCreateError:
138
226
  log.info(
139
- "Release %s could not be created for project %s/%s",
227
+ "New release %s could not be created for project %s",
140
228
  tag,
141
- self.owner,
142
- self.repo_name,
229
+ self.project_namespace,
230
+ )
231
+
232
+ if (release_obj := self.get_release_by_tag(tag)) is None:
233
+ raise ValueError(
234
+ f"release for tag {tag} could not be found, and could not be created"
143
235
  )
144
- return self.edit_release_notes(release_id=tag, release_notes=release_notes)
236
+
237
+ log.debug(
238
+ "Found existing release commit %s, updating", release_obj.commit.get("id")
239
+ )
240
+ # If this errors we let it die
241
+ return self.edit_release_notes(
242
+ release=release_obj,
243
+ release_notes=release_notes,
244
+ )
145
245
 
146
246
  def remote_url(self, use_token: bool = True) -> str:
147
247
  """Get the remote url including the token for authentication if requested"""
@@ -150,7 +250,7 @@ class Gitlab(RemoteHvcsBase):
150
250
 
151
251
  return self.create_server_url(
152
252
  auth=f"gitlab-ci-token:{self.token}",
153
- path=f"{self.owner}/{self.repo_name}.git",
253
+ path=f"{self.project_namespace}.git",
154
254
  )
155
255
 
156
256
  def compare_url(self, from_rev: str, to_rev: str) -> str: