python-semantic-release 9.4.0__py3-none-any.whl → 9.4.2__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.4.0
3
+ Version: 9.4.2
4
4
  Summary: Automatic Semantic Versioning for Python projects
5
5
  Author-email: Rolf Erik Lekang <me@rolflekang.com>
6
6
  License: MIT
@@ -30,19 +30,19 @@ Requires-Dist: tomlkit ~=0.11
30
30
  Requires-Dist: dotty-dict ~=1.3
31
31
  Requires-Dist: importlib-resources ~=6.0
32
32
  Requires-Dist: pydantic ~=2.0
33
- Requires-Dist: rich ~=12.5
33
+ Requires-Dist: rich ~=13.0
34
34
  Requires-Dist: shellingham ~=1.5
35
35
  Provides-Extra: dev
36
36
  Requires-Dist: pre-commit ~=3.5 ; extra == 'dev'
37
37
  Requires-Dist: tox ~=4.11 ; extra == 'dev'
38
- Requires-Dist: ruff ==0.3.4 ; extra == 'dev'
38
+ Requires-Dist: ruff ==0.3.5 ; extra == 'dev'
39
39
  Provides-Extra: docs
40
40
  Requires-Dist: Sphinx ~=6.0 ; extra == 'docs'
41
41
  Requires-Dist: sphinxcontrib-apidoc ==0.5.0 ; extra == 'docs'
42
42
  Requires-Dist: sphinx-autobuild ==2024.2.4 ; extra == 'docs'
43
- Requires-Dist: furo ~=2023.3 ; extra == 'docs'
43
+ Requires-Dist: furo ~=2024.1 ; extra == 'docs'
44
44
  Provides-Extra: mypy
45
- Requires-Dist: mypy ==1.8.0 ; extra == 'mypy'
45
+ Requires-Dist: mypy ==1.9.0 ; extra == 'mypy'
46
46
  Requires-Dist: types-requests ~=2.31.0 ; extra == 'mypy'
47
47
  Provides-Extra: test
48
48
  Requires-Dist: coverage[toml] ~=7.0 ; extra == 'test'
@@ -1,8 +1,8 @@
1
- semantic_release/__init__.py,sha256=WXo3kBqr41p8SiuEqS2v7ET5zAKCTK4d5KJ7Zc0Sgs0,870
1
+ semantic_release/__init__.py,sha256=PnqssXtNohSJYnHQls9mQ0VheM9Lx9YBzjCAXqFlefs,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
5
- semantic_release/errors.py,sha256=8cNiMhgUTNZC0CecokLyl9YWxREHEh9otm1cRs9kkbU,1001
5
+ semantic_release/errors.py,sha256=070p5fRe4l2BYv1zbTZjnM5YwRcJmi9BVa8TdMFzvf0,1159
6
6
  semantic_release/helpers.py,sha256=d1jOX0SNyqPc_3wr14xR25FfpqhMd4Ev7MNBOWlScc0,5581
7
7
  semantic_release/changelog/__init__.py,sha256=Bg6Xe5Vt32rWoMscW-hd4sUwiZqzWmsg4CD1EhMesMY,262
8
8
  semantic_release/changelog/context.py,sha256=ItSz5Bon2mcJkLxyQZzBTukN0zEdcaVQ4iXvz3jGCLc,986
@@ -10,9 +10,9 @@ 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=lBkTlc2x4PkCj7aGJn6DahsF_LH7Fsy-5OiTW61wRzA,16070
13
+ semantic_release/cli/config.py,sha256=9NhaxZ1F1ux-8Qu7_KeE7kLH3eCsllkPV4Hy5YI1tv4,17574
14
14
  semantic_release/cli/const.py,sha256=S8oNNpSaKdZxcAs7PAKEjQHic0czoJBGd7XHo57FOzw,39
15
- semantic_release/cli/github_actions_output.py,sha256=stGWcuMtelY0qM2E_-IfofP6UvsQ6czsO7qpMfnRJI4,2102
15
+ semantic_release/cli/github_actions_output.py,sha256=Wy4jEIpSFgoMyJqq4FD7scIpgovivutFLZ7Fs4y2CUY,2110
16
16
  semantic_release/cli/masking_filter.py,sha256=DxqjiJyABlzwwwZ1r8JGQpb6QrF00StJFm0-2-s5Fv0,3071
17
17
  semantic_release/cli/util.py,sha256=FyXaBkeL7nXKjy3X9rQLEwvn7p46xPekp2V8Z-5MVrk,3755
