python-semantic-release 9.8.2__py3-none-any.whl → 9.8.4__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-9.8.2.dist-info → python_semantic_release-9.8.4.dist-info}/METADATA +4 -4
- python_semantic_release-9.8.4.dist-info/RECORD +56 -0
- python_semantic_release-9.8.4.dist-info/entry_points.txt +3 -0
- semantic_release/__init__.py +20 -2
- semantic_release/__main__.py +1 -1
- semantic_release/changelog/context.py +25 -1
- semantic_release/changelog/release_history.py +3 -0
- semantic_release/cli/__init__.py +0 -10
- semantic_release/cli/changelog_writer.py +169 -0
- semantic_release/cli/{commands/cli_context.py → cli_context.py} +7 -3
- semantic_release/cli/commands/changelog.py +89 -81
- semantic_release/cli/commands/main.py +32 -2
- semantic_release/cli/commands/publish.py +56 -21
- semantic_release/cli/commands/version.py +308 -351
- semantic_release/cli/config.py +67 -23
- semantic_release/commit_parser/_base.py +2 -1
- semantic_release/commit_parser/scipy.py +10 -1
- semantic_release/commit_parser/token.py +4 -2
- semantic_release/errors.py +33 -1
- semantic_release/gitproject.py +254 -0
- semantic_release/hvcs/bitbucket.py +3 -2
- semantic_release/hvcs/gitea.py +26 -1
- semantic_release/hvcs/github.py +26 -1
- semantic_release/hvcs/gitlab.py +8 -1
- semantic_release/hvcs/remote_hvcs_base.py +2 -1
- semantic_release/version/algorithm.py +1 -1
- python_semantic_release-9.8.2.dist-info/RECORD +0 -55
- python_semantic_release-9.8.2.dist-info/entry_points.txt +0 -3
- semantic_release/cli/common.py +0 -50
- {python_semantic_release-9.8.2.dist-info → python_semantic_release-9.8.4.dist-info}/AUTHORS.rst +0 -0
- {python_semantic_release-9.8.2.dist-info → python_semantic_release-9.8.4.dist-info}/LICENSE +0 -0
- {python_semantic_release-9.8.2.dist-info → python_semantic_release-9.8.4.dist-info}/WHEEL +0 -0
- {python_semantic_release-9.8.2.dist-info → python_semantic_release-9.8.4.dist-info}/top_level.txt +0 -0
{python_semantic_release-9.8.2.dist-info → python_semantic_release-9.8.4.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-semantic-release
|
|
3
|
-
Version: 9.8.
|
|
3
|
+
Version: 9.8.4
|
|
4
4
|
Summary: Automatic Semantic Versioning for Python projects
|
|
5
5
|
Author-email: Rolf Erik Lekang <me@rolflekang.com>
|
|
6
6
|
License: MIT
|
|
@@ -37,15 +37,15 @@ 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.
|
|
40
|
+
Requires-Dist: ruff ==0.5.0 ; 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'
|
|
44
44
|
Requires-Dist: sphinx-autobuild ==2024.2.4 ; extra == 'docs'
|
|
45
45
|
Requires-Dist: furo ~=2024.1 ; extra == 'docs'
|
|
46
46
|
Provides-Extra: mypy
|
|
47
|
-
Requires-Dist: mypy ==1.10.
|
|
48
|
-
Requires-Dist: types-requests ~=2.
|
|
47
|
+
Requires-Dist: mypy ==1.10.1 ; extra == 'mypy'
|
|
48
|
+
Requires-Dist: types-requests ~=2.32.0 ; extra == 'mypy'
|
|
49
49
|
Provides-Extra: test
|
|
50
50
|
Requires-Dist: coverage[toml] ~=7.0 ; extra == 'test'
|
|
51
51
|
Requires-Dist: pytest ~=7.0 ; extra == 'test'
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
semantic_release/__init__.py,sha256=2zPAOZ5IfYsvLnhab9ZWAjy_bunkNmm0OwE3CVcP6rI,1228
|
|
2
|
+
semantic_release/__main__.py,sha256=blPn7CMpuSe4Z-GBIDv0QA0OEDTMGU-7edsLRkkhPR4,120
|
|
3
|
+
semantic_release/const.py,sha256=Z1o2QNh60wSLeF-_1TemMBjU3ZXbV0XghnUFsbTVfOs,831
|
|
4
|
+
semantic_release/enums.py,sha256=D5B_reQGGKQQT22HO5PUtvn2Bok3fkht6TfJtXkmAUg,1020
|
|
5
|
+
semantic_release/errors.py,sha256=bPQDMW9MVhCwkE_TrpsmsRYZRuk5iOnqi-hKeujXQc0,2676
|
|
6
|
+
semantic_release/gitproject.py,sha256=izWc4NLdUzAwxGG_fJeqqHW9ivSrPcWBzSaOijQx4f8,8564
|
|
7
|
+
semantic_release/helpers.py,sha256=d1jOX0SNyqPc_3wr14xR25FfpqhMd4Ev7MNBOWlScc0,5581
|
|
8
|
+
semantic_release/changelog/__init__.py,sha256=Bg6Xe5Vt32rWoMscW-hd4sUwiZqzWmsg4CD1EhMesMY,262
|
|
9
|
+
semantic_release/changelog/context.py,sha256=8cbOv9cB9Ji7xjRn4-YUUHBfg9LLfSwmmXuZpqEmvns,1653
|
|
10
|
+
semantic_release/changelog/release_history.py,sha256=jfkfnlATBqRpVLzxVi7wf2fO_bH_GOifQbaMO-mqDeY,6781
|
|
11
|
+
semantic_release/changelog/template.py,sha256=JqZcjdozM9-059Sa8PfiHxYNeyZFa0jxk_beyRLv5sU,4640
|
|
12
|
+
semantic_release/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
semantic_release/cli/changelog_writer.py,sha256=Ga8QAq12kUD-fWWlrFoQCpn1pOzZmHnaCeZfFbBytFo,5027
|
|
14
|
+
semantic_release/cli/cli_context.py,sha256=LApmhR6PTdHTPxs42KYMT_yYbaEXae-myuvio0mhI8w,3522
|
|
15
|
+
semantic_release/cli/config.py,sha256=NAKOqLAc8NV0oL9XxAUPz2FWwS89Bjz8aKSCsRmQsvw,23661
|
|
16
|
+
semantic_release/cli/const.py,sha256=S8oNNpSaKdZxcAs7PAKEjQHic0czoJBGd7XHo57FOzw,39
|
|
17
|
+
semantic_release/cli/github_actions_output.py,sha256=Wy4jEIpSFgoMyJqq4FD7scIpgovivutFLZ7Fs4y2CUY,2110
|
|
18
|
+
semantic_release/cli/masking_filter.py,sha256=DxqjiJyABlzwwwZ1r8JGQpb6QrF00StJFm0-2-s5Fv0,3071
|
|
19
|
+
semantic_release/cli/util.py,sha256=FyXaBkeL7nXKjy3X9rQLEwvn7p46xPekp2V8Z-5MVrk,3755
|
|
20
|
+
semantic_release/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
semantic_release/cli/commands/changelog.py,sha256=zincrvCBwMb7S3K3udRcArtQCuGkree-7IEzNtvF42A,3489
|
|
22
|
+
semantic_release/cli/commands/generate_config.py,sha256=2xZOu3NpyhBp0pWr7d8ugKl_kjqQgpSsSMHq5wHTfrE,1699
|
|
23
|
+
semantic_release/cli/commands/main.py,sha256=kqO8bZw9Nv6T5QkIl53zzmH2rd4LuLoA8agweH0VYPY,4022
|
|
24
|
+
semantic_release/cli/commands/publish.py,sha256=SZQlIewvqyIC14dkIIVVFetE0tPsKbO1cUyxnZsicrw,2845
|
|
25
|
+
semantic_release/cli/commands/version.py,sha256=Saunh8juMDHJuPK8VcH0ZiUfYJduNODSwsAX2DjLki4,23809
|
|
26
|
+
semantic_release/commit_parser/__init__.py,sha256=cv5HFBdw7OJd4Laj4Ex8ZZ5Tml8GwXgQcXW6Pasr2Ao,615
|
|
27
|
+
semantic_release/commit_parser/_base.py,sha256=t-Z9ALgAe7aZpYXz1mk3Fe-uAvipgKdNrq4Okg_WW9c,3026
|
|
28
|
+
semantic_release/commit_parser/angular.py,sha256=ERWeTBTFNIBOaTIopY4Vff0KbeFwrSQWbADe3smVW-8,4579
|
|
29
|
+
semantic_release/commit_parser/emoji.py,sha256=M6zgqsXqegLK5FxZGyfsOPM3vOsJkPKVAjnGATOhwa4,3452
|
|
30
|
+
semantic_release/commit_parser/scipy.py,sha256=AH_GeMZ9hWiNQTL78bovpQy7mBeobGpUk5Shzyxxm6c,5951
|
|
31
|
+
semantic_release/commit_parser/tag.py,sha256=SAop9INQvKOwktNmVypKFngd6xHmY_XZrLUWChUjz84,3353
|
|
32
|
+
semantic_release/commit_parser/token.py,sha256=BB4ZCyt753CCaBFF95cQ4sFm6Au96wpO0YyTAWdcOvE,1609
|
|
33
|
+
semantic_release/commit_parser/util.py,sha256=vLcVDErZrExM55jMffos0hyMbNVQoJ-PeeVDG1Ej51I,730
|
|
34
|
+
semantic_release/data/templates/CHANGELOG.md.j2,sha256=4SNA1uTM2_qLA_3QTbS8JHZi9tB-21gCzLjkj_U20JI,1069
|
|
35
|
+
semantic_release/data/templates/release_notes.md.j2,sha256=27uIHgG6iYmqdMaoWFP5LJRZcrO-3paqFzHHHsGpXXE,465
|
|
36
|
+
semantic_release/hvcs/__init__.py,sha256=JwoaLOF-12L-OBo_9-tOXXhdiHKeVungA9865to2oZk,494
|
|
37
|
+
semantic_release/hvcs/_base.py,sha256=9-iTqTPSbiEevKbCBP9K2hq4c-2T4wPbeLWe-kAxBzo,2607
|
|
38
|
+
semantic_release/hvcs/bitbucket.py,sha256=nqlOmeNda0sRSEBGWMluphy1KlpRTQrHV7itxf0IXE0,9266
|
|
39
|
+
semantic_release/hvcs/gitea.py,sha256=XPwsjQ1XeSAdAsAjW7_QJdjxfTPJvW-Vnmoze7kCZP8,11919
|
|
40
|
+
semantic_release/hvcs/github.py,sha256=TVXlviaXn2N_DBH0GxvIhTH-SQdn5mxbkjgu8rvb1y0,19148
|
|
41
|
+
semantic_release/hvcs/gitlab.py,sha256=ia8Gjfm8mIctlEu5xqMPa-6t7YGSZQUbzKhKzgRuC3Q,9683
|
|
42
|
+
semantic_release/hvcs/remote_hvcs_base.py,sha256=2TaPPp1NzsQjUMOKa0NjqkjYTxJpX245qGm7-GCrnx8,6085
|
|
43
|
+
semantic_release/hvcs/token_auth.py,sha256=ZjT56-NIPB4OKIt1qwHCu1TavXnrWFIBl9ARlg56hgU,663
|
|
44
|
+
semantic_release/hvcs/util.py,sha256=PNKXApgL2np9lpnKjAbo77toRgElLPXrbS1q7621waI,2886
|
|
45
|
+
semantic_release/version/__init__.py,sha256=CLhtGQry9dLIij5XyRa9ZevxU_1p8tjMTSQ-K_GMpWM,270
|
|
46
|
+
semantic_release/version/algorithm.py,sha256=ofx_bIWq6ptJVr-ekI11IzxzDEctDKFiVwaSmmmgRlE,16870
|
|
47
|
+
semantic_release/version/declaration.py,sha256=f6Ld7hIhrqvDrRBapJHr-KDimuyo-4IG8009Zu9BIgU,7357
|
|
48
|
+
semantic_release/version/translator.py,sha256=P1noIsVBn8u6zNOFjG0xKYOWapxqf_PHSMvMeLJ9kXg,3050
|
|
49
|
+
semantic_release/version/version.py,sha256=6PCtSbLP88U1daoxnCwHc--YguZo4waGNLqJ5JfeczE,14175
|
|
50
|
+
python_semantic_release-9.8.4.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
|
|
51
|
+
python_semantic_release-9.8.4.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
|
|
52
|
+
python_semantic_release-9.8.4.dist-info/METADATA,sha256=pfsnObyVM-vMXf-fME2dZbgH1yyiboC8gH805RJLvVE,5404
|
|
53
|
+
python_semantic_release-9.8.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
54
|
+
python_semantic_release-9.8.4.dist-info/entry_points.txt,sha256=6bS6euSagjerp7onDtfI9_ZpczWreZF3gjHfGAegkbo,123
|
|
55
|
+
python_semantic_release-9.8.4.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
|
|
56
|
+
python_semantic_release-9.8.4.dist-info/RECORD,,
|
semantic_release/__init__.py
CHANGED
|
@@ -24,7 +24,25 @@ from semantic_release.version import (
|
|
|
24
24
|
tags_and_versions,
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
-
__version__ = "9.8.
|
|
27
|
+
__version__ = "9.8.4"
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"CommitParser",
|
|
31
|
+
"ParsedCommit",
|
|
32
|
+
"ParseError",
|
|
33
|
+
"ParseResult",
|
|
34
|
+
"ParseResultType",
|
|
35
|
+
"ParserOptions",
|
|
36
|
+
"LevelBump",
|
|
37
|
+
"SemanticReleaseBaseError",
|
|
38
|
+
"CommitParseError",
|
|
39
|
+
"InvalidConfiguration",
|
|
40
|
+
"InvalidVersion",
|
|
41
|
+
"Version",
|
|
42
|
+
"VersionTranslator",
|
|
43
|
+
"next_version",
|
|
44
|
+
"tags_and_versions",
|
|
45
|
+
]
|
|
28
46
|
|
|
29
47
|
|
|
30
48
|
def setup_hook(argv: list[str]) -> None:
|
|
@@ -36,6 +54,6 @@ def setup_hook(argv: list[str]) -> None:
|
|
|
36
54
|
if len(argv) > 1 and any(
|
|
37
55
|
cmd in argv for cmd in ["version", "publish", "changelog"]
|
|
38
56
|
):
|
|
39
|
-
from semantic_release.cli import main
|
|
57
|
+
from semantic_release.cli.commands.main import main
|
|
40
58
|
|
|
41
59
|
main()
|
semantic_release/__main__.py
CHANGED
|
@@ -6,8 +6,32 @@ from typing import TYPE_CHECKING, Any, Callable
|
|
|
6
6
|
if TYPE_CHECKING:
|
|
7
7
|
from jinja2 import Environment
|
|
8
8
|
|
|
9
|
-
from semantic_release.changelog.release_history import ReleaseHistory
|
|
9
|
+
from semantic_release.changelog.release_history import Release, ReleaseHistory
|
|
10
10
|
from semantic_release.hvcs._base import HvcsBase
|
|
11
|
+
from semantic_release.version.version import Version
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class ReleaseNotesContext:
|
|
16
|
+
repo_name: str
|
|
17
|
+
repo_owner: str
|
|
18
|
+
hvcs_type: str
|
|
19
|
+
version: Version
|
|
20
|
+
release: Release
|
|
21
|
+
filters: tuple[Callable[..., Any], ...] = ()
|
|
22
|
+
|
|
23
|
+
def bind_to_environment(self, env: Environment) -> Environment:
|
|
24
|
+
env_globals = dict(
|
|
25
|
+
filter(lambda k_v: k_v[0] != "filters", self.__dict__.items())
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
for g, v in env_globals.items():
|
|
29
|
+
env.globals[g] = v
|
|
30
|
+
|
|
31
|
+
for f in self.filters:
|
|
32
|
+
env.filters[f.__name__] = f
|
|
33
|
+
|
|
34
|
+
return env
|
|
11
35
|
|
|
12
36
|
|
|
13
37
|
@dataclass
|
|
@@ -99,6 +99,7 @@ class ReleaseHistory:
|
|
|
99
99
|
committer=committer,
|
|
100
100
|
tagged_date=tagged_date,
|
|
101
101
|
elements=defaultdict(list),
|
|
102
|
+
version=the_version,
|
|
102
103
|
)
|
|
103
104
|
|
|
104
105
|
released.setdefault(the_version, release)
|
|
@@ -165,6 +166,7 @@ class ReleaseHistory:
|
|
|
165
166
|
"committer": committer,
|
|
166
167
|
"tagged_date": tagged_date,
|
|
167
168
|
"elements": self.unreleased,
|
|
169
|
+
"version": version,
|
|
168
170
|
},
|
|
169
171
|
**self.released,
|
|
170
172
|
},
|
|
@@ -183,3 +185,4 @@ class Release(TypedDict):
|
|
|
183
185
|
committer: Actor
|
|
184
186
|
tagged_date: datetime
|
|
185
187
|
elements: dict[str, list[ParseResult]]
|
|
188
|
+
version: Version
|
semantic_release/cli/__init__.py
CHANGED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
from semantic_release.cli.commands.changelog import changelog
|
|
2
|
-
from semantic_release.cli.commands.generate_config import generate_config
|
|
3
|
-
from semantic_release.cli.commands.main import main
|
|
4
|
-
from semantic_release.cli.commands.publish import publish
|
|
5
|
-
from semantic_release.cli.commands.version import version
|
|
6
|
-
|
|
7
|
-
main.add_command(changelog)
|
|
8
|
-
main.add_command(generate_config)
|
|
9
|
-
main.add_command(version)
|
|
10
|
-
main.add_command(publish)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from logging import getLogger
|
|
4
|
+
from os import listdir
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
# NOTE: use backport with newer API than stdlib
|
|
9
|
+
from importlib_resources import files
|
|
10
|
+
|
|
11
|
+
from semantic_release.changelog.context import (
|
|
12
|
+
ReleaseNotesContext,
|
|
13
|
+
make_changelog_context,
|
|
14
|
+
)
|
|
15
|
+
from semantic_release.changelog.template import environment, recursive_render
|
|
16
|
+
from semantic_release.cli.util import noop_report
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from jinja2 import Environment
|
|
20
|
+
|
|
21
|
+
from semantic_release.changelog.release_history import Release, ReleaseHistory
|
|
22
|
+
from semantic_release.cli.config import RuntimeContext
|
|
23
|
+
from semantic_release.hvcs._base import HvcsBase
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
log = getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_release_notes_template(template_dir: Path) -> str:
|
|
30
|
+
"""Read the project's template for release notes, falling back to the default."""
|
|
31
|
+
fname = template_dir / ".release_notes.md.j2"
|
|
32
|
+
try:
|
|
33
|
+
return fname.read_text(encoding="utf-8")
|
|
34
|
+
except FileNotFoundError:
|
|
35
|
+
return (
|
|
36
|
+
files("semantic_release")
|
|
37
|
+
.joinpath("data/templates/release_notes.md.j2")
|
|
38
|
+
.read_text(encoding="utf-8")
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def render_default_changelog_file(template_env: Environment) -> str:
|
|
43
|
+
changelog_text = (
|
|
44
|
+
files("semantic_release")
|
|
45
|
+
.joinpath("data/templates/CHANGELOG.md.j2")
|
|
46
|
+
.read_text(encoding="utf-8")
|
|
47
|
+
)
|
|
48
|
+
template = template_env.from_string(changelog_text)
|
|
49
|
+
return template.render().rstrip()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def render_release_notes(
|
|
53
|
+
release_notes_template: str,
|
|
54
|
+
template_env: Environment,
|
|
55
|
+
) -> str:
|
|
56
|
+
template = template_env.from_string(release_notes_template)
|
|
57
|
+
return template.render().rstrip()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def apply_user_changelog_template_directory(
|
|
61
|
+
template_dir: Path,
|
|
62
|
+
environment: Environment,
|
|
63
|
+
destination_dir: Path,
|
|
64
|
+
noop: bool = False,
|
|
65
|
+
) -> list[str]:
|
|
66
|
+
if noop:
|
|
67
|
+
noop_report(
|
|
68
|
+
str.join(
|
|
69
|
+
" ",
|
|
70
|
+
[
|
|
71
|
+
"would have recursively rendered the template directory",
|
|
72
|
+
f"{template_dir!r} relative to {destination_dir!r}.",
|
|
73
|
+
"Paths which would be modified by this operation cannot be",
|
|
74
|
+
"determined in no-op mode.",
|
|
75
|
+
],
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
return []
|
|
79
|
+
|
|
80
|
+
return recursive_render(
|
|
81
|
+
template_dir, environment=environment, _root_dir=destination_dir
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def write_default_changelog(
|
|
86
|
+
changelog_file: Path,
|
|
87
|
+
destination_dir: Path,
|
|
88
|
+
environment: Environment,
|
|
89
|
+
noop: bool = False,
|
|
90
|
+
) -> str:
|
|
91
|
+
if noop:
|
|
92
|
+
noop_report(
|
|
93
|
+
str.join(
|
|
94
|
+
" ",
|
|
95
|
+
[
|
|
96
|
+
"would have written your changelog to",
|
|
97
|
+
str(changelog_file.relative_to(destination_dir)),
|
|
98
|
+
],
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
changelog_text = render_default_changelog_file(environment)
|
|
103
|
+
changelog_file.write_text(f"{changelog_text}\n", encoding="utf-8")
|
|
104
|
+
|
|
105
|
+
return str(changelog_file)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def write_changelog_files(
|
|
109
|
+
runtime_ctx: RuntimeContext,
|
|
110
|
+
release_history: ReleaseHistory,
|
|
111
|
+
hvcs_client: HvcsBase,
|
|
112
|
+
noop: bool = False,
|
|
113
|
+
) -> list[str]:
|
|
114
|
+
project_dir = Path(runtime_ctx.repo_dir)
|
|
115
|
+
template_dir = runtime_ctx.template_dir
|
|
116
|
+
|
|
117
|
+
changelog_context = make_changelog_context(
|
|
118
|
+
hvcs_client=hvcs_client,
|
|
119
|
+
release_history=release_history,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
changelog_context.bind_to_environment(runtime_ctx.template_environment)
|
|
123
|
+
|
|
124
|
+
use_user_template_dir = bool(
|
|
125
|
+
# Directory exists and directory is not empty
|
|
126
|
+
template_dir.exists() and template_dir.is_dir() and listdir(template_dir)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if use_user_template_dir:
|
|
130
|
+
return apply_user_changelog_template_directory(
|
|
131
|
+
template_dir=template_dir,
|
|
132
|
+
environment=runtime_ctx.template_environment,
|
|
133
|
+
destination_dir=project_dir,
|
|
134
|
+
noop=noop,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
log.info("No contents found in %r, using default changelog template", template_dir)
|
|
138
|
+
return [
|
|
139
|
+
write_default_changelog(
|
|
140
|
+
changelog_file=runtime_ctx.changelog_file,
|
|
141
|
+
destination_dir=project_dir,
|
|
142
|
+
environment=runtime_ctx.template_environment,
|
|
143
|
+
noop=noop,
|
|
144
|
+
)
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def generate_release_notes(
|
|
149
|
+
hvcs_client: HvcsBase,
|
|
150
|
+
release: Release,
|
|
151
|
+
template_dir: Path,
|
|
152
|
+
) -> str:
|
|
153
|
+
release_notes_env = ReleaseNotesContext(
|
|
154
|
+
repo_name=hvcs_client.repo_name,
|
|
155
|
+
repo_owner=hvcs_client.owner,
|
|
156
|
+
hvcs_type=hvcs_client.__class__.__name__.lower(),
|
|
157
|
+
version=release["version"],
|
|
158
|
+
release=release,
|
|
159
|
+
filters=(*hvcs_client.get_changelog_context_filters(),),
|
|
160
|
+
).bind_to_environment(
|
|
161
|
+
# Use a new, non-configurable environment for release notes -
|
|
162
|
+
# not user-configurable at the moment
|
|
163
|
+
environment(template_dir=template_dir)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return render_release_notes(
|
|
167
|
+
release_notes_template=get_release_notes_template(template_dir),
|
|
168
|
+
template_env=release_notes_env,
|
|
169
|
+
)
|
|
@@ -14,7 +14,11 @@ from semantic_release.cli.config import (
|
|
|
14
14
|
RuntimeContext,
|
|
15
15
|
)
|
|
16
16
|
from semantic_release.cli.util import load_raw_config_file, rprint
|
|
17
|
-
from semantic_release.errors import
|
|
17
|
+
from semantic_release.errors import (
|
|
18
|
+
DetachedHeadGitError,
|
|
19
|
+
InvalidConfiguration,
|
|
20
|
+
NotAReleaseBranch,
|
|
21
|
+
)
|
|
18
22
|
|
|
19
23
|
if TYPE_CHECKING:
|
|
20
24
|
from semantic_release.cli.config import GlobalCommandLineOptions
|
|
@@ -58,7 +62,7 @@ class CliContextObj:
|
|
|
58
62
|
|
|
59
63
|
try:
|
|
60
64
|
if was_conf_file_user_provided and not conf_file_exists:
|
|
61
|
-
raise FileNotFoundError(
|
|
65
|
+
raise FileNotFoundError( # noqa: TRY301
|
|
62
66
|
f"File {self.global_opts.config_file} does not exist"
|
|
63
67
|
)
|
|
64
68
|
|
|
@@ -75,7 +79,7 @@ class CliContextObj:
|
|
|
75
79
|
raw_config,
|
|
76
80
|
global_cli_options=self.global_opts,
|
|
77
81
|
)
|
|
78
|
-
except NotAReleaseBranch as exc:
|
|
82
|
+
except (DetachedHeadGitError, NotAReleaseBranch) as exc:
|
|
79
83
|
rprint(f"[bold {'red' if self.global_opts.strict else 'orange1'}]{exc!s}")
|
|
80
84
|
# If not strict, exit 0 so other processes can continue. For example, in
|
|
81
85
|
# multibranch CI it might be desirable to run a non-release branch's pipeline
|
|
@@ -1,28 +1,50 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import os
|
|
5
4
|
from typing import TYPE_CHECKING
|
|
6
5
|
|
|
7
6
|
import click
|
|
7
|
+
from git import Repo
|
|
8
8
|
|
|
9
|
-
from semantic_release.changelog import ReleaseHistory
|
|
10
|
-
from semantic_release.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
render_default_changelog_file,
|
|
14
|
-
render_release_notes,
|
|
9
|
+
from semantic_release.changelog import ReleaseHistory
|
|
10
|
+
from semantic_release.cli.changelog_writer import (
|
|
11
|
+
generate_release_notes,
|
|
12
|
+
write_changelog_files,
|
|
15
13
|
)
|
|
16
14
|
from semantic_release.cli.util import noop_report
|
|
17
15
|
from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase
|
|
18
16
|
|
|
19
17
|
if TYPE_CHECKING:
|
|
20
|
-
from semantic_release.cli.
|
|
21
|
-
|
|
18
|
+
from semantic_release.cli.cli_context import CliContextObj
|
|
19
|
+
|
|
22
20
|
|
|
23
21
|
log = logging.getLogger(__name__)
|
|
24
22
|
|
|
25
23
|
|
|
24
|
+
def post_release_notes(
|
|
25
|
+
release_tag: str,
|
|
26
|
+
release_notes: str,
|
|
27
|
+
prerelease: bool,
|
|
28
|
+
hvcs_client: RemoteHvcsBase,
|
|
29
|
+
noop: bool = False,
|
|
30
|
+
) -> None:
|
|
31
|
+
if noop:
|
|
32
|
+
noop_report(
|
|
33
|
+
str.join(
|
|
34
|
+
"\n",
|
|
35
|
+
[
|
|
36
|
+
f"would have posted the following release notes for tag {release_tag}:",
|
|
37
|
+
release_notes,
|
|
38
|
+
],
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
hvcs_client.create_or_update_release(
|
|
44
|
+
release_tag, f"{release_notes}\n", prerelease=prerelease
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
26
48
|
@click.command(
|
|
27
49
|
short_help="Generate a changelog",
|
|
28
50
|
context_settings={
|
|
@@ -36,86 +58,72 @@ log = logging.getLogger(__name__)
|
|
|
36
58
|
help="Post the generated release notes to the remote VCS's release for this tag",
|
|
37
59
|
)
|
|
38
60
|
@click.pass_obj
|
|
39
|
-
def changelog(cli_ctx: CliContextObj, release_tag: str | None
|
|
61
|
+
def changelog(cli_ctx: CliContextObj, release_tag: str | None) -> None:
|
|
40
62
|
"""Generate and optionally publish a changelog for your project"""
|
|
41
63
|
ctx = click.get_current_context()
|
|
42
64
|
runtime = cli_ctx.runtime_ctx
|
|
43
|
-
repo = runtime.repo
|
|
44
|
-
parser = runtime.commit_parser
|
|
45
65
|
translator = runtime.version_translator
|
|
46
66
|
hvcs_client = runtime.hvcs_client
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
hvcs_client=hvcs_client,
|
|
67
|
+
|
|
68
|
+
with Repo(str(runtime.repo_dir)) as git_repo:
|
|
69
|
+
release_history = ReleaseHistory.from_git_history(
|
|
70
|
+
repo=git_repo,
|
|
71
|
+
translator=translator,
|
|
72
|
+
commit_parser=runtime.commit_parser,
|
|
73
|
+
exclude_commit_patterns=runtime.changelog_excluded_commit_patterns,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
write_changelog_files(
|
|
77
|
+
runtime_ctx=runtime,
|
|
78
|
+
release_history=release_history,
|
|
79
|
+
hvcs_client=hvcs_client,
|
|
80
|
+
noop=runtime.global_cli_options.noop,
|
|
60
81
|
)
|
|
61
|
-
changelog_context.bind_to_environment(env)
|
|
62
|
-
|
|
63
|
-
if not os.path.exists(template_dir):
|
|
64
|
-
log.info("Path %r not found, using default changelog template", template_dir)
|
|
65
|
-
if runtime.global_cli_options.noop:
|
|
66
|
-
noop_report(
|
|
67
|
-
"would have written your changelog to "
|
|
68
|
-
+ str(changelog_file.relative_to(repo.working_dir))
|
|
69
|
-
)
|
|
70
|
-
else:
|
|
71
|
-
changelog_text = render_default_changelog_file(env)
|
|
72
|
-
changelog_file.write_text(f"{changelog_text}\n", encoding="utf-8")
|
|
73
|
-
|
|
74
|
-
else:
|
|
75
|
-
if runtime.global_cli_options.noop:
|
|
76
|
-
noop_report(
|
|
77
|
-
f"would have recursively rendered the template directory "
|
|
78
|
-
f"{template_dir!r} relative to {repo.working_dir!r}"
|
|
79
|
-
)
|
|
80
|
-
else:
|
|
81
|
-
recursive_render(template_dir, environment=env, _root_dir=repo.working_dir)
|
|
82
82
|
|
|
83
|
-
if release_tag
|
|
84
|
-
|
|
85
|
-
noop_report(
|
|
86
|
-
f"would have posted changelog to the release for tag {release_tag}"
|
|
87
|
-
)
|
|
83
|
+
if not release_tag:
|
|
84
|
+
return
|
|
88
85
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
86
|
+
if not isinstance(hvcs_client, RemoteHvcsBase):
|
|
87
|
+
click.echo(
|
|
88
|
+
"Remote does not support releases. Skipping release notes update...",
|
|
89
|
+
err=True,
|
|
90
|
+
)
|
|
91
|
+
return
|
|
96
92
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
release=release,
|
|
93
|
+
if not (version := translator.from_tag(release_tag)):
|
|
94
|
+
click.echo(
|
|
95
|
+
str.join(
|
|
96
|
+
" ",
|
|
97
|
+
[
|
|
98
|
+
f"Tag {release_tag!r} does not match the tag format",
|
|
99
|
+
repr(translator.tag_format),
|
|
100
|
+
],
|
|
101
|
+
),
|
|
102
|
+
err=True,
|
|
108
103
|
)
|
|
104
|
+
ctx.exit(1)
|
|
109
105
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
106
|
+
try:
|
|
107
|
+
release = release_history.released[version]
|
|
108
|
+
except KeyError:
|
|
109
|
+
click.echo(f"tag {release_tag} not in release history", err=True)
|
|
110
|
+
ctx.exit(2)
|
|
111
|
+
|
|
112
|
+
release_notes = generate_release_notes(
|
|
113
|
+
hvcs_client,
|
|
114
|
+
release,
|
|
115
|
+
template_dir=runtime.template_dir,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
post_release_notes(
|
|
120
|
+
release_tag=release_tag,
|
|
121
|
+
release_notes=release_notes,
|
|
122
|
+
prerelease=version.is_prerelease,
|
|
123
|
+
hvcs_client=hvcs_client,
|
|
124
|
+
noop=runtime.global_cli_options.noop,
|
|
125
|
+
)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
log.exception(e)
|
|
128
|
+
click.echo("Failed to post release notes to remote", err=True)
|
|
129
|
+
ctx.exit(1)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import importlib
|
|
3
4
|
import logging
|
|
5
|
+
from enum import Enum
|
|
4
6
|
|
|
5
7
|
# from typing import TYPE_CHECKING
|
|
6
8
|
import click
|
|
@@ -8,7 +10,7 @@ from rich.console import Console
|
|
|
8
10
|
from rich.logging import RichHandler
|
|
9
11
|
|
|
10
12
|
import semantic_release
|
|
11
|
-
from semantic_release.cli.
|
|
13
|
+
from semantic_release.cli.cli_context import CliContextObj
|
|
12
14
|
from semantic_release.cli.config import GlobalCommandLineOptions
|
|
13
15
|
from semantic_release.cli.const import DEFAULT_CONFIG_FILE
|
|
14
16
|
from semantic_release.cli.util import rprint
|
|
@@ -20,7 +22,35 @@ from semantic_release.cli.util import rprint
|
|
|
20
22
|
FORMAT = "[%(name)s] %(levelname)s %(module)s.%(funcName)s: %(message)s"
|
|
21
23
|
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
class Cli(click.MultiCommand):
|
|
26
|
+
"""Root MultiCommand for the semantic-release CLI"""
|
|
27
|
+
|
|
28
|
+
class SubCmds(Enum):
|
|
29
|
+
"""Subcommand import definitions"""
|
|
30
|
+
|
|
31
|
+
# SUBCMD_FUNCTION_NAME => MODULE_WITH_FUNCTION
|
|
32
|
+
CHANGELOG = f"{__package__}.changelog"
|
|
33
|
+
GENERATE_CONFIG = f"{__package__}.generate_config"
|
|
34
|
+
VERSION = f"{__package__}.version"
|
|
35
|
+
PUBLISH = f"{__package__}.publish"
|
|
36
|
+
|
|
37
|
+
def list_commands(self, _ctx: click.Context) -> list[str]:
|
|
38
|
+
# Used for shell-completion
|
|
39
|
+
return [subcmd.lower().replace("_", "-") for subcmd in Cli.SubCmds.__members__]
|
|
40
|
+
|
|
41
|
+
def get_command(self, _ctx: click.Context, name: str) -> click.Command | None:
|
|
42
|
+
subcmd_name = name.lower().replace("-", "_")
|
|
43
|
+
try:
|
|
44
|
+
subcmd_def: Cli.SubCmds = Cli.SubCmds.__dict__[subcmd_name.upper()]
|
|
45
|
+
module_path = subcmd_def.value
|
|
46
|
+
subcmd_module = importlib.import_module(module_path)
|
|
47
|
+
return getattr(subcmd_module, subcmd_name)
|
|
48
|
+
except (KeyError, ModuleNotFoundError, AttributeError):
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@click.command(
|
|
53
|
+
cls=Cli,
|
|
24
54
|
context_settings={
|
|
25
55
|
"help_option_names": ["-h", "--help"],
|
|
26
56
|
},
|