python-semantic-release 10.5.3__py3-none-any.whl → 10.6.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.
- {python_semantic_release-10.5.3.dist-info → python_semantic_release-10.6.0.dist-info}/METADATA +2 -2
- {python_semantic_release-10.5.3.dist-info → python_semantic_release-10.6.0.dist-info}/RECORD +16 -15
- {python_semantic_release-10.5.3.dist-info → python_semantic_release-10.6.0.dist-info}/WHEEL +1 -1
- semantic_release/cli/commands/generate_config.py +27 -8
- semantic_release/cli/commands/publish.py +11 -6
- semantic_release/cli/config.py +16 -5
- semantic_release/cli/util.py +1 -1
- semantic_release/commit_parser/emoji.py +6 -1
- semantic_release/helpers.py +12 -2
- semantic_release/hvcs/github.py +12 -1
- semantic_release/version/declaration.py +3 -1
- semantic_release/version/declarations/file.py +145 -0
- semantic_release/version/declarations/i_version_replacer.py +6 -0
- {python_semantic_release-10.5.3.dist-info → python_semantic_release-10.6.0.dist-info}/entry_points.txt +0 -0
- {python_semantic_release-10.5.3.dist-info → python_semantic_release-10.6.0.dist-info}/licenses/LICENSE +0 -0
- {python_semantic_release-10.5.3.dist-info → python_semantic_release-10.6.0.dist-info}/top_level.txt +0 -0
{python_semantic_release-10.5.3.dist-info → python_semantic_release-10.6.0.dist-info}/METADATA
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-semantic-release
|
|
3
|
-
Version: 10.
|
|
3
|
+
Version: 10.6.0
|
|
4
4
|
Summary: Automatic Semantic Versioning for Python projects
|
|
5
5
|
Author-email: Rolf Erik Lekang <me@rolflekang.com>, codejedi365 <codejedi365@gmail.com>
|
|
6
6
|
License: MIT
|
|
7
|
-
Project-URL: changelog, https://
|
|
7
|
+
Project-URL: changelog, https://python-semantic-release.readthedocs.io/en/stable/misc/psr_changelog.html
|
|
8
8
|
Project-URL: documentation, https://python-semantic-release.readthedocs.io
|
|
9
9
|
Project-URL: homepage, https://python-semantic-release.readthedocs.io
|
|
10
10
|
Project-URL: issues, https://github.com/python-semantic-release/python-semantic-release/issues
|
{python_semantic_release-10.5.3.dist-info → python_semantic_release-10.6.0.dist-info}/RECORD
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
python_semantic_release-10.
|
|
1
|
+
python_semantic_release-10.6.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
|
|
@@ -6,7 +6,7 @@ semantic_release/enums.py,sha256=vrEw1UNRcNrFjPqOFnuUzfeoqKj0ChixVVlyk5fqbng,174
|
|
|
6
6
|
semantic_release/errors.py,sha256=FyocaqHbRhux-iNmCf9nI7awyUaGKjG9_5C_QDvhEas,3399
|
|
7
7
|
semantic_release/gitproject.py,sha256=18jsD8YFsaKKqt_l5a0ccygIE_sOqvqf9FoEkJjyCkk,18584
|
|
8
8
|
semantic_release/globals.py,sha256=IBhBbhZr2jx8dmpySnnu9m9jOGYu9Yu-vqHvAGQxgnw,464
|
|
9
|
-
semantic_release/helpers.py,sha256=
|
|
9
|
+
semantic_release/helpers.py,sha256=3munzjFYx_wxi97tRjCoP7EhcMtbgnO7G0ENlShCm8w,10334
|
|
10
10
|
semantic_release/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
semantic_release/changelog/__init__.py,sha256=Bg6Xe5Vt32rWoMscW-hd4sUwiZqzWmsg4CD1EhMesMY,262
|
|
12
12
|
semantic_release/changelog/context.py,sha256=234LLcB_uuGaQ5N_zTbcQYF27ZwYIBmicIWkhFs7-aQ,5993
|
|
@@ -15,21 +15,21 @@ semantic_release/changelog/template.py,sha256=eqOjtVfBbksgTK4CAZOajfkaAcKueJ-FhF
|
|
|
15
15
|
semantic_release/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
semantic_release/cli/changelog_writer.py,sha256=2jP5b0LK61Y2tb22GQSFFmwHwXd2WjbopgsMAmsQsfY,9284
|
|
17
17
|
semantic_release/cli/cli_context.py,sha256=Nop71LdVCJOeSUHgTXunMyK3xAu_QKQC2cRp1QBVkX0,4134
|
|
18
|
-
semantic_release/cli/config.py,sha256=
|
|
18
|
+
semantic_release/cli/config.py,sha256=jfG_N4rUo9pMpoC1eHNku_fK47eQXPrzRENu2g8CwWg,34297
|
|
19
19
|
semantic_release/cli/const.py,sha256=h7XE2D0D__TAZSrUUtVszwvzpkHTMOiQCf97XQNbEvA,163
|
|
20
20
|
semantic_release/cli/github_actions_output.py,sha256=yC0nsMvEFGACjDwB8DdmGKwNGI8aIIhDxRHrmcS7tzA,5410
|
|
21
21
|
semantic_release/cli/masking_filter.py,sha256=7t7XFL7Iy4QTvaYevZ-jnTkJBz15GoBw1vjW3hihe38,3159
|
|
22
|
-
semantic_release/cli/util.py,sha256=
|
|
22
|
+
semantic_release/cli/util.py,sha256=6TAbHgDZU6T-rm5yzg2TdGP_-ueHQhfbAkghP1q6we0,3750
|
|
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
|
-
semantic_release/cli/commands/generate_config.py,sha256=
|
|
25
|
+
semantic_release/cli/commands/generate_config.py,sha256=sKTO2V_XGRFc6M5KaTEPS4UOmqDuQOyTm64IYLr1VBc,2452
|
|
26
26
|
semantic_release/cli/commands/main.py,sha256=u1zhkkvKCZ2TtUqjzvdFTe5UZsvfws_pjqqo6CY0bBo,4351
|
|
27
|
-
semantic_release/cli/commands/publish.py,sha256=
|
|
27
|
+
semantic_release/cli/commands/publish.py,sha256=q9sNYCqlIjbT8RjJvHPVYzDdAjtnXe4_IdT0_h4S9MA,3047
|
|
28
28
|
semantic_release/cli/commands/version.py,sha256=8pn21AskKRMg1WJJXNNTZzlS1bNZefHKOPDRe-3NF5M,29502
|
|
29
29
|
semantic_release/commit_parser/__init__.py,sha256=uCC2YI-EDPUAa6TwWYJA8TS7baWE2bXrLEGcrhJKofI,1323
|
|
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
|
|
32
|
-
semantic_release/commit_parser/emoji.py,sha256=
|
|
32
|
+
semantic_release/commit_parser/emoji.py,sha256=IMRq0sUat-cU7aNIg38M0kAmZ376p7zuRy3XSOgsrOM,17826
|
|
33
33
|
semantic_release/commit_parser/scipy.py,sha256=gA9TfmrxGVvtv6kki6N_9abJx-_WlpBsSX2TBh0YgPw,19463
|
|
34
34
|
semantic_release/commit_parser/tag.py,sha256=bVO2XghM0G_eW2rG9Xc2q5TPsjtxr-xcHK5RpE1u_HM,3537
|
|
35
35
|
semantic_release/commit_parser/token.py,sha256=1_q8mJ4SRu7kNfa-Nxr8fEyuvCfjPgiPEitqSP1KR5g,7904
|
|
@@ -62,23 +62,24 @@ semantic_release/hvcs/__init__.py,sha256=JwoaLOF-12L-OBo_9-tOXXhdiHKeVungA9865to
|
|
|
62
62
|
semantic_release/hvcs/_base.py,sha256=gUCAbSHidhB6ou1oOP--wqEaVc4X2NPLjZQ5P_g3Bwc,2600
|
|
63
63
|
semantic_release/hvcs/bitbucket.py,sha256=dbWvqf79ACN42srhfVRtJ8m5WNI-_d7NE1cxZPwtCLs,10148
|
|
64
64
|
semantic_release/hvcs/gitea.py,sha256=PWK3URTuiWvx0SwSlSIGCbtVmD7XjaYo051fiTUYsJY,13463
|
|
65
|
-
semantic_release/hvcs/github.py,sha256=
|
|
65
|
+
semantic_release/hvcs/github.py,sha256=taaoLs1khLlpJp3IjyRiNBk_c_bQim8goYSistnL_Xk,21157
|
|
66
66
|
semantic_release/hvcs/gitlab.py,sha256=uUnTKq9Kpqc83ppXA3NXfVYnz4O1ej2noz68jCYzg68,10606
|
|
67
67
|
semantic_release/hvcs/remote_hvcs_base.py,sha256=QtkjdMy9l-c7UOtyPz25cqVOkCk4IU-EOw6A8lP8l-U,6327
|
|
68
68
|
semantic_release/hvcs/token_auth.py,sha256=ZjT56-NIPB4OKIt1qwHCu1TavXnrWFIBl9ARlg56hgU,663
|
|
69
69
|
semantic_release/hvcs/util.py,sha256=PUNV4yUlpzDtNCFmh2joaPdU4JyfUBnVp0zaQsT9EDQ,2871
|
|
70
70
|
semantic_release/version/__init__.py,sha256=CLhtGQry9dLIij5XyRa9ZevxU_1p8tjMTSQ-K_GMpWM,270
|
|
71
71
|
semantic_release/version/algorithm.py,sha256=IxgYNF78W7qdMzdV4WsoljwfJgB-sn2XdfkevD0aZlo,16254
|
|
72
|
-
semantic_release/version/declaration.py,sha256=
|
|
72
|
+
semantic_release/version/declaration.py,sha256=qWHnDm8xakX_dXcPDhKk_3J5Zuu7HUYKmbqm5Iv68uQ,3743
|
|
73
73
|
semantic_release/version/translator.py,sha256=iIfu3WreB9qqPHgqJLILbBluVQQNcpP0DEsnn_WzAaM,3689
|
|
74
74
|
semantic_release/version/version.py,sha256=Y3Qqqv7CypirikT7jNqqFMNAvR2UjV-VQx-c2G0mZc0,14519
|
|
75
75
|
semantic_release/version/declarations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
76
76
|
semantic_release/version/declarations/enum.py,sha256=3n5Py9DoFkmItIdsmtQrJgmAhepTv_1EnogzSdXu1Wg,244
|
|
77
|
-
semantic_release/version/declarations/
|
|
77
|
+
semantic_release/version/declarations/file.py,sha256=m-MohBTN69T3unoPcSBgwA76ftwPr2WXfGgVjSvf1vY,4683
|
|
78
|
+
semantic_release/version/declarations/i_version_replacer.py,sha256=YktOY_7cfguZHEyV5f_R-ldMznj140mcS0BXdoa9zf8,2584
|
|
78
79
|
semantic_release/version/declarations/pattern.py,sha256=sKk0uQpJjWVZc8RJUjxQoEPUvFLxXNGGBow5h1IqCTM,8378
|
|
79
80
|
semantic_release/version/declarations/toml.py,sha256=2K4DtX5Qq1iHT8cG8mISPTMmp50w6Av0KmLAKZPYqq8,4931
|
|
80
|
-
python_semantic_release-10.
|
|
81
|
-
python_semantic_release-10.
|
|
82
|
-
python_semantic_release-10.
|
|
83
|
-
python_semantic_release-10.
|
|
84
|
-
python_semantic_release-10.
|
|
81
|
+
python_semantic_release-10.6.0.dist-info/METADATA,sha256=loZ9-00JeyoQIE0DUn9O3JlLMhbpDVX8AHW6ULb_G6c,4053
|
|
82
|
+
python_semantic_release-10.6.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
83
|
+
python_semantic_release-10.6.0.dist-info/entry_points.txt,sha256=kzkCyDJsMOwgpFwEWKE9wxN1tXaUP6g6GIO4xtc0QuE,162
|
|
84
|
+
python_semantic_release-10.6.0.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
|
|
85
|
+
python_semantic_release-10.6.0.dist-info/RECORD,,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Literal
|
|
4
6
|
|
|
5
7
|
import click
|
|
6
8
|
import tomlkit
|
|
@@ -31,7 +33,9 @@ from semantic_release.cli.config import RawConfig
|
|
|
31
33
|
"'semantic_release'"
|
|
32
34
|
),
|
|
33
35
|
)
|
|
34
|
-
def generate_config(
|
|
36
|
+
def generate_config(
|
|
37
|
+
fmt: Literal["toml", "json"], is_pyproject_toml: bool = False
|
|
38
|
+
) -> None:
|
|
35
39
|
"""
|
|
36
40
|
Generate default configuration for semantic-release, to help you get started
|
|
37
41
|
quickly. You can inspect the defaults, write to a file and then edit according to
|
|
@@ -42,14 +46,29 @@ def generate_config(fmt: str = "toml", is_pyproject_toml: bool = False) -> None:
|
|
|
42
46
|
"""
|
|
43
47
|
# due to possible IntEnum values (which are not supported by tomlkit.dumps, see sdispater/tomlkit#237),
|
|
44
48
|
# we must ensure the transformation of the model to a dict uses json serializable values
|
|
45
|
-
|
|
49
|
+
config_dct = {
|
|
50
|
+
"semantic_release": RawConfig().model_dump(mode="json", exclude_none=True)
|
|
51
|
+
}
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
config_dct = {"tool": config_dct}
|
|
53
|
+
if is_pyproject_toml:
|
|
54
|
+
output = tomlkit.dumps({"tool": config_dct})
|
|
50
55
|
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
elif fmt == "toml":
|
|
57
|
+
output = tomlkit.dumps(config_dct)
|
|
53
58
|
|
|
54
59
|
elif fmt == "json":
|
|
55
|
-
|
|
60
|
+
output = json.dumps(config_dct, indent=4)
|
|
61
|
+
|
|
62
|
+
else:
|
|
63
|
+
raise ValueError(f"Unsupported format: {fmt}")
|
|
64
|
+
|
|
65
|
+
# Write output directly to stdout buffer as UTF-8 bytes
|
|
66
|
+
# This ensures consistent UTF-8 output on all platforms, especially Windows where
|
|
67
|
+
# shell redirection (>, >>) defaults to the system encoding (e.g., UTF-16LE or cp1252)
|
|
68
|
+
# By writing to sys.stdout.buffer, we bypass the encoding layer and guarantee UTF-8.
|
|
69
|
+
try:
|
|
70
|
+
sys.stdout.buffer.write(f"{output.strip()}\n".encode("utf-8")) # noqa: UP012; allow explicit encoding declaration
|
|
71
|
+
sys.stdout.buffer.flush()
|
|
72
|
+
except (AttributeError, TypeError):
|
|
73
|
+
# Fallback for environments without buffer (shouldn't happen in standard Python)
|
|
74
|
+
click.echo(output)
|
|
@@ -6,6 +6,7 @@ import click
|
|
|
6
6
|
from git import Repo
|
|
7
7
|
|
|
8
8
|
from semantic_release.cli.util import noop_report
|
|
9
|
+
from semantic_release.errors import AssetUploadError
|
|
9
10
|
from semantic_release.globals import logger
|
|
10
11
|
from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase
|
|
11
12
|
from semantic_release.version.algorithm import tags_and_versions
|
|
@@ -90,9 +91,13 @@ def publish(cli_ctx: CliContextObj, tag: str) -> None:
|
|
|
90
91
|
)
|
|
91
92
|
return
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
try:
|
|
95
|
+
publish_distributions(
|
|
96
|
+
tag=tag,
|
|
97
|
+
hvcs_client=hvcs_client,
|
|
98
|
+
dist_glob_patterns=dist_glob_patterns,
|
|
99
|
+
noop=runtime.global_cli_options.noop,
|
|
100
|
+
)
|
|
101
|
+
except AssetUploadError as err:
|
|
102
|
+
click.echo(err, err=True)
|
|
103
|
+
ctx.exit(1)
|
semantic_release/cli/config.py
CHANGED
|
@@ -57,6 +57,7 @@ from semantic_release.errors import (
|
|
|
57
57
|
)
|
|
58
58
|
from semantic_release.globals import logger
|
|
59
59
|
from semantic_release.helpers import dynamic_import
|
|
60
|
+
from semantic_release.version.declarations.file import FileVersionDeclaration
|
|
60
61
|
from semantic_release.version.declarations.i_version_replacer import IVersionReplacer
|
|
61
62
|
from semantic_release.version.declarations.pattern import PatternVersionDeclaration
|
|
62
63
|
from semantic_release.version.declarations.toml import TomlVersionDeclaration
|
|
@@ -757,12 +758,22 @@ class RuntimeContext:
|
|
|
757
758
|
) from err
|
|
758
759
|
|
|
759
760
|
try:
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
761
|
+
for definition in iter(raw.version_variables or ()):
|
|
762
|
+
# Check if this is a file replacement definition (pattern is "*")
|
|
763
|
+
parts = definition.split(":", maxsplit=2)
|
|
764
|
+
if len(parts) >= 2 and parts[1] == "*":
|
|
765
|
+
# Use FileVersionDeclaration for entire file replacement
|
|
766
|
+
version_declarations.append(
|
|
767
|
+
FileVersionDeclaration.from_string_definition(definition)
|
|
768
|
+
)
|
|
769
|
+
continue
|
|
770
|
+
|
|
771
|
+
# Use PatternVersionDeclaration for pattern-based replacement
|
|
772
|
+
version_declarations.append(
|
|
773
|
+
PatternVersionDeclaration.from_string_definition(
|
|
774
|
+
definition, raw.tag_format
|
|
775
|
+
)
|
|
763
776
|
)
|
|
764
|
-
for definition in iter(raw.version_variables or ())
|
|
765
|
-
)
|
|
766
777
|
except ValueError as err:
|
|
767
778
|
raise InvalidConfiguration(
|
|
768
779
|
str.join(
|
semantic_release/cli/util.py
CHANGED
|
@@ -75,7 +75,7 @@ def load_raw_config_file(config_file: Path | str) -> dict[Any, Any]:
|
|
|
75
75
|
while trying to read the specified configuration file
|
|
76
76
|
"""
|
|
77
77
|
logger.info("Loading configuration from %s", config_file)
|
|
78
|
-
raw_text = (Path() / config_file).resolve().read_text(encoding="utf-8")
|
|
78
|
+
raw_text = (Path() / config_file).resolve().read_text(encoding="utf-8-sig")
|
|
79
79
|
try:
|
|
80
80
|
logger.debug("Trying to parse configuration %s in TOML format", config_file)
|
|
81
81
|
return parse_toml(raw_text)
|
|
@@ -65,7 +65,12 @@ class EmojiParserOptions(ParserOptions):
|
|
|
65
65
|
)
|
|
66
66
|
"""Commit-type prefixes that should result in a patch release bump."""
|
|
67
67
|
|
|
68
|
-
other_allowed_tags: Tuple[str, ...] = (
|
|
68
|
+
other_allowed_tags: Tuple[str, ...] = (
|
|
69
|
+
":checkmark:",
|
|
70
|
+
":construction_worker:",
|
|
71
|
+
":memo:",
|
|
72
|
+
":recycle:",
|
|
73
|
+
)
|
|
69
74
|
"""Commit-type prefixes that are allowed but do not result in a version bump."""
|
|
70
75
|
|
|
71
76
|
allowed_tags: Tuple[str, ...] = (
|
semantic_release/helpers.py
CHANGED
|
@@ -9,7 +9,7 @@ from functools import lru_cache, reduce, wraps
|
|
|
9
9
|
from pathlib import Path, PurePosixPath
|
|
10
10
|
from re import IGNORECASE, compile as regexp
|
|
11
11
|
from typing import TYPE_CHECKING, Any, Callable, NamedTuple, Sequence, TypeVar
|
|
12
|
-
from urllib.parse import urlsplit
|
|
12
|
+
from urllib.parse import urlsplit, urlunsplit
|
|
13
13
|
|
|
14
14
|
from semantic_release.globals import logger
|
|
15
15
|
|
|
@@ -215,6 +215,16 @@ class ParsedGitUrl(NamedTuple):
|
|
|
215
215
|
repo_name: str
|
|
216
216
|
|
|
217
217
|
|
|
218
|
+
def _hide_credentials_in_url(url: str) -> str:
|
|
219
|
+
url_parts = urlsplit(url)
|
|
220
|
+
|
|
221
|
+
if not url_parts.scheme or "@" not in url_parts.netloc:
|
|
222
|
+
return url
|
|
223
|
+
|
|
224
|
+
_, _, host = url_parts.netloc.rpartition("@")
|
|
225
|
+
return urlunsplit(url_parts._replace(netloc=f"<credentials>@{host}"))
|
|
226
|
+
|
|
227
|
+
|
|
218
228
|
@lru_cache(maxsize=512)
|
|
219
229
|
def parse_git_url(url: str) -> ParsedGitUrl:
|
|
220
230
|
"""
|
|
@@ -242,7 +252,7 @@ def parse_git_url(url: str) -> ParsedGitUrl:
|
|
|
242
252
|
|
|
243
253
|
Raises ValueError if the url can't be parsed.
|
|
244
254
|
"""
|
|
245
|
-
logger.debug("Parsing git url %r", url)
|
|
255
|
+
logger.debug("Parsing git url %r", _hide_credentials_in_url(url))
|
|
246
256
|
|
|
247
257
|
# Normalizers are a list of tuples of (pattern, replacement)
|
|
248
258
|
normalizers = [
|
semantic_release/hvcs/github.py
CHANGED
|
@@ -463,14 +463,25 @@ class Github(RemoteHvcsBase):
|
|
|
463
463
|
|
|
464
464
|
# Upload assets
|
|
465
465
|
n_succeeded = 0
|
|
466
|
+
errors = []
|
|
466
467
|
for file_path in (
|
|
467
468
|
f for f in glob.glob(dist_glob, recursive=True) if os.path.isfile(f)
|
|
468
469
|
):
|
|
469
470
|
try:
|
|
470
471
|
self.upload_release_asset(release_id, file_path)
|
|
471
472
|
n_succeeded += 1
|
|
472
|
-
except HTTPError: # noqa: PERF203
|
|
473
|
+
except HTTPError as err: # noqa: PERF203
|
|
473
474
|
logger.exception("error uploading asset %s", file_path)
|
|
475
|
+
status_code = (
|
|
476
|
+
err.response.status_code if err.response is not None else "unknown"
|
|
477
|
+
)
|
|
478
|
+
error_msg = f"Failed to upload asset '{file_path}' to release"
|
|
479
|
+
if status_code != "unknown":
|
|
480
|
+
error_msg += f" (HTTP {status_code})"
|
|
481
|
+
errors.append(error_msg)
|
|
482
|
+
|
|
483
|
+
if errors:
|
|
484
|
+
raise AssetUploadError("\n".join(errors))
|
|
474
485
|
|
|
475
486
|
return n_succeeded
|
|
476
487
|
|
|
@@ -9,6 +9,7 @@ from deprecated.sphinx import deprecated
|
|
|
9
9
|
|
|
10
10
|
from semantic_release.globals import logger
|
|
11
11
|
from semantic_release.version.declarations.enum import VersionStampType
|
|
12
|
+
from semantic_release.version.declarations.file import FileVersionDeclaration
|
|
12
13
|
from semantic_release.version.declarations.i_version_replacer import IVersionReplacer
|
|
13
14
|
from semantic_release.version.declarations.pattern import PatternVersionDeclaration
|
|
14
15
|
from semantic_release.version.declarations.toml import TomlVersionDeclaration
|
|
@@ -19,11 +20,12 @@ if TYPE_CHECKING: # pragma: no cover
|
|
|
19
20
|
|
|
20
21
|
# Globals
|
|
21
22
|
__all__ = [
|
|
23
|
+
"FileVersionDeclaration",
|
|
22
24
|
"IVersionReplacer",
|
|
23
|
-
"VersionStampType",
|
|
24
25
|
"PatternVersionDeclaration",
|
|
25
26
|
"TomlVersionDeclaration",
|
|
26
27
|
"VersionDeclarationABC",
|
|
28
|
+
"VersionStampType",
|
|
27
29
|
]
|
|
28
30
|
|
|
29
31
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from deprecated.sphinx import deprecated
|
|
7
|
+
|
|
8
|
+
from semantic_release.globals import logger
|
|
9
|
+
from semantic_release.version.declarations.enum import VersionStampType
|
|
10
|
+
from semantic_release.version.declarations.i_version_replacer import IVersionReplacer
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
13
|
+
from semantic_release.version.version import Version
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FileVersionDeclaration(IVersionReplacer):
|
|
17
|
+
"""
|
|
18
|
+
IVersionReplacer implementation that replaces the entire file content
|
|
19
|
+
with the version string.
|
|
20
|
+
|
|
21
|
+
This is useful for files that contain only a version number, such as
|
|
22
|
+
VERSION files or similar single-line version storage files.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, path: Path | str, stamp_format: VersionStampType) -> None:
|
|
26
|
+
self._content: str | None = None
|
|
27
|
+
self._path = Path(path).resolve()
|
|
28
|
+
self._stamp_format = stamp_format
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def content(self) -> str:
|
|
32
|
+
"""A cached property that stores the content of the configured source file."""
|
|
33
|
+
if self._content is None:
|
|
34
|
+
logger.debug("No content stored, reading from source file %s", self._path)
|
|
35
|
+
|
|
36
|
+
if not self._path.exists():
|
|
37
|
+
logger.debug(
|
|
38
|
+
f"path {self._path!r} does not exist, assuming empty content"
|
|
39
|
+
)
|
|
40
|
+
self._content = ""
|
|
41
|
+
else:
|
|
42
|
+
self._content = self._path.read_text()
|
|
43
|
+
|
|
44
|
+
return self._content
|
|
45
|
+
|
|
46
|
+
@content.deleter
|
|
47
|
+
def content(self) -> None:
|
|
48
|
+
self._content = None
|
|
49
|
+
|
|
50
|
+
@deprecated(
|
|
51
|
+
version="10.6.0",
|
|
52
|
+
reason="Function is unused and will be removed in a future release",
|
|
53
|
+
)
|
|
54
|
+
def parse(self) -> set[Version]:
|
|
55
|
+
raise NotImplementedError # pragma: no cover
|
|
56
|
+
|
|
57
|
+
def replace(self, new_version: Version) -> str:
|
|
58
|
+
"""
|
|
59
|
+
Replace the file content with the new version string.
|
|
60
|
+
|
|
61
|
+
:param new_version: The new version number as a `Version` instance
|
|
62
|
+
:return: The new content (just the version string)
|
|
63
|
+
"""
|
|
64
|
+
new_content = (
|
|
65
|
+
new_version.as_tag()
|
|
66
|
+
if self._stamp_format == VersionStampType.TAG_FORMAT
|
|
67
|
+
else str(new_version)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
logger.debug(
|
|
71
|
+
"Replacing entire file content: path=%r old_content=%r new_content=%r",
|
|
72
|
+
self._path,
|
|
73
|
+
self.content.strip(),
|
|
74
|
+
new_content,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return new_content
|
|
78
|
+
|
|
79
|
+
def update_file_w_version(
|
|
80
|
+
self, new_version: Version, noop: bool = False
|
|
81
|
+
) -> Path | None:
|
|
82
|
+
if noop:
|
|
83
|
+
if not self._path.exists():
|
|
84
|
+
logger.warning(
|
|
85
|
+
f"FILE NOT FOUND: file '{self._path}' does not exist but it will be created"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return self._path
|
|
89
|
+
|
|
90
|
+
new_content = self.replace(new_version)
|
|
91
|
+
if new_content == self.content.strip():
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
self._path.write_text(f"{new_content}\n")
|
|
95
|
+
del self.content
|
|
96
|
+
|
|
97
|
+
return self._path
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def from_string_definition(cls, replacement_def: str) -> FileVersionDeclaration:
|
|
101
|
+
"""
|
|
102
|
+
Create an instance of self from a string representing one item
|
|
103
|
+
of the "version_variables" list in the configuration.
|
|
104
|
+
|
|
105
|
+
This method expects a definition in the format:
|
|
106
|
+
"file:*:format_type"
|
|
107
|
+
|
|
108
|
+
where:
|
|
109
|
+
- file is the path to the file
|
|
110
|
+
- * is the literal asterisk character indicating file replacement
|
|
111
|
+
- format_type is either "nf" (number format) or "tf" (tag format)
|
|
112
|
+
"""
|
|
113
|
+
parts = replacement_def.split(":", maxsplit=2)
|
|
114
|
+
|
|
115
|
+
if len(parts) <= 1:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"Invalid replacement definition {replacement_def!r}, missing ':'"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if len(parts) == 2:
|
|
121
|
+
# apply default version_type of "number_format" (ie. "1.2.3")
|
|
122
|
+
parts = [*parts, VersionStampType.NUMBER_FORMAT.value]
|
|
123
|
+
|
|
124
|
+
path, pattern, version_type = parts
|
|
125
|
+
|
|
126
|
+
# Validate that the pattern is exactly "*"
|
|
127
|
+
if pattern != "*":
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"Invalid pattern {pattern!r} for FileVersionDeclaration, expected '*'"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
stamp_type = VersionStampType(version_type)
|
|
134
|
+
except ValueError as err:
|
|
135
|
+
raise ValueError(
|
|
136
|
+
str.join(
|
|
137
|
+
" ",
|
|
138
|
+
[
|
|
139
|
+
"Invalid stamp type, must be one of:",
|
|
140
|
+
str.join(", ", [e.value for e in VersionStampType]),
|
|
141
|
+
],
|
|
142
|
+
)
|
|
143
|
+
) from err
|
|
144
|
+
|
|
145
|
+
return cls(path, stamp_type)
|
|
@@ -3,6 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
from abc import ABCMeta, abstractmethod
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
|
+
from deprecated.sphinx import deprecated
|
|
7
|
+
|
|
6
8
|
if TYPE_CHECKING: # pragma: no cover
|
|
7
9
|
from pathlib import Path
|
|
8
10
|
|
|
@@ -32,6 +34,10 @@ class IVersionReplacer(metaclass=ABCMeta):
|
|
|
32
34
|
)
|
|
33
35
|
)
|
|
34
36
|
|
|
37
|
+
@deprecated(
|
|
38
|
+
version="9.20.0",
|
|
39
|
+
reason="Function is unused and will be removed in a future release",
|
|
40
|
+
)
|
|
35
41
|
@abstractmethod
|
|
36
42
|
def parse(self) -> set[Version]:
|
|
37
43
|
"""
|
|
File without changes
|
|
File without changes
|
{python_semantic_release-10.5.3.dist-info → python_semantic_release-10.6.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|