18
18
  semantic_release/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -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=_pz7rKx65tCH7J2uyWiBghQ61MmmIbxhF9bE4AkE0N8,1703
24
- semantic_release/cli/commands/version.py,sha256=jz3ao9AV0YeMAwZULSnnanl6OL-NWIc5rnr9E7BrZL0,22111
24
+ semantic_release/cli/commands/version.py,sha256=H_I4AmZzBQdMzYejRKgCyvMtx55omzpSHJFWMz-uQRc,23015
25
25
  semantic_release/commit_parser/__init__.py,sha256=cv5HFBdw7OJd4Laj4Ex8ZZ5Tml8GwXgQcXW6Pasr2Ao,615
26
26
  semantic_release/commit_parser/_base.py,sha256=aDS0dhFosT5i11tA5Ez8BTeikk6jwXjSEpJnJC5D2cs,2609
27
27
  semantic_release/commit_parser/angular.py,sha256=bS7Wrzpg_6zFil5Y4GIZkeGDPhvWQdTlFdWTtGVcpp0,4393
@@ -33,11 +33,11 @@ semantic_release/commit_parser/util.py,sha256=vLcVDErZrExM55jMffos0hyMbNVQoJ-Pee
33
33
  semantic_release/data/templates/CHANGELOG.md.j2,sha256=GB3YJwgVrhlcb-g5ESdEh_Gsysb_Texxv-Ip2LjulC0,1066
34
34
  semantic_release/data/templates/release_notes.md.j2,sha256=27uIHgG6iYmqdMaoWFP5LJRZcrO-3paqFzHHHsGpXXE,465
35
35
  semantic_release/hvcs/__init__.py,sha256=ulesZslSG4Lu8Iz3YzOrPXpdgvgbADq_MV17B-bSYQA,379
36
- semantic_release/hvcs/_base.py,sha256=VnsEz01W9t3DQ2QHGnsbuAB9jYfZT3hIXRRsCUnhS0g,5635
37
- semantic_release/hvcs/bitbucket.py,sha256=pGZksLht66zLJXbPIgRz4hntsa-F1A02xl4JNRCXE0o,4626
38
- semantic_release/hvcs/gitea.py,sha256=FbS5AQTyjBTGk0RLR0Lky3TBoHGtLgjr83N1uje5bn4,9146
39
- semantic_release/hvcs/github.py,sha256=L29AaUmmtdJTNDJqD6ro3YVcDNr8OMwqTx5ofujOZz8,10710
40
- semantic_release/hvcs/gitlab.py,sha256=qSvNdQK40QM20-HDh3eIO6Rz8Lo3PEND_4kluugVx4w,6154
36
+ semantic_release/hvcs/_base.py,sha256=qjjlU3rI-sOhIY_FfXsZ9OQGthNYxgzpL4PhlGSXEuA,5232
37
+ semantic_release/hvcs/bitbucket.py,sha256=6vGOcRG7WexxG8SzLcWDIzp7zNpB0ruDLUTQ9GE2jGM,11004
38
+ semantic_release/hvcs/gitea.py,sha256=OTThBTZ93xUP9am7tNIE0UVchhJwG9ASvI6zuiG8oLE,11450
39
+ semantic_release/hvcs/github.py,sha256=qOEGFRYjhGhFfeL7l8adrAUBfi2yd8AUFlARjym383s,16297
40
+ semantic_release/hvcs/gitlab.py,sha256=qmowL22DCNOFa7RwmMTmNmno3BS0dyNYT9L36ujgZ8A,7409
41
41
  semantic_release/hvcs/token_auth.py,sha256=ZjT56-NIPB4OKIt1qwHCu1TavXnrWFIBl9ARlg56hgU,663
42
42
  semantic_release/hvcs/util.py,sha256=PNKXApgL2np9lpnKjAbo77toRgElLPXrbS1q7621waI,2886
43
43
  semantic_release/version/__init__.py,sha256=CLhtGQry9dLIij5XyRa9ZevxU_1p8tjMTSQ-K_GMpWM,270
@@ -45,10 +45,10 @@ semantic_release/version/algorithm.py,sha256=-9ulTy1z4u-jUvbc-FrvEdFL7Omc3O3HhaP
45
45
  semantic_release/version/declaration.py,sha256=f6Ld7hIhrqvDrRBapJHr-KDimuyo-4IG8009Zu9BIgU,7357
46
46
  semantic_release/version/translator.py,sha256=P1noIsVBn8u6zNOFjG0xKYOWapxqf_PHSMvMeLJ9kXg,3050
