python-semantic-release 9.8.9__py3-none-any.whl → 9.10.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-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/METADATA +1 -1
- {python_semantic_release-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/RECORD +26 -20
- semantic_release/__init__.py +1 -1
- semantic_release/changelog/context.py +32 -3
- semantic_release/changelog/template.py +29 -8
- semantic_release/cli/changelog_writer.py +124 -36
- semantic_release/cli/commands/changelog.py +4 -1
- semantic_release/cli/commands/version.py +1 -0
- semantic_release/cli/config.py +45 -3
- semantic_release/cli/const.py +4 -0
- semantic_release/cli/github_actions_output.py +10 -7
- semantic_release/commit_parser/angular.py +11 -3
- semantic_release/data/templates/angular/md/.changelog_header.md.j2 +9 -0
- semantic_release/data/templates/angular/md/.changelog_init.md.j2 +22 -0
- semantic_release/data/templates/angular/md/.changelog_update.md.j2 +62 -0
- semantic_release/data/templates/angular/md/.changes.md.j2 +16 -0
- semantic_release/data/templates/angular/md/.unreleased_changes.md.j2 +7 -0
- semantic_release/data/templates/angular/md/.versioned_changes.md.j2 +14 -0
- semantic_release/data/templates/angular/md/CHANGELOG.md.j2 +24 -0
- semantic_release/data/templates/angular/release_notes.md.j2 +1 -0
- semantic_release/errors.py +4 -0
- semantic_release/data/templates/CHANGELOG.md.j2 +0 -21
- semantic_release/data/templates/release_notes.md.j2 +0 -8
- {python_semantic_release-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/AUTHORS.rst +0 -0
- {python_semantic_release-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/LICENSE +0 -0
- {python_semantic_release-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/WHEEL +0 -0
- {python_semantic_release-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/entry_points.txt +0 -0
- {python_semantic_release-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/top_level.txt +0 -0
|
@@ -1,38 +1,44 @@
|
|
|
1
|
-
semantic_release/__init__.py,sha256
|
|
1
|
+
semantic_release/__init__.py,sha256=C5zv3UkZdzyn-xzqjsl6ltjgVx_HcwOtrPym3F0BF5I,1229
|
|
2
2
|
semantic_release/__main__.py,sha256=blPn7CMpuSe4Z-GBIDv0QA0OEDTMGU-7edsLRkkhPR4,120
|
|
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=
|
|
5
|
+
semantic_release/errors.py,sha256=rco5-lwz_0JbJrDsQWmTvT_l3bA3HFkeTmBiZfvCx-Q,2799
|
|
6
6
|
semantic_release/gitproject.py,sha256=izWc4NLdUzAwxGG_fJeqqHW9ivSrPcWBzSaOijQx4f8,8564
|
|
7
7
|
semantic_release/helpers.py,sha256=d1jOX0SNyqPc_3wr14xR25FfpqhMd4Ev7MNBOWlScc0,5581
|
|
8
8
|
semantic_release/changelog/__init__.py,sha256=Bg6Xe5Vt32rWoMscW-hd4sUwiZqzWmsg4CD1EhMesMY,262
|
|
9
|
-
semantic_release/changelog/context.py,sha256=
|
|
9
|
+
semantic_release/changelog/context.py,sha256=zvkjQ9qv5Jk1IPZOnPPh1spM9AiWVYctJfMbI8L_qaI,2403
|
|
10
10
|
semantic_release/changelog/release_history.py,sha256=9gJxqOKPF9HjmOdXW-k-dWbe2YGWxqZEOWSSef-qwoE,7050
|
|
11
|
-
semantic_release/changelog/template.py,sha256=
|
|
11
|
+
semantic_release/changelog/template.py,sha256=FfA5-6sWh3bYQ5sEK4fR414_WNxm3TPUXghqak_9eyk,5989
|
|
12
12
|
semantic_release/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
semantic_release/cli/changelog_writer.py,sha256=
|
|
13
|
+
semantic_release/cli/changelog_writer.py,sha256=tvUtUFfxP4mKHLfWiC2yGrdCNMtUHQrpFxfdNpGTam4,8630
|
|
14
14
|
semantic_release/cli/cli_context.py,sha256=23eyV6KWIpUckUSWILEd7t9dixp-QCY5-kGZnyucEYY,4114
|
|
15
|
-
semantic_release/cli/config.py,sha256=
|
|
16
|
-
semantic_release/cli/const.py,sha256=
|
|
17
|
-
semantic_release/cli/github_actions_output.py,sha256=
|
|
15
|
+
semantic_release/cli/config.py,sha256=wu_ytryKW9jOAb649pkjlrAfwlLDuE83GPODtdmXHpA,26632
|
|
16
|
+
semantic_release/cli/const.py,sha256=haTpNlJifrtbwICwF1u0MNyB0kDsqGUsfM2zwuDu4RM,162
|
|
17
|
+
semantic_release/cli/github_actions_output.py,sha256=VYIOb5x5h8eEJdSlXM_mkhT9xXtYi-RgxvnoM7iUn8s,2288
|
|
18
18
|
semantic_release/cli/masking_filter.py,sha256=DxqjiJyABlzwwwZ1r8JGQpb6QrF00StJFm0-2-s5Fv0,3071
|
|
19
19
|
semantic_release/cli/util.py,sha256=FyXaBkeL7nXKjy3X9rQLEwvn7p46xPekp2V8Z-5MVrk,3755
|
|
20
20
|
semantic_release/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
semantic_release/cli/commands/changelog.py,sha256=
|
|
21
|
+
semantic_release/cli/commands/changelog.py,sha256=kVHcGdfud74-M6hjWf1PS6l95gD4yUlu3CZGBkCt7aY,3765
|
|
22
22
|
semantic_release/cli/commands/generate_config.py,sha256=2xZOu3NpyhBp0pWr7d8ugKl_kjqQgpSsSMHq5wHTfrE,1699
|
|
23
23
|
semantic_release/cli/commands/main.py,sha256=kqO8bZw9Nv6T5QkIl53zzmH2rd4LuLoA8agweH0VYPY,4022
|
|
24
24
|
semantic_release/cli/commands/publish.py,sha256=SZQlIewvqyIC14dkIIVVFetE0tPsKbO1cUyxnZsicrw,2845
|
|
25
|
-
semantic_release/cli/commands/version.py,sha256=
|
|
25
|
+
semantic_release/cli/commands/version.py,sha256=4ZfF2zbwnSNlVewfyXI1UrlvxJJFSI3A3xGo6Vg8CC0,24270
|
|
26
26
|
semantic_release/commit_parser/__init__.py,sha256=cv5HFBdw7OJd4Laj4Ex8ZZ5Tml8GwXgQcXW6Pasr2Ao,615
|
|
27
27
|
semantic_release/commit_parser/_base.py,sha256=t-Z9ALgAe7aZpYXz1mk3Fe-uAvipgKdNrq4Okg_WW9c,3026
|
|
28
|
-
semantic_release/commit_parser/angular.py,sha256=
|
|
28
|
+
semantic_release/commit_parser/angular.py,sha256=AbclYVP_aX_WKpwBCN7SCyJ3-aYf-SVOT4O5TOBLVwI,4857
|
|
29
29
|
semantic_release/commit_parser/emoji.py,sha256=M6zgqsXqegLK5FxZGyfsOPM3vOsJkPKVAjnGATOhwa4,3452
|
|
30
30
|
semantic_release/commit_parser/scipy.py,sha256=AH_GeMZ9hWiNQTL78bovpQy7mBeobGpUk5Shzyxxm6c,5951
|
|
31
31
|
semantic_release/commit_parser/tag.py,sha256=SAop9INQvKOwktNmVypKFngd6xHmY_XZrLUWChUjz84,3353
|
|
32
32
|
semantic_release/commit_parser/token.py,sha256=BB4ZCyt753CCaBFF95cQ4sFm6Au96wpO0YyTAWdcOvE,1609
|
|
33
33
|
semantic_release/commit_parser/util.py,sha256=vLcVDErZrExM55jMffos0hyMbNVQoJ-PeeVDG1Ej51I,730
|
|
34
|
-
semantic_release/data/templates/
|
|
35
|
-
semantic_release/data/templates/
|
|
34
|
+
semantic_release/data/templates/angular/release_notes.md.j2,sha256=ftg0pFkmGYlb9UH2waSLc6neC8rPMMCqblJaQeq4vTg,46
|
|
35
|
+
semantic_release/data/templates/angular/md/.changelog_header.md.j2,sha256=qNxTuSr59CV_yyimVU_RYp5azCnK0l6nJ03Zf0u5Ugg,166
|
|
36
|
+
semantic_release/data/templates/angular/md/.changelog_init.md.j2,sha256=mIV6AuZmB6BGl_hBDGM9ea_CG5BV7HqI3Ko9jsn7zMs,602
|
|
37
|
+
semantic_release/data/templates/angular/md/.changelog_update.md.j2,sha256=DHY4K_EQPXGHNHVbsVI5l3x_IWNtYq3IyHFyFsNnLe0,2387
|
|
38
|
+
semantic_release/data/templates/angular/md/.changes.md.j2,sha256=oC2amekjxsNjgj0BkVzs6ANswxntOi3ItlQd9VtbVm8,339
|
|
39
|
+
semantic_release/data/templates/angular/md/.unreleased_changes.md.j2,sha256=8CGiPvJEzrjNyqdDFjRGNlLpjIFGiMccPKPmi-vdGTI,178
|
|
40
|
+
semantic_release/data/templates/angular/md/.versioned_changes.md.j2,sha256=K2J5GHTBUffPxZ-FSoewHkwNJtuPidZA83hEGtpk7rg,258
|
|
41
|
+
semantic_release/data/templates/angular/md/CHANGELOG.md.j2,sha256=LjSFwb3QROfj3PZY-2qeMBSlbnP7vf2L9Qc_4-FX68Y,816
|
|
36
42
|
semantic_release/hvcs/__init__.py,sha256=JwoaLOF-12L-OBo_9-tOXXhdiHKeVungA9865to2oZk,494
|
|
37
43
|
semantic_release/hvcs/_base.py,sha256=9-iTqTPSbiEevKbCBP9K2hq4c-2T4wPbeLWe-kAxBzo,2607
|
|
38
44
|
semantic_release/hvcs/bitbucket.py,sha256=nqlOmeNda0sRSEBGWMluphy1KlpRTQrHV7itxf0IXE0,9266
|
|
@@ -47,10 +53,10 @@ semantic_release/version/algorithm.py,sha256=ofx_bIWq6ptJVr-ekI11IzxzDEctDKFiVwa
|
|
|
47
53
|
semantic_release/version/declaration.py,sha256=f6Ld7hIhrqvDrRBapJHr-KDimuyo-4IG8009Zu9BIgU,7357
|
|
48
54
|
semantic_release/version/translator.py,sha256=P1noIsVBn8u6zNOFjG0xKYOWapxqf_PHSMvMeLJ9kXg,3050
|
|
49
55
|
semantic_release/version/version.py,sha256=6PCtSbLP88U1daoxnCwHc--YguZo4waGNLqJ5JfeczE,14175
|
|
50
|
-
python_semantic_release-9.
|
|
51
|
-
python_semantic_release-9.
|
|
52
|
-
python_semantic_release-9.
|
|
53
|
-
python_semantic_release-9.
|
|
54
|
-
python_semantic_release-9.
|
|
55
|
-
python_semantic_release-9.
|
|
56
|
-
python_semantic_release-9.
|
|
56
|
+
python_semantic_release-9.10.0.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
|
|
57
|
+
python_semantic_release-9.10.0.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
|
|
58
|
+
python_semantic_release-9.10.0.dist-info/METADATA,sha256=Oa_kotAU8xlkMsUejoXJQ2XEJwoPWw4HBWPhTtx-tEM,3521
|
|
59
|
+
python_semantic_release-9.10.0.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
|
60
|
+
python_semantic_release-9.10.0.dist-info/entry_points.txt,sha256=6bS6euSagjerp7onDtfI9_ZpczWreZF3gjHfGAegkbo,123
|
|
61
|
+
python_semantic_release-9.10.0.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
|
|
62
|
+
python_semantic_release-9.10.0.dist-info/RECORD,,
|
semantic_release/__init__.py
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
3
5
|
from dataclasses import dataclass
|
|
4
|
-
from
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable, Literal
|
|
5
9
|
|
|
6
10
|
if TYPE_CHECKING:
|
|
7
11
|
from jinja2 import Environment
|
|
@@ -34,28 +38,53 @@ class ReleaseNotesContext:
|
|
|
34
38
|
return env
|
|
35
39
|
|
|
36
40
|
|
|
41
|
+
class ChangelogMode(Enum):
|
|
42
|
+
INIT = "init"
|
|
43
|
+
UPDATE = "update"
|
|
44
|
+
|
|
45
|
+
|
|
37
46
|
@dataclass
|
|
38
47
|
class ChangelogContext:
|
|
39
48
|
repo_name: str
|
|
40
49
|
repo_owner: str
|
|
41
50
|
hvcs_type: str
|
|
42
51
|
history: ReleaseHistory
|
|
52
|
+
changelog_mode: Literal["update", "init"]
|
|
53
|
+
prev_changelog_file: str
|
|
54
|
+
changelog_insertion_flag: str
|
|
43
55
|
filters: tuple[Callable[..., Any], ...] = ()
|
|
44
56
|
|
|
45
57
|
def bind_to_environment(self, env: Environment) -> Environment:
|
|
46
58
|
env.globals["context"] = self
|
|
59
|
+
env.globals["ctx"] = self
|
|
47
60
|
for f in self.filters:
|
|
48
61
|
env.filters[f.__name__] = f
|
|
49
62
|
return env
|
|
50
63
|
|
|
51
64
|
|
|
52
65
|
def make_changelog_context(
|
|
53
|
-
hvcs_client: HvcsBase,
|
|
66
|
+
hvcs_client: HvcsBase,
|
|
67
|
+
release_history: ReleaseHistory,
|
|
68
|
+
mode: ChangelogMode,
|
|
69
|
+
prev_changelog_file: Path,
|
|
70
|
+
insertion_flag: str,
|
|
54
71
|
) -> ChangelogContext:
|
|
55
72
|
return ChangelogContext(
|
|
56
73
|
repo_name=hvcs_client.repo_name,
|
|
57
74
|
repo_owner=hvcs_client.owner,
|
|
58
75
|
history=release_history,
|
|
76
|
+
changelog_mode=mode.value,
|
|
77
|
+
changelog_insertion_flag=insertion_flag,
|
|
78
|
+
prev_changelog_file=str(prev_changelog_file),
|
|
59
79
|
hvcs_type=hvcs_client.__class__.__name__.lower(),
|
|
60
|
-
filters=(*hvcs_client.get_changelog_context_filters(),),
|
|
80
|
+
filters=(*hvcs_client.get_changelog_context_filters(), read_file),
|
|
61
81
|
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def read_file(filepath: str) -> str:
|
|
85
|
+
try:
|
|
86
|
+
with Path(filepath).open(newline=os.linesep) as rfd:
|
|
87
|
+
return rfd.read()
|
|
88
|
+
except FileNotFoundError as err:
|
|
89
|
+
logging.warning(err)
|
|
90
|
+
return ""
|
|
@@ -3,8 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
5
|
import shutil
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
6
|
+
from pathlib import Path, PurePosixPath
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
9
9
|
from jinja2 import FileSystemLoader
|
|
10
10
|
from jinja2.sandbox import SandboxedEnvironment
|
|
@@ -12,7 +12,7 @@ from jinja2.sandbox import SandboxedEnvironment
|
|
|
12
12
|
from semantic_release.helpers import dynamic_import
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
|
-
from typing import Literal
|
|
15
|
+
from typing import Callable, Iterable, Literal
|
|
16
16
|
|
|
17
17
|
from jinja2 import Environment
|
|
18
18
|
|
|
@@ -56,7 +56,7 @@ def environment(
|
|
|
56
56
|
autoescape_value = autoescape
|
|
57
57
|
log.debug("%s", locals())
|
|
58
58
|
|
|
59
|
-
return
|
|
59
|
+
return ComplexDirectorySandboxedEnvironment(
|
|
60
60
|
block_start_string=block_start_string,
|
|
61
61
|
block_end_string=block_end_string,
|
|
62
62
|
variable_start_string=variable_start_string,
|
|
@@ -75,6 +75,23 @@ def environment(
|
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
class ComplexDirectorySandboxedEnvironment(SandboxedEnvironment):
|
|
79
|
+
def join_path(self, template: str, parent: str) -> str:
|
|
80
|
+
"""
|
|
81
|
+
Add support for complex directory structures in the template directory.
|
|
82
|
+
|
|
83
|
+
This method overrides the default functionality of the SandboxedEnvironment
|
|
84
|
+
where all 'include' keywords expect to be in the same directory as the calling
|
|
85
|
+
template, however this is unintuitive when using a complex directory structure.
|
|
86
|
+
|
|
87
|
+
This override simulates the changing of directories when you include the template
|
|
88
|
+
from a child directory. When the child then includes a template, it will make the
|
|
89
|
+
path relative to the child directory rather than the top level template directory.
|
|
90
|
+
"""
|
|
91
|
+
# Must be posixpath because jinja only knows how to handle posix path includes
|
|
92
|
+
return str(PurePosixPath(parent).parent / template)
|
|
93
|
+
|
|
94
|
+
|
|
78
95
|
# pylint: disable=redefined-outer-name
|
|
79
96
|
def recursive_render(
|
|
80
97
|
template_dir: Path,
|
|
@@ -107,11 +124,15 @@ def recursive_render(
|
|
|
107
124
|
src_file_path = str((root / file).relative_to(template_dir))
|
|
108
125
|
output_file_path = str((output_path / output_filename).resolve())
|
|
109
126
|
|
|
127
|
+
# Although, file stream rendering is possible and preferred in most
|
|
128
|
+
# situations, here it is not desired as you cannot read the previous
|
|
129
|
+
# contents of a file during the rendering of the template. This mechanism
|
|
130
|
+
# is used for inserting into a current changelog. When using stream rendering
|
|
131
|
+
# of the same file, it always came back empty
|
|
110
132
|
log.debug("rendering %s to %s", src_file_path, output_file_path)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
stream.dump(output_file, encoding="utf-8")
|
|
133
|
+
rendered_file = environment.get_template(src_file_path).render()
|
|
134
|
+
with open(output_file_path, "w", encoding="utf-8") as output_file:
|
|
135
|
+
output_file.write(rendered_file)
|
|
115
136
|
|
|
116
137
|
rendered_paths.append(output_file_path)
|
|
117
138
|
else:
|
|
@@ -1,60 +1,108 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import os
|
|
4
|
+
from contextlib import suppress
|
|
3
5
|
from logging import getLogger
|
|
4
|
-
from os import listdir
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import TYPE_CHECKING
|
|
7
8
|
|
|
8
9
|
# NOTE: use backport with newer API than stdlib
|
|
9
10
|
from importlib_resources import files
|
|
10
11
|
|
|
12
|
+
import semantic_release
|
|
11
13
|
from semantic_release.changelog.context import (
|
|
12
14
|
ReleaseNotesContext,
|
|
13
15
|
make_changelog_context,
|
|
14
16
|
)
|
|
15
17
|
from semantic_release.changelog.template import environment, recursive_render
|
|
18
|
+
from semantic_release.cli.const import (
|
|
19
|
+
DEFAULT_CHANGELOG_NAME_STEM,
|
|
20
|
+
DEFAULT_RELEASE_NOTES_TPL_FILE,
|
|
21
|
+
JINJA2_EXTENSION,
|
|
22
|
+
)
|
|
16
23
|
from semantic_release.cli.util import noop_report
|
|
24
|
+
from semantic_release.errors import InternalError
|
|
17
25
|
|
|
18
26
|
if TYPE_CHECKING:
|
|
19
27
|
from jinja2 import Environment
|
|
20
28
|
|
|
29
|
+
from semantic_release.changelog.context import ChangelogContext
|
|
21
30
|
from semantic_release.changelog.release_history import Release, ReleaseHistory
|
|
22
|
-
from semantic_release.cli.config import RuntimeContext
|
|
31
|
+
from semantic_release.cli.config import ChangelogOutputFormat, RuntimeContext
|
|
23
32
|
from semantic_release.hvcs._base import HvcsBase
|
|
24
33
|
|
|
25
34
|
|
|
26
35
|
log = getLogger(__name__)
|
|
27
36
|
|
|
28
37
|
|
|
29
|
-
def
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
def get_default_tpl_dir(style: str, sub_dir: str | None = None) -> Path:
|
|
39
|
+
module_base_path = Path(str(files(semantic_release.__name__)))
|
|
40
|
+
default_templates_path = module_base_path.joinpath(
|
|
41
|
+
f"data/templates/{style}",
|
|
42
|
+
"" if sub_dir is None else sub_dir.strip("/"),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if default_templates_path.is_dir():
|
|
46
|
+
return default_templates_path
|
|
47
|
+
|
|
48
|
+
raise InternalError(
|
|
49
|
+
str.join(
|
|
50
|
+
" ",
|
|
51
|
+
[
|
|
52
|
+
"Default template directory not found at",
|
|
53
|
+
f"{default_templates_path}. Installation corrupted!",
|
|
54
|
+
],
|
|
39
55
|
)
|
|
56
|
+
)
|
|
40
57
|
|
|
41
58
|
|
|
42
|
-
def render_default_changelog_file(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
def render_default_changelog_file(
|
|
60
|
+
output_format: ChangelogOutputFormat,
|
|
61
|
+
changelog_context: ChangelogContext,
|
|
62
|
+
changelog_style: str,
|
|
63
|
+
) -> str:
|
|
64
|
+
tpl_dir = get_default_tpl_dir(style=changelog_style, sub_dir=output_format.value)
|
|
65
|
+
changelog_tpl_file = Path(DEFAULT_CHANGELOG_NAME_STEM).with_suffix(
|
|
66
|
+
str.join(".", ["", output_format.value, JINJA2_EXTENSION.lstrip(".")])
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Create a new environment as we don't want user's configuration as it might
|
|
70
|
+
# not match our default template structure
|
|
71
|
+
template_env = changelog_context.bind_to_environment(
|
|
72
|
+
environment(
|
|
73
|
+
autoescape=False,
|
|
74
|
+
newline_sequence="\n",
|
|
75
|
+
template_dir=tpl_dir,
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Using the proper enviroment with the changelog context, render the template
|
|
80
|
+
template = template_env.get_template(str(changelog_tpl_file))
|
|
81
|
+
changelog_content = template.render().rstrip()
|
|
82
|
+
|
|
83
|
+
# Normalize line endings to ensure universal newlines because that is what is expected
|
|
84
|
+
# of the content when we write it to a file. When using pathlib.Path.write_text(), it
|
|
85
|
+
# will automatically normalize the file to the OS. At this point after render, we may
|
|
86
|
+
# have mixed line endings because of the read_file() call of the previous changelog
|
|
87
|
+
# (which may be /r/n or /n)
|
|
88
|
+
return str.join(
|
|
89
|
+
"\n", [line.replace("\r", "") for line in changelog_content.split("\n")]
|
|
47
90
|
)
|
|
48
|
-
template = template_env.from_string(changelog_text)
|
|
49
|
-
return template.render().rstrip()
|
|
50
91
|
|
|
51
92
|
|
|
52
93
|
def render_release_notes(
|
|
53
|
-
|
|
94
|
+
release_notes_template_file: str,
|
|
54
95
|
template_env: Environment,
|
|
55
96
|
) -> str:
|
|
56
|
-
template
|
|
57
|
-
|
|
97
|
+
# NOTE: release_notes_template_file must be a relative path to the template directory
|
|
98
|
+
# because jinja2's filtering and template loading filter is janky
|
|
99
|
+
template = template_env.get_template(release_notes_template_file)
|
|
100
|
+
release_notes = template.render().rstrip() + os.linesep
|
|
101
|
+
|
|
102
|
+
# Normalize line endings to match the current platform
|
|
103
|
+
return str.join(
|
|
104
|
+
os.linesep, [line.replace("\r", "") for line in release_notes.split("\n")]
|
|
105
|
+
)
|
|
58
106
|
|
|
59
107
|
|
|
60
108
|
def apply_user_changelog_template_directory(
|
|
@@ -85,7 +133,9 @@ def apply_user_changelog_template_directory(
|
|
|
85
133
|
def write_default_changelog(
|
|
86
134
|
changelog_file: Path,
|
|
87
135
|
destination_dir: Path,
|
|
88
|
-
|
|
136
|
+
output_format: ChangelogOutputFormat,
|
|
137
|
+
changelog_context: ChangelogContext,
|
|
138
|
+
changelog_style: str,
|
|
89
139
|
noop: bool = False,
|
|
90
140
|
) -> str:
|
|
91
141
|
if noop:
|
|
@@ -98,9 +148,15 @@ def write_default_changelog(
|
|
|
98
148
|
],
|
|
99
149
|
)
|
|
100
150
|
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
151
|
+
return str(changelog_file)
|
|
152
|
+
|
|
153
|
+
changelog_text = render_default_changelog_file(
|
|
154
|
+
output_format=output_format,
|
|
155
|
+
changelog_context=changelog_context,
|
|
156
|
+
changelog_style=changelog_style,
|
|
157
|
+
)
|
|
158
|
+
# write_text() will automatically normalize newlines to the OS, so we just use an universal newline here
|
|
159
|
+
changelog_file.write_text(f"{changelog_text}\n", encoding="utf-8")
|
|
104
160
|
|
|
105
161
|
return str(changelog_file)
|
|
106
162
|
|
|
@@ -117,19 +173,34 @@ def write_changelog_files(
|
|
|
117
173
|
changelog_context = make_changelog_context(
|
|
118
174
|
hvcs_client=hvcs_client,
|
|
119
175
|
release_history=release_history,
|
|
176
|
+
mode=runtime_ctx.changelog_mode,
|
|
177
|
+
insertion_flag=runtime_ctx.changelog_insertion_flag,
|
|
178
|
+
prev_changelog_file=runtime_ctx.changelog_file,
|
|
120
179
|
)
|
|
121
180
|
|
|
122
|
-
|
|
181
|
+
user_templates = []
|
|
123
182
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
183
|
+
# Update known templates list if Directory exists and directory has actual files to render
|
|
184
|
+
if template_dir.is_dir():
|
|
185
|
+
user_templates.extend(
|
|
186
|
+
[
|
|
187
|
+
f
|
|
188
|
+
for f in template_dir.rglob("*")
|
|
189
|
+
if f.is_file() and f.suffix == JINJA2_EXTENSION
|
|
190
|
+
]
|
|
191
|
+
)
|
|
128
192
|
|
|
129
|
-
|
|
193
|
+
with suppress(ValueError):
|
|
194
|
+
# do not include a release notes override when considering number of changelog templates
|
|
195
|
+
user_templates.remove(template_dir / f".{DEFAULT_RELEASE_NOTES_TPL_FILE}")
|
|
196
|
+
|
|
197
|
+
# Render user templates if found
|
|
198
|
+
if len(user_templates) > 0:
|
|
130
199
|
return apply_user_changelog_template_directory(
|
|
131
200
|
template_dir=template_dir,
|
|
132
|
-
environment=
|
|
201
|
+
environment=changelog_context.bind_to_environment(
|
|
202
|
+
runtime_ctx.template_environment
|
|
203
|
+
),
|
|
133
204
|
destination_dir=project_dir,
|
|
134
205
|
noop=noop,
|
|
135
206
|
)
|
|
@@ -139,7 +210,9 @@ def write_changelog_files(
|
|
|
139
210
|
write_default_changelog(
|
|
140
211
|
changelog_file=runtime_ctx.changelog_file,
|
|
141
212
|
destination_dir=project_dir,
|
|
142
|
-
|
|
213
|
+
output_format=runtime_ctx.changelog_output_format,
|
|
214
|
+
changelog_context=changelog_context,
|
|
215
|
+
changelog_style=runtime_ctx.changelog_style,
|
|
143
216
|
noop=noop,
|
|
144
217
|
)
|
|
145
218
|
]
|
|
@@ -150,7 +223,22 @@ def generate_release_notes(
|
|
|
150
223
|
release: Release,
|
|
151
224
|
template_dir: Path,
|
|
152
225
|
history: ReleaseHistory,
|
|
226
|
+
style: str,
|
|
153
227
|
) -> str:
|
|
228
|
+
users_tpl_file = template_dir / f".{DEFAULT_RELEASE_NOTES_TPL_FILE}"
|
|
229
|
+
|
|
230
|
+
# Determine if the user has a custom release notes template or we should use
|
|
231
|
+
# the default template directory with our default release notes template
|
|
232
|
+
tpl_dir = (
|
|
233
|
+
template_dir if users_tpl_file.is_file() else get_default_tpl_dir(style=style)
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
release_notes_tpl_file = (
|
|
237
|
+
users_tpl_file.name
|
|
238
|
+
if users_tpl_file.is_file()
|
|
239
|
+
else DEFAULT_RELEASE_NOTES_TPL_FILE
|
|
240
|
+
)
|
|
241
|
+
|
|
154
242
|
release_notes_env = ReleaseNotesContext(
|
|
155
243
|
repo_name=hvcs_client.repo_name,
|
|
156
244
|
repo_owner=hvcs_client.owner,
|
|
@@ -161,7 +249,7 @@ def generate_release_notes(
|
|
|
161
249
|
).bind_to_environment(
|
|
162
250
|
# Use a new, non-configurable environment for release notes -
|
|
163
251
|
# not user-configurable at the moment
|
|
164
|
-
environment(template_dir=
|
|
252
|
+
environment(autoescape=False, template_dir=tpl_dir)
|
|
165
253
|
)
|
|
166
254
|
|
|
167
255
|
# TODO: Remove in v10
|
|
@@ -170,6 +258,6 @@ def generate_release_notes(
|
|
|
170
258
|
}
|
|
171
259
|
|
|
172
260
|
return render_release_notes(
|
|
173
|
-
|
|
261
|
+
release_notes_template_file=release_notes_tpl_file,
|
|
174
262
|
template_env=release_notes_env,
|
|
175
263
|
)
|
|
@@ -43,7 +43,9 @@ def post_release_notes(
|
|
|
43
43
|
return
|
|
44
44
|
|
|
45
45
|
hvcs_client.create_or_update_release(
|
|
46
|
-
release_tag,
|
|
46
|
+
release_tag,
|
|
47
|
+
release_notes,
|
|
48
|
+
prerelease=prerelease,
|
|
47
49
|
)
|
|
48
50
|
|
|
49
51
|
|
|
@@ -116,6 +118,7 @@ def changelog(cli_ctx: CliContextObj, release_tag: str | None) -> None:
|
|
|
116
118
|
release,
|
|
117
119
|
runtime.template_dir,
|
|
118
120
|
release_history,
|
|
121
|
+
style=runtime.changelog_style,
|
|
119
122
|
)
|
|
120
123
|
|
|
121
124
|
try:
|
semantic_release/cli/config.py
CHANGED
|
@@ -27,6 +27,7 @@ from urllib3.util.url import parse_url
|
|
|
27
27
|
|
|
28
28
|
from semantic_release import hvcs
|
|
29
29
|
from semantic_release.changelog import environment
|
|
30
|
+
from semantic_release.changelog.context import ChangelogMode
|
|
30
31
|
from semantic_release.cli.const import DEFAULT_CONFIG_FILE
|
|
31
32
|
from semantic_release.cli.masking_filter import MaskingFilter
|
|
32
33
|
from semantic_release.commit_parser import (
|
|
@@ -94,6 +95,13 @@ class EnvConfigVar(BaseModel):
|
|
|
94
95
|
MaybeFromEnv = Union[EnvConfigVar, str]
|
|
95
96
|
|
|
96
97
|
|
|
98
|
+
class ChangelogOutputFormat(str, Enum):
|
|
99
|
+
"""Supported changelog output formats when using the default templates."""
|
|
100
|
+
|
|
101
|
+
MARKDOWN = "md"
|
|
102
|
+
# RESTRUCTURED_TEXT = "rst"
|
|
103
|
+
|
|
104
|
+
|
|
97
105
|
class ChangelogEnvironmentConfig(BaseModel):
|
|
98
106
|
block_start_string: str = "{%"
|
|
99
107
|
block_end_string: str = "%}"
|
|
@@ -108,14 +116,26 @@ class ChangelogEnvironmentConfig(BaseModel):
|
|
|
108
116
|
newline_sequence: Literal["\n", "\r", "\r\n"] = "\n"
|
|
109
117
|
keep_trailing_newline: bool = False
|
|
110
118
|
extensions: Tuple[str, ...] = ()
|
|
111
|
-
autoescape: Union[bool, str] =
|
|
119
|
+
autoescape: Union[bool, str] = False
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class DefaultChangelogTemplatesConfig(BaseModel):
|
|
123
|
+
# TODO: BREAKING CHANGE v10
|
|
124
|
+
# changelog_file: str = "CHANGELOG.md"
|
|
125
|
+
output_format: ChangelogOutputFormat = ChangelogOutputFormat.MARKDOWN
|
|
112
126
|
|
|
113
127
|
|
|
114
128
|
class ChangelogConfig(BaseModel):
|
|
115
|
-
|
|
129
|
+
# TODO: BREAKING CHANGE v10, move to DefaultChangelogTemplatesConfig
|
|
116
130
|
changelog_file: str = "CHANGELOG.md"
|
|
117
|
-
|
|
131
|
+
default_templates: DefaultChangelogTemplatesConfig = (
|
|
132
|
+
DefaultChangelogTemplatesConfig()
|
|
133
|
+
)
|
|
118
134
|
environment: ChangelogEnvironmentConfig = ChangelogEnvironmentConfig()
|
|
135
|
+
exclude_commit_patterns: Tuple[str, ...] = ()
|
|
136
|
+
mode: ChangelogMode = ChangelogMode.INIT
|
|
137
|
+
insertion_flag: str = "<!-- version list -->"
|
|
138
|
+
template_dir: str = "templates"
|
|
119
139
|
|
|
120
140
|
|
|
121
141
|
class BranchConfig(BaseModel):
|
|
@@ -386,7 +406,11 @@ class RuntimeContext:
|
|
|
386
406
|
changelog_excluded_commit_patterns: Tuple[re.Pattern[str], ...]
|
|
387
407
|
version_declarations: Tuple[VersionDeclarationABC, ...]
|
|
388
408
|
hvcs_client: hvcs.HvcsBase
|
|
409
|
+
changelog_insertion_flag: str
|
|
410
|
+
changelog_mode: ChangelogMode
|
|
389
411
|
changelog_file: Path
|
|
412
|
+
changelog_style: str
|
|
413
|
+
changelog_output_format: ChangelogOutputFormat
|
|
390
414
|
ignore_token_for_push: bool
|
|
391
415
|
template_environment: Environment
|
|
392
416
|
template_dir: Path
|
|
@@ -640,6 +664,18 @@ class RuntimeContext:
|
|
|
640
664
|
|
|
641
665
|
build_cmd_env[name] = env_val
|
|
642
666
|
|
|
667
|
+
# TODO: better support for custom parsers that actually just extend defaults
|
|
668
|
+
#
|
|
669
|
+
# Here we just assume the desired changelog style matches the parser name
|
|
670
|
+
# as we provide templates specific to each parser type. Unfortunately if the user has
|
|
671
|
+
# provided a custom parser, it would be up to the user to provide custom templates
|
|
672
|
+
# but we just assume the base template is angular
|
|
673
|
+
# changelog_style = (
|
|
674
|
+
# raw.commit_parser
|
|
675
|
+
# if raw.commit_parser in _known_commit_parsers
|
|
676
|
+
# else "angular"
|
|
677
|
+
# )
|
|
678
|
+
|
|
643
679
|
self = cls(
|
|
644
680
|
repo_dir=raw.repo_dir,
|
|
645
681
|
commit_parser=commit_parser,
|
|
@@ -651,10 +687,16 @@ class RuntimeContext:
|
|
|
651
687
|
version_declarations=tuple(version_declarations),
|
|
652
688
|
hvcs_client=hvcs_client,
|
|
653
689
|
changelog_file=changelog_file,
|
|
690
|
+
changelog_mode=raw.changelog.mode,
|
|
691
|
+
changelog_insertion_flag=raw.changelog.insertion_flag,
|
|
654
692
|
assets=raw.assets,
|
|
655
693
|
commit_author=commit_author,
|
|
656
694
|
commit_message=raw.commit_message,
|
|
657
695
|
changelog_excluded_commit_patterns=changelog_excluded_commit_patterns,
|
|
696
|
+
# TODO: change when we have other styles per parser
|
|
697
|
+
# changelog_style=changelog_style,
|
|
698
|
+
changelog_style="angular",
|
|
699
|
+
changelog_output_format=raw.changelog.default_templates.output_format,
|
|
658
700
|
prerelease=branch_config.prerelease,
|
|
659
701
|
ignore_token_for_push=raw.remote.ignore_token_for_push,
|
|
660
702
|
template_dir=template_dir,
|
semantic_release/cli/const.py
CHANGED
|
@@ -12,7 +12,9 @@ class VersionGitHubActionsOutput:
|
|
|
12
12
|
OUTPUT_ENV_VAR = "GITHUB_OUTPUT"
|
|
13
13
|
|
|
14
14
|
def __init__(
|
|
15
|
-
self,
|
|
15
|
+
self,
|
|
16
|
+
released: bool | None = None,
|
|
17
|
+
version: Version | None = None,
|
|
16
18
|
) -> None:
|
|
17
19
|
self._released = released
|
|
18
20
|
self._version = version
|
|
@@ -29,9 +31,7 @@ class VersionGitHubActionsOutput:
|
|
|
29
31
|
|
|
30
32
|
@property
|
|
31
33
|
def version(self) -> Version | None:
|
|
32
|
-
if self._version is None
|
|
33
|
-
return None
|
|
34
|
-
return self._version
|
|
34
|
+
return self._version if self._version is not None else None
|
|
35
35
|
|
|
36
36
|
@version.setter
|
|
37
37
|
def version(self, value: Version) -> None:
|
|
@@ -41,9 +41,11 @@ class VersionGitHubActionsOutput:
|
|
|
41
41
|
|
|
42
42
|
@property
|
|
43
43
|
def tag(self) -> str | None:
|
|
44
|
-
if self.
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
return self.version.as_tag() if self.version is not None else None
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def is_prerelease(self) -> bool | None:
|
|
48
|
+
return self.version.is_prerelease if self.version is not None else None
|
|
47
49
|
|
|
48
50
|
def to_output_text(self) -> str:
|
|
49
51
|
missing = set()
|
|
@@ -61,6 +63,7 @@ class VersionGitHubActionsOutput:
|
|
|
61
63
|
"released": str(self.released).lower(),
|
|
62
64
|
"version": str(self.version),
|
|
63
65
|
"tag": self.tag,
|
|
66
|
+
"is_prerelease": str(self.is_prerelease).lower(),
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
return str.join("", [f"{key}={value!s}\n" for key, value in outputs.items()])
|
|
@@ -27,11 +27,19 @@ def _logged_parse_error(commit: Commit, error: str) -> ParseError:
|
|
|
27
27
|
return ParseError(commit, error=error)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
# TODO: Remove from here, allow for user customization instead via options
|
|
30
31
|
# types with long names in changelog
|
|
31
32
|
LONG_TYPE_NAMES = {
|
|
32
|
-
"
|
|
33
|
+
"build": "build system",
|
|
34
|
+
"ci": "continuous integration",
|
|
35
|
+
"chore": "chores",
|
|
33
36
|
"docs": "documentation",
|
|
34
|
-
"
|
|
37
|
+
"feat": "features",
|
|
38
|
+
"fix": "fixes",
|
|
39
|
+
"perf": "performance improvements",
|
|
40
|
+
"refactor": "refactoring",
|
|
41
|
+
"style": "code style",
|
|
42
|
+
"test": "testing",
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
|
|
@@ -82,7 +90,7 @@ class AngularCommitParser(CommitParser[ParseResult, AngularParserOptions]):
|
|
|
82
90
|
def get_default_options() -> AngularParserOptions:
|
|
83
91
|
return AngularParserOptions()
|
|
84
92
|
|
|
85
|
-
# Maybe this can be cached as an
|
|
93
|
+
# Maybe this can be cached as an optimization, similar to how
|
|
86
94
|
# mypy/pytest use their own caching directories, for very large commit
|
|
87
95
|
# histories?
|
|
88
96
|
# The problem is the cache likely won't be present in CI environments
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{#
|
|
2
|
+
This changelog template initializes a full changelog for the project,
|
|
3
|
+
it follows the following logic:
|
|
4
|
+
1. Header
|
|
5
|
+
2. Any Unreleased Details (uncommon)
|
|
6
|
+
3. all previous releases
|
|
7
|
+
|
|
8
|
+
#}{#
|
|
9
|
+
# Header
|
|
10
|
+
#}{% include ".changelog_header.md.j2"
|
|
11
|
+
-%}{#
|
|
12
|
+
# Any Unreleased Details (uncommon)
|
|
13
|
+
#}{% include ".unreleased_changes.md.j2"
|
|
14
|
+
-%}{#
|
|
15
|
+
# Since this is initialization, we are generating all the previous
|
|
16
|
+
# release notes per version.
|
|
17
|
+
#}{% for release in context.history.released.values()
|
|
18
|
+
%}{{ "\n"
|
|
19
|
+
}}{% include ".versioned_changes.md.j2"
|
|
20
|
+
-%}{{ "\n"
|
|
21
|
+
}}{% endfor
|
|
22
|
+
%}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{#
|
|
2
|
+
This Update changelog template uses the following logic:
|
|
3
|
+
|
|
4
|
+
1. Read previous changelog file (ex. project_root/CHANGELOG.md)
|
|
5
|
+
2. Split on insertion flag (ex. <!-- version list -->)
|
|
6
|
+
3. Print top half of previous changelog
|
|
7
|
+
3. New Changes (unreleased commits & newly released)
|
|
8
|
+
4. Print bottom half of previous changelog
|
|
9
|
+
|
|
10
|
+
Note: if a previous file was not found, it does not write anything at the bottom
|
|
11
|
+
but render does NOT fail
|
|
12
|
+
|
|
13
|
+
#}{% set prev_changelog_contents = prev_changelog_file | read_file | safe
|
|
14
|
+
%}{% set changelog_parts = prev_changelog_contents.split(insertion_flag, maxsplit=1)
|
|
15
|
+
%}{#
|
|
16
|
+
#}{% if changelog_parts | length < 2
|
|
17
|
+
%}{# # insertion flag was not found, check if the file was empty or did not exist
|
|
18
|
+
#}{% if prev_changelog_contents | length > 0
|
|
19
|
+
%}{# # File has content but no insertion flag, therefore, file will not be updated
|
|
20
|
+
#}{{ changelog_parts[0]
|
|
21
|
+
}}{% else
|
|
22
|
+
%}{# # File was empty or did not exist, therefore, it will be created from scratch
|
|
23
|
+
#}{% include ".changelog_init.md.j2"
|
|
24
|
+
%}{% endif
|
|
25
|
+
%}{% else
|
|
26
|
+
%}{#
|
|
27
|
+
# Previous Changelog Header
|
|
28
|
+
# - Depending if there is header content, then it will separate the insertion flag
|
|
29
|
+
# with a newline from header content, otherwise it will just print the insertion flag
|
|
30
|
+
#}{% set prev_changelog_top = changelog_parts[0] | trim
|
|
31
|
+
%}{% if prev_changelog_top | length > 0
|
|
32
|
+
%}{{
|
|
33
|
+
"%s\n\n%s\n" | format(prev_changelog_top, insertion_flag | trim)
|
|
34
|
+
|
|
35
|
+
}}{% else
|
|
36
|
+
%}{{
|
|
37
|
+
"%s\n" | format(insertion_flag | trim)
|
|
38
|
+
|
|
39
|
+
}}{% endif
|
|
40
|
+
%}{#
|
|
41
|
+
# Any Unreleased Details (uncommon)
|
|
42
|
+
#}{% include ".unreleased_changes.md.j2"
|
|
43
|
+
-%}{#
|
|
44
|
+
# Latest Release Details
|
|
45
|
+
#}{% for release in new_releases
|
|
46
|
+
%}{# # Check if the release version is already in the changelog and if not, add it
|
|
47
|
+
#}{% if "# " ~ release.version.as_semver_tag() ~ " " not in changelog_parts[1]
|
|
48
|
+
%}{{ "\n"
|
|
49
|
+
}}{% include ".versioned_changes.md.j2"
|
|
50
|
+
-%}{{ "\n"
|
|
51
|
+
}}{% endif
|
|
52
|
+
%}{% endfor
|
|
53
|
+
%}{#
|
|
54
|
+
# Previous Changelog Footer
|
|
55
|
+
# - skips printing footer if empty, which happens when the insertion_flag
|
|
56
|
+
# was at the end of the file (ignoring whitespace)
|
|
57
|
+
#}{% set previous_changelog_bottom = changelog_parts[1] | trim
|
|
58
|
+
%}{% if previous_changelog_bottom | length > 0
|
|
59
|
+
%}{{ "\n%s\n" | format(previous_changelog_bottom)
|
|
60
|
+
}}{% endif
|
|
61
|
+
%}{% endif
|
|
62
|
+
%}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{#
|
|
2
|
+
#}{% for type_, commits in commit_objects
|
|
3
|
+
%}{{
|
|
4
|
+
"\n### %s\n" | format(type_ | title)
|
|
5
|
+
|
|
6
|
+
}}{% for commit in commits
|
|
7
|
+
%}{{
|
|
8
|
+
"\n* %s ([`%s`](%s))\n" | format(
|
|
9
|
+
commit.message.rstrip(),
|
|
10
|
+
commit.short_hash,
|
|
11
|
+
commit.hexsha | commit_hash_url,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
}}{% endfor
|
|
15
|
+
%}{% endfor
|
|
16
|
+
%}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{#
|
|
2
|
+
|
|
3
|
+
## vX.X.X (YYYY-MMM-DD)
|
|
4
|
+
|
|
5
|
+
#}{{
|
|
6
|
+
|
|
7
|
+
"## %s (%s)\n" | format(
|
|
8
|
+
release.version.as_semver_tag(),
|
|
9
|
+
release.tagged_date.strftime("%Y-%m-%d")
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
}}{% set commit_objects = release["elements"] | dictsort
|
|
13
|
+
%}{% include ".changes.md.j2"
|
|
14
|
+
-%}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{#
|
|
2
|
+
This changelog template controls which changelog creation occurs
|
|
3
|
+
based on which mode is provided.
|
|
4
|
+
|
|
5
|
+
Modes:
|
|
6
|
+
- init: Initialize a full changelog from scratch
|
|
7
|
+
- update: Insert new version details where the placeholder exists in the current changelog
|
|
8
|
+
|
|
9
|
+
#}{% set insertion_flag = ctx.changelog_insertion_flag
|
|
10
|
+
%}{% set unreleased_commits = ctx.history.unreleased | dictsort
|
|
11
|
+
%}{#
|
|
12
|
+
#}{% if ctx.changelog_mode == "init"
|
|
13
|
+
%}{% include ".changelog_init.md.j2"
|
|
14
|
+
%}{#
|
|
15
|
+
#}{% elif ctx.changelog_mode == "update"
|
|
16
|
+
%}{% set prev_changelog_file = ctx.prev_changelog_file
|
|
17
|
+
%}{% set new_releases = []
|
|
18
|
+
%}{% if ctx.history.released.values() | length > 0
|
|
19
|
+
%}{% set new_releases = [ctx.history.released.values() | first]
|
|
20
|
+
%}{% endif
|
|
21
|
+
%}{% include ".changelog_update.md.j2"
|
|
22
|
+
%}{#
|
|
23
|
+
#}{% endif
|
|
24
|
+
%}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{% include "./md/.versioned_changes.md.j2" %}
|
semantic_release/errors.py
CHANGED
|
@@ -8,6 +8,10 @@ class SemanticReleaseBaseError(Exception):
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
class InternalError(SemanticReleaseBaseError):
|
|
12
|
+
"""Raised when an internal error occurs, which should never happen"""
|
|
13
|
+
|
|
14
|
+
|
|
11
15
|
class InvalidConfiguration(SemanticReleaseBaseError):
|
|
12
16
|
"""Raised when configuration is deemed invalid"""
|
|
13
17
|
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# CHANGELOG
|
|
2
|
-
{% if context.history.unreleased | length > 0 -%}
|
|
3
|
-
{# UNRELEASED #}
|
|
4
|
-
## Unreleased
|
|
5
|
-
{% for type_, commits in context.history.unreleased | dictsort %}
|
|
6
|
-
### {{ type_ | capitalize }}
|
|
7
|
-
{% for commit in commits %}{% if type_ != "unknown" %}
|
|
8
|
-
* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
|
|
9
|
-
{% else %}
|
|
10
|
-
* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
|
|
11
|
-
{% endif %}{% endfor %}{% endfor %}{% endif -%}
|
|
12
|
-
{% for version, release in context.history.released.items() -%}
|
|
13
|
-
{# RELEASED #}
|
|
14
|
-
## {{ version.as_semver_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }})
|
|
15
|
-
{% for type_, commits in release["elements"] | dictsort %}
|
|
16
|
-
### {{ type_ | capitalize }}
|
|
17
|
-
{% for commit in commits %}{% if type_ != "unknown" %}
|
|
18
|
-
* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
|
|
19
|
-
{% else %}
|
|
20
|
-
* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
|
|
21
|
-
{% endif %}{% endfor %}{% endfor %}{% endfor %}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }})
|
|
2
|
-
{% for type_, commits in release["elements"] | dictsort %}
|
|
3
|
-
## {{ type_ | capitalize }}
|
|
4
|
-
{% for commit in commits %}{% if type_ != "unknown" %}
|
|
5
|
-
* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
|
|
6
|
-
{% else %}
|
|
7
|
-
* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }}))
|
|
8
|
-
{% endif %}{% endfor %}{% endfor %}
|
{python_semantic_release-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/AUTHORS.rst
RENAMED
|
File without changes
|
{python_semantic_release-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_semantic_release-9.8.9.dist-info → python_semantic_release-9.10.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|