47
47
  semantic_release/version/version.py,sha256=6PCtSbLP88U1daoxnCwHc--YguZo4waGNLqJ5JfeczE,14175
48
- python_semantic_release-9.4.0.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
49
- python_semantic_release-9.4.0.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
50
- python_semantic_release-9.4.0.dist-info/METADATA,sha256=GU4p2nEh2IoCvk4nUGldohgzWQyMTCxfWAD8eAXbyyw,5335
51
- python_semantic_release-9.4.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
52
- python_semantic_release-9.4.0.dist-info/entry_points.txt,sha256=qojUBx_Ywf4u7KgP6NWlDaBDymZVUGQ7m5wF2duC2F0,95
53
- python_semantic_release-9.4.0.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
54
- python_semantic_release-9.4.0.dist-info/RECORD,,
48
+ python_semantic_release-9.4.2.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
49
+ python_semantic_release-9.4.2.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
50
+ python_semantic_release-9.4.2.dist-info/METADATA,sha256=9MVGGhT1GY66GdrYc8oVvYaGV2gUS4skpG-DuPd5XIE,5335
51
+ python_semantic_release-9.4.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
52
+ python_semantic_release-9.4.2.dist-info/entry_points.txt,sha256=qojUBx_Ywf4u7KgP6NWlDaBDymZVUGQ7m5wF2duC2F0,95
53
+ python_semantic_release-9.4.2.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
54
+ python_semantic_release-9.4.2.dist-info/RECORD,,
@@ -24,7 +24,7 @@ from semantic_release.version import (
24
24
  tags_and_versions,
25
25
  )
26
26
 
27
- __version__ = "9.4.0"
27
+ __version__ = "9.4.2"
28
28
 
29
29
 
30
30
  def setup_hook(argv: list[str]) -> None:
@@ -11,6 +11,7 @@ import click
11
11
  import shellingham # type: ignore[import]
12
12
  from click_option_group import MutuallyExclusiveOptionGroup, optgroup
13
13
  from git.exc import GitCommandError
14
+ from requests import HTTPError
14
15
 
15
16
  from semantic_release.changelog import ReleaseHistory, environment, recursive_render
16
17
  from semantic_release.changelog.context import make_changelog_context
@@ -23,6 +24,7 @@ from semantic_release.cli.github_actions_output import VersionGitHubActionsOutpu
23
24
  from semantic_release.cli.util import indented, noop_report, rprint
24
25
  from semantic_release.const import DEFAULT_SHELL, DEFAULT_VERSION
25
26
  from semantic_release.enums import LevelBump
27
+ from semantic_release.errors import UnexpectedResponse
26
28
  from semantic_release.version import Version, next_version, tags_and_versions
27
29
 
28
30
  log = logging.getLogger(__name__)
@@ -633,6 +635,21 @@ def version( # noqa: C901
633
635
  release_notes=release_notes,
634
636
  prerelease=new_version.is_prerelease,
635
637
  )
638
+ except HTTPError as err:
639
+ log.exception(err)
640
+ ctx.fail(str.join("\n", [str(err), "Failed to create release!"]))
641
+ except UnexpectedResponse as err:
642
+ log.exception(err)
643
+ ctx.fail(
644
+ str.join(
645
+ "\n",
646
+ [
647
+ str(err),
648
+ "Unexpected response from remote VCS!",
649
+ "Before re-running, make sure to clean up any artifacts on the hvcs that may have already been created.",
650
+ ],
651
+ )
652
+ )
636
653
  except Exception as e:
637
654
  log.exception(e)
638
655
  ctx.fail(str(e))
@@ -641,6 +658,9 @@ def version( # noqa: C901
641
658
  log.info("Uploading asset %s", asset)
642
659
  try:
643
660
  hvcs_client.upload_asset(release_id, asset)
661
+ except HTTPError as err:
662
+ log.exception(err)
663
+ ctx.fail(str.join("\n", [str(err), "Failed to upload asset!"]))
644
664
  except Exception as e:
645
665
  log.exception(e)
646
666
  ctx.fail(str(e))
@@ -12,10 +12,18 @@ from typing import Any, ClassVar, Dict, List, Literal, Optional, Tuple, Type, Un
12
12
  from git import Actor, InvalidGitRepositoryError
13
13
  from git.repo.base import Repo
14
14
  from jinja2 import Environment
15
- from pydantic import BaseModel, Field, RootModel, ValidationError, model_validator
15
+ from pydantic import (
16
+ BaseModel,
17
+ Field,
18
+ RootModel,
19
+ ValidationError,
20
+ field_validator,
21
+ model_validator,
22
+ )
16
23
 
17
- # For Python 3.8, 3.9, 3.10 compatibility
24
+ # typing_extensions is for Python 3.8, 3.9, 3.10 compatibility
18
25
  from typing_extensions import Annotated, Self
26
+ from urllib3.util.url import parse_url
19
27
 
20
28
  from semantic_release import hvcs
21
29
  from semantic_release.changelog import environment
@@ -111,12 +119,23 @@ class BranchConfig(BaseModel):
111
119
 
112
120
  class RemoteConfig(BaseModel):
113
121
  name: str = "origin"
114
- token: MaybeFromEnv = ""
115
- url: Optional[MaybeFromEnv] = None
122
+ token: Optional[str] = None
123
+ url: Optional[str] = None
116
124
  type: HvcsClient = HvcsClient.GITHUB
117
125
  domain: Optional[str] = None
118
126
  api_domain: Optional[str] = None
119
127
  ignore_token_for_push: bool = False
128
+ insecure: bool = False
129
+
130
+ @field_validator("url", "domain", "api_domain", "token", mode="before")
131
+ @classmethod
132
+ def resolve_env_vars(cls, val: Any) -> str | None:
133
+ ret_val = (
134
+ val
135
+ if not isinstance(val, dict)
136
+ else (EnvConfigVar.model_validate(val).getvalue())
137
+ )
138
+ return ret_val or None
120
139
 
121
140
  @model_validator(mode="after")
122
141
  def set_default_token(self) -> Self:
@@ -124,9 +143,51 @@ class RemoteConfig(BaseModel):
124
143
  if not self.token and self.type in _known_hvcs:
125
144
  default_token_name = _known_hvcs[self.type].DEFAULT_ENV_TOKEN_NAME
126
145
  if default_token_name:
127
- self.token = EnvConfigVar(env=default_token_name)
146
+ env_token = EnvConfigVar(env=default_token_name).getvalue()
147
+ if env_token:
148
+ self.token = env_token
128
149
  return self
129
150
 
151
+ @model_validator(mode="after")
152
+ def check_url_scheme(self) -> Self:
153
+ if self.url and isinstance(self.url, str):
154
+ self.check_insecure_flag(self.url, "url")
155
+
156
+ if self.domain and isinstance(self.domain, str):
157
+ self.check_insecure_flag(self.domain, "domain")
158
+
159
+ if self.api_domain and isinstance(self.api_domain, str):
160
+ self.check_insecure_flag(self.api_domain, "api_domain")
161
+
162
+ return self
163
+
164
+ def check_insecure_flag(self, url_str: str, field_name: str) -> None:
165
+ if not url_str:
166
+ return
167
+
168
+ scheme = parse_url(url_str).scheme
169
+ if scheme == "http" and not self.insecure:
170
+ raise ValueError(
171
+ str.join(
172
+ "\n",
173
+ [
174
+ "Insecure 'HTTP' URL detected and disabled by default.",
175
+ "Set the 'insecure' flag to 'True' to enable insecure connections.",
176
+ ],
177
+ )
178
+ )
179
+
180
+ if scheme == "https" and self.insecure:
181
+ log.warning(
182
+ str.join(
183
+ "\n",
184
+ [
185
+ f"'{field_name}' starts with 'https://' but the 'insecure' flag is set.",
186
+ "This flag is only necessary for 'http://' URLs.",
187
+ ],
188
+ )
189
+ )
190
+
130
191
 
131
192
  class PublishConfig(BaseModel):
132
193
  dist_glob_patterns: Tuple[str, ...] = ("dist/*",)
@@ -370,35 +431,24 @@ class RuntimeContext:
370
431
 
371
432
  version_declarations.append(pd)
372
433
 
373
- # hvcs_client
374
- hvcs_client_cls = _known_hvcs[raw.remote.type]
375
- raw_remote_url = raw.remote.url
376
- resolved_remote_url = cls.resolve_from_env(raw_remote_url)
377
- remote_url = (
378
- resolved_remote_url
379
- if resolved_remote_url is not None
380
- else repo.remote(raw.remote.name).url
381
- )
382
-
383
- token = cls.resolve_from_env(raw.remote.token)
384
- if (
385
- isinstance(raw.remote.token, EnvConfigVar)
386
- and not raw.remote.ignore_token_for_push
387
- and not token
388
- ):
389
- log.warning(
390
- "the token for the remote VCS is configured as stored in the %s "
391
- "environment variable, but it is empty",
392
- raw.remote.token.env,
393
- )
394
- elif not token:
434
+ # Provide warnings if the token is missing
435
+ if not raw.remote.token:
395
436
  log.debug("hvcs token is not set")
396
437
 
438
+ if not raw.remote.ignore_token_for_push:
439
+ log.warning("Token value is missing!")
440
+
441
+ # retrieve remote url
442
+ remote_url = raw.remote.url or repo.remote(raw.remote.name).url
443
+
444
+ # hvcs_client
445
+ hvcs_client_cls = _known_hvcs[raw.remote.type]
397
446
  hvcs_client = hvcs_client_cls(
398
447
  remote_url=remote_url,
399
- hvcs_domain=cls.resolve_from_env(raw.remote.domain),
400
- hvcs_api_domain=cls.resolve_from_env(raw.remote.api_domain),
401
- token=token,
448
+ hvcs_domain=raw.remote.domain,
449
+ hvcs_api_domain=raw.remote.api_domain,
450
+ token=raw.remote.token,
451
+ allow_insecure=raw.remote.insecure,
402
452
  )
403
453
 
404
454
  # changelog_file
@@ -62,7 +62,8 @@ class VersionGitHubActionsOutput:
62
62
  "version": str(self.version),
63
63
  "tag": self.tag,
64
64
  }
65
- return "\n".join(f"{key}={value!s}" for key, value in outputs.items())
65
+
66
+ return str.join("", [f"{key}={value!s}\n" for key, value in outputs.items()])
66
67
 
67
68
  def write_if_possible(self, filename: str | None = None) -> None:
68
69
  output_file = filename or os.getenv(self.OUTPUT_ENV_VAR)
@@ -38,3 +38,10 @@ class MissingMergeBaseError(SemanticReleaseBaseError):
38
38
  Raised when the merge base cannot be found with the current history. Generally
39
39
  because of a shallow git clone.
40
40
  """
41
+
42
+
43
+ class UnexpectedResponse(Exception):
44
+ """
45
+ Raised when an HTTP response cannot be parsed properly or the expected structure
46
+ is not found.
47
+ """
@@ -5,11 +5,15 @@ from __future__ import annotations
5
5
  import logging
6
6
  import warnings
7
7
  from functools import lru_cache
8
+ from typing import TYPE_CHECKING
8
9
 
9
10
  from semantic_release.helpers import parse_git_url
10
- from semantic_release.hvcs.token_auth import TokenAuth
11
- from semantic_release.hvcs.util import build_requests_session
12
11
 
12
+ if TYPE_CHECKING:
13
+ from typing import Any
14
+
15
+
16
+ # Globals
13
17
  logger = logging.getLogger(__name__)
14
18
 
15
19
 
@@ -32,23 +36,13 @@ class HvcsBase:
32
36
  (i.e. without raising an exception) return _not_supported, and can be overridden
33
37
  to provide an implementation in subclasses. This is more straightforward than
34
38
  checking for NotImplemented around every method call.
39
+
35
40
  """
36
41
 
37
42
  DEFAULT_ENV_TOKEN_NAME = "HVCS_TOKEN" # noqa: S105
38
43
 
39
- def __init__(
40
- self,
41
- remote_url: str,
42
- hvcs_domain: str | None = None,
43
- hvcs_api_domain: str | None = None,
44
- token: str | None = None,
45
- ) -> None:
46
- self.hvcs_domain = hvcs_domain
47
- self.hvcs_api_domain = hvcs_api_domain
48
- self.token = token
49
- auth = None if not self.token else TokenAuth(self.token)
44
+ def __init__(self, remote_url: str, *args: Any, **kwargs: Any) -> None:
50
45
  self._remote_url = remote_url
51
- self.session = build_requests_session(auth=auth)
52
46
 
53
47
  @lru_cache(maxsize=1)
54
48
  def _get_repository_owner_and_name(self) -> tuple[str, str]:
@@ -72,10 +66,13 @@ class HvcsBase:
72
66
  def compare_url(self, from_rev: str, to_rev: str) -> str:
73
67
  """
74
68
  Get the comparison link between two version tags.
69
+
75
70
  :param from_rev: The older version to compare. Can be a commit sha, tag or
76
- branch name.
71
+ branch name.
72
+
77
73
  :param to_rev: The newer version to compare. Can be a commit sha, tag or
78
- branch name.
74
+ branch name.
75
+
79
76
  :return: Link to view a comparison between the two versions.
80
77
  """
81
78
  _not_supported(self, "compare_url")