python-semantic-release 9.16.1__py3-none-any.whl → 9.18.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.16.1.dist-info → python_semantic_release-9.18.0.dist-info}/METADATA +1 -1
- {python_semantic_release-9.16.1.dist-info → python_semantic_release-9.18.0.dist-info}/RECORD +40 -40
- semantic_release/__init__.py +1 -1
- semantic_release/changelog/context.py +20 -1
- semantic_release/changelog/release_history.py +100 -56
- semantic_release/changelog/template.py +0 -1
- semantic_release/cli/changelog_writer.py +10 -1
- semantic_release/cli/commands/changelog.py +45 -1
- semantic_release/cli/commands/version.py +21 -3
- semantic_release/cli/config.py +25 -3
- semantic_release/cli/masking_filter.py +1 -1
- semantic_release/commit_parser/_base.py +1 -1
- semantic_release/commit_parser/angular.py +240 -21
- semantic_release/commit_parser/emoji.py +239 -29
- semantic_release/commit_parser/scipy.py +7 -7
- semantic_release/commit_parser/tag.py +3 -1
- semantic_release/commit_parser/token.py +19 -6
- semantic_release/commit_parser/util.py +60 -8
- semantic_release/const.py +2 -0
- semantic_release/data/templates/angular/md/.components/changes.md.j2 +59 -14
- semantic_release/data/templates/angular/md/.components/first_release.md.j2 +11 -4
- semantic_release/data/templates/angular/md/.components/macros.md.j2 +66 -0
- semantic_release/data/templates/angular/md/.components/versioned_changes.md.j2 +13 -7
- semantic_release/data/templates/angular/md/.release_notes.md.j2 +25 -21
- semantic_release/data/templates/angular/rst/.components/changes.rst.j2 +68 -23
- semantic_release/data/templates/angular/rst/.components/first_release.rst.j2 +2 -0
- semantic_release/data/templates/angular/rst/.components/macros.rst.j2 +66 -0
- semantic_release/data/templates/angular/rst/.components/versioned_changes.rst.j2 +2 -0
- semantic_release/helpers.py +90 -2
- semantic_release/hvcs/_base.py +1 -1
- semantic_release/hvcs/bitbucket.py +15 -0
- semantic_release/hvcs/gitea.py +21 -0
- semantic_release/hvcs/github.py +21 -0
- semantic_release/hvcs/gitlab.py +20 -0
- semantic_release/version/algorithm.py +48 -13
- {python_semantic_release-9.16.1.dist-info → python_semantic_release-9.18.0.dist-info}/AUTHORS.rst +0 -0
- {python_semantic_release-9.16.1.dist-info → python_semantic_release-9.18.0.dist-info}/LICENSE +0 -0
- {python_semantic_release-9.16.1.dist-info → python_semantic_release-9.18.0.dist-info}/WHEEL +0 -0
- {python_semantic_release-9.16.1.dist-info → python_semantic_release-9.18.0.dist-info}/entry_points.txt +0 -0
- {python_semantic_release-9.16.1.dist-info → python_semantic_release-9.18.0.dist-info}/top_level.txt +0 -0
{python_semantic_release-9.16.1.dist-info → python_semantic_release-9.18.0.dist-info}/RECORD
RENAMED
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
semantic_release/__init__.py,sha256
|
|
1
|
+
semantic_release/__init__.py,sha256=-F25Nu3RzbeFLRgFP4ms_VHphEUTK1SdYz0y3LLOJB8,1229
|
|
2
2
|
semantic_release/__main__.py,sha256=KOIBOvLruqfi5ArXcWK3ucIZ7NB55kfCbycJaxx6aQg,1485
|
|
3
|
-
semantic_release/const.py,sha256=
|
|
3
|
+
semantic_release/const.py,sha256=wInJR7vcOgT1ysm5VuJQ6lD_ZGYnCwRVKz7Uz3htQc4,861
|
|
4
4
|
semantic_release/enums.py,sha256=vrEw1UNRcNrFjPqOFnuUzfeoqKj0ChixVVlyk5fqbng,1744
|
|
5
5
|
semantic_release/errors.py,sha256=PY9rmviSFBZkqawW6VXbUfmF9C_RNOIObcmeGxLefMo,2904
|
|
6
6
|
semantic_release/gitproject.py,sha256=G4XrucN-ZwT1Kj4RMrABcr1vWb0bjKgurEeJjcL-61c,9422
|
|
7
7
|
semantic_release/globals.py,sha256=imI9WKGa6MS2pTRAZiWZ2qIJup2eWnBz3OZmIj2YIHM,158
|
|
8
|
-
semantic_release/helpers.py,sha256=
|
|
8
|
+
semantic_release/helpers.py,sha256=8yQTYUS3InvEnEqqhzJPM_R-69Pk6k9gF1NgPlgQx1Q,9774
|
|
9
9
|
semantic_release/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
semantic_release/changelog/__init__.py,sha256=Bg6Xe5Vt32rWoMscW-hd4sUwiZqzWmsg4CD1EhMesMY,262
|
|
11
|
-
semantic_release/changelog/context.py,sha256=
|
|
12
|
-
semantic_release/changelog/release_history.py,sha256=
|
|
13
|
-
semantic_release/changelog/template.py,sha256=
|
|
11
|
+
semantic_release/changelog/context.py,sha256=WeLQ2BvYEWunIF8XEl6ldQE4IRg0d7r8mYSBi-TqK7o,5988
|
|
12
|
+
semantic_release/changelog/release_history.py,sha256=epxN_tFGk3H3OGjiMsvwEVp5wgrTIKn2WRmjK5ZJ6SM,10578
|
|
13
|
+
semantic_release/changelog/template.py,sha256=R3V5m-7kv9ES23e_g37fe17tk-ESgvwV0C9rp5uoeOE,5691
|
|
14
14
|
semantic_release/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
semantic_release/cli/changelog_writer.py,sha256=
|
|
15
|
+
semantic_release/cli/changelog_writer.py,sha256=92DntVFFGdzFc5eBUgrRXbSOSV9KVvV769NyhLOTirg,9281
|
|
16
16
|
semantic_release/cli/cli_context.py,sha256=Nop71LdVCJOeSUHgTXunMyK3xAu_QKQC2cRp1QBVkX0,4134
|
|
17
|
-
semantic_release/cli/config.py,sha256=
|
|
17
|
+
semantic_release/cli/config.py,sha256=rLva6QIB5DJIYtWCnC1GHiHMWbnEkWg_YxIVVhznjnk,33138
|
|
18
18
|
semantic_release/cli/const.py,sha256=h7XE2D0D__TAZSrUUtVszwvzpkHTMOiQCf97XQNbEvA,163
|
|
19
19
|
semantic_release/cli/github_actions_output.py,sha256=6oNwjnQBg9XF5QgGc4TgbwX_-W0aj65VwGSL4ALvqVg,2296
|
|
20
|
-
semantic_release/cli/masking_filter.py,sha256=
|
|
20
|
+
semantic_release/cli/masking_filter.py,sha256=ric34rnXfN5RiAVVaKnhiMJOxTnEl26kI06jQqZPZoQ,3072
|
|
21
21
|
semantic_release/cli/util.py,sha256=FyXaBkeL7nXKjy3X9rQLEwvn7p46xPekp2V8Z-5MVrk,3755
|
|
22
22
|
semantic_release/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
semantic_release/cli/commands/changelog.py,sha256=
|
|
23
|
+
semantic_release/cli/commands/changelog.py,sha256=i3j6jhcfhFo3d7Bgrp1tD01vJkuTZvtaWX7bPteLQbA,5345
|
|
24
24
|
semantic_release/cli/commands/generate_config.py,sha256=2xZOu3NpyhBp0pWr7d8ugKl_kjqQgpSsSMHq5wHTfrE,1699
|
|
25
25
|
semantic_release/cli/commands/main.py,sha256=237rn_Od4LOWfjUjiUKI_jSV820MfcCtRpwPjxjLbyU,4312
|
|
26
26
|
semantic_release/cli/commands/publish.py,sha256=y_LalPti_kZeQJzl2CR2pTZUK8DCMvNSTe4NaMC5TJA,2875
|
|
27
|
-
semantic_release/cli/commands/version.py,sha256=
|
|
27
|
+
semantic_release/cli/commands/version.py,sha256=BMAOXToqOC_U0nh3d-QszAOS7xhN86aGhFiaePSdfws,25287
|
|
28
28
|
semantic_release/commit_parser/__init__.py,sha256=cv5HFBdw7OJd4Laj4Ex8ZZ5Tml8GwXgQcXW6Pasr2Ao,615
|
|
29
|
-
semantic_release/commit_parser/_base.py,sha256=
|
|
30
|
-
semantic_release/commit_parser/angular.py,sha256=
|
|
31
|
-
semantic_release/commit_parser/emoji.py,sha256=
|
|
32
|
-
semantic_release/commit_parser/scipy.py,sha256=
|
|
33
|
-
semantic_release/commit_parser/tag.py,sha256=
|
|
34
|
-
semantic_release/commit_parser/token.py,sha256=
|
|
35
|
-
semantic_release/commit_parser/util.py,sha256=
|
|
36
|
-
semantic_release/data/templates/angular/md/.release_notes.md.j2,sha256=
|
|
29
|
+
semantic_release/commit_parser/_base.py,sha256=oDifeTmFDpS238cp_DDrGzfidaKeAD5olCB5IM4Q6z8,3058
|
|
30
|
+
semantic_release/commit_parser/angular.py,sha256=UM88ethWT34__kDgsBXXb26d0V8FN1ZFjcU7psuOhgY,19259
|
|
31
|
+
semantic_release/commit_parser/emoji.py,sha256=T3kw8jyq1zYME3f6JJ6O3hVJ2IpRk3DD5LB0VOlcbMM,18194
|
|
32
|
+
semantic_release/commit_parser/scipy.py,sha256=0rYZglJ7uib-1Deu4J30wHh7AZS8KfO0eND82bPtDQ8,4526
|
|
33
|
+
semantic_release/commit_parser/tag.py,sha256=oGB3lgyp2Eu3Tg3jjxqNzN86N6bokSaFu6f4Ir6IS_k,3546
|
|
34
|
+
semantic_release/commit_parser/token.py,sha256=ECgi7eeSgk3Biq1Y_ChbFJZQLkrUpNvGhIaEOXrNC4M,7904
|
|
35
|
+
semantic_release/commit_parser/util.py,sha256=_ACiopznjwINn4t1zPHl8bxZEc0zOAURTycNU-sXs3M,3959
|
|
36
|
+
semantic_release/data/templates/angular/md/.release_notes.md.j2,sha256=3d4ihVVY1u2rAm7XgydRKQq92YfQkFWanpEP5uQ8xmM,2512
|
|
37
37
|
semantic_release/data/templates/angular/md/CHANGELOG.md.j2,sha256=FZmrQ-qOIoSoJmAa_NFaRelfmqUpypU2xlDeScdGOf4,729
|
|
38
38
|
semantic_release/data/templates/angular/md/.components/changelog_header.md.j2,sha256=qNxTuSr59CV_yyimVU_RYp5azCnK0l6nJ03Zf0u5Ugg,166
|
|
39
39
|
semantic_release/data/templates/angular/md/.components/changelog_init.md.j2,sha256=MyEQdWUemGXKWDhvpTmubZdozd3iaLECZAI1VRlE7mg,862
|
|
40
40
|
semantic_release/data/templates/angular/md/.components/changelog_update.md.j2,sha256=uVF4wbbjTMvl6Kbsq9xy3YIrj-uhBnHylEfA7S76aHI,2606
|
|
41
|
-
semantic_release/data/templates/angular/md/.components/changes.md.j2,sha256=
|
|
42
|
-
semantic_release/data/templates/angular/md/.components/first_release.md.j2,sha256
|
|
43
|
-
semantic_release/data/templates/angular/md/.components/macros.md.j2,sha256=
|
|
41
|
+
semantic_release/data/templates/angular/md/.components/changes.md.j2,sha256=NIdXNNao4Oq9nWHaq6X4ZbGMMJ2H-7yxqAP5HmBmSV8,5965
|
|
42
|
+
semantic_release/data/templates/angular/md/.components/first_release.md.j2,sha256=-S2aJV3ka4YicLs8UF6BEV-CDnL8iXLcNRRIiENFrYE,421
|
|
43
|
+
semantic_release/data/templates/angular/md/.components/macros.md.j2,sha256=RzBVwKSCb-WmRIMFVQL9U157mAih-PzZ_iRcfyWkG20,8196
|
|
44
44
|
semantic_release/data/templates/angular/md/.components/unreleased_changes.md.j2,sha256=HRLj6cyRfPZXC0s-0Av6s0Gp3jKxWg9AIEtIXBVqJuY,177
|
|
45
|
-
semantic_release/data/templates/angular/md/.components/versioned_changes.md.j2,sha256=
|
|
45
|
+
semantic_release/data/templates/angular/md/.components/versioned_changes.md.j2,sha256=KbU7D_n_VqET7q2CLwBQ7931dvO7xQklADw-oY6ujlY,502
|
|
46
46
|
semantic_release/data/templates/angular/rst/CHANGELOG.rst.j2,sha256=VmkXEMHiPBdZ1Z47QMxnJBZA0NbFSbKenUbThQVFAGY,731
|
|
47
47
|
semantic_release/data/templates/angular/rst/.components/changelog_header.rst.j2,sha256=c9xN1SEYLFwMvPYXYKt-ZbYPn2-Ss0V7zepEtFFj3Os,200
|
|
48
48
|
semantic_release/data/templates/angular/rst/.components/changelog_init.rst.j2,sha256=XD0l3eTyz1yydLKsmSqBk-u8RnO-RdQ2Q8uWezHMAWw,866
|
|
49
49
|
semantic_release/data/templates/angular/rst/.components/changelog_update.rst.j2,sha256=x23-qk9owJrOQaHx8SgSnIZECITjPf1R2awfv9EOHN0,2604
|
|
50
|
-
semantic_release/data/templates/angular/rst/.components/changes.rst.j2,sha256=
|
|
51
|
-
semantic_release/data/templates/angular/rst/.components/first_release.rst.j2,sha256=
|
|
52
|
-
semantic_release/data/templates/angular/rst/.components/macros.rst.j2,sha256=
|
|
50
|
+
semantic_release/data/templates/angular/rst/.components/changes.rst.j2,sha256=IZdUJ5tt-8P5AyPgS1hsTuu2vg10GDINJ9wLJ4CpP68,7352
|
|
51
|
+
semantic_release/data/templates/angular/rst/.components/first_release.rst.j2,sha256=huaO-B3BRs7h2LRks2a6-656W2qzURkzLiyuKvYxMTg,462
|
|
52
|
+
semantic_release/data/templates/angular/rst/.components/macros.rst.j2,sha256=WLNUD2H2V-5vrwT7TKelwQ2wclLcZxFs0E2Lk3Ld10U,9096
|
|
53
53
|
semantic_release/data/templates/angular/rst/.components/unreleased_changes.rst.j2,sha256=ARBhc1ZpKwehGKDvOMqukmN59mTJiHzHsS7rOfKYCt8,202
|
|
54
|
-
semantic_release/data/templates/angular/rst/.components/versioned_changes.rst.j2,sha256=
|
|
54
|
+
semantic_release/data/templates/angular/rst/.components/versioned_changes.rst.j2,sha256=5THfdfYRjHNEQZVUDt112124hFEZxu7J4qIdgn54O1Q,553
|
|
55
55
|
semantic_release/hvcs/__init__.py,sha256=JwoaLOF-12L-OBo_9-tOXXhdiHKeVungA9865to2oZk,494
|
|
56
|
-
semantic_release/hvcs/_base.py,sha256=
|
|
57
|
-
semantic_release/hvcs/bitbucket.py,sha256=
|
|
58
|
-
semantic_release/hvcs/gitea.py,sha256=
|
|
59
|
-
semantic_release/hvcs/github.py,sha256=
|
|
60
|
-
semantic_release/hvcs/gitlab.py,sha256=
|
|
56
|
+
semantic_release/hvcs/_base.py,sha256=ycHg0WljEVTqjFXoGRrHMvbyqWqPs4HziB090IcPZSE,2664
|
|
57
|
+
semantic_release/hvcs/bitbucket.py,sha256=5Z6q_ySwLZ-repf5EH0e8VVvk4fkWvdKCp3sIw4cBBs,10162
|
|
58
|
+
semantic_release/hvcs/gitea.py,sha256=wETF7JDR2xmcyR355faVx7GAtee35gdx7u27Si0prz8,13423
|
|
59
|
+
semantic_release/hvcs/github.py,sha256=nks7cEyp9usw8e-aZ6V2SXsDHu6nPk5haM92LkQ8UBk,20649
|
|
60
|
+
semantic_release/hvcs/gitlab.py,sha256=bKRIzzdAcFmXT_PixadSbpKn6icfLu8KQ7HIHc_uKkU,10623
|
|
61
61
|
semantic_release/hvcs/remote_hvcs_base.py,sha256=cV8qYHtP47bmfIZqV4K2EiMHskFEoIo6agW_iU6wfVE,6105
|
|
62
62
|
semantic_release/hvcs/token_auth.py,sha256=ZjT56-NIPB4OKIt1qwHCu1TavXnrWFIBl9ARlg56hgU,663
|
|
63
63
|
semantic_release/hvcs/util.py,sha256=guxisysY_IW5tv7aaV-iVPEVJzgbOs375kiRRpSquTI,2879
|
|
64
64
|
semantic_release/version/__init__.py,sha256=CLhtGQry9dLIij5XyRa9ZevxU_1p8tjMTSQ-K_GMpWM,270
|
|
65
|
-
semantic_release/version/algorithm.py,sha256=
|
|
65
|
+
semantic_release/version/algorithm.py,sha256=s5lso4Py-PiVzuPhgeJOz6IDzIdqc6EeoUirmBZ8IXY,16696
|
|
66
66
|
semantic_release/version/declaration.py,sha256=f6Ld7hIhrqvDrRBapJHr-KDimuyo-4IG8009Zu9BIgU,7357
|
|
67
67
|
semantic_release/version/translator.py,sha256=P1noIsVBn8u6zNOFjG0xKYOWapxqf_PHSMvMeLJ9kXg,3050
|
|
68
68
|
semantic_release/version/version.py,sha256=6PCtSbLP88U1daoxnCwHc--YguZo4waGNLqJ5JfeczE,14175
|
|
69
|
-
python_semantic_release-9.
|
|
70
|
-
python_semantic_release-9.
|
|
71
|
-
python_semantic_release-9.
|
|
72
|
-
python_semantic_release-9.
|
|
73
|
-
python_semantic_release-9.
|
|
74
|
-
python_semantic_release-9.
|
|
75
|
-
python_semantic_release-9.
|
|
69
|
+
python_semantic_release-9.18.0.dist-info/AUTHORS.rst,sha256=XOReVvpymEFUPsS2QPH97jlfJBVrxwS2eu8-jVAe4gk,230
|
|
70
|
+
python_semantic_release-9.18.0.dist-info/LICENSE,sha256=NE85nszX252sdQdu0xgS9qwfYES0k8qS6gW3uO4jRGE,1083
|
|
71
|
+
python_semantic_release-9.18.0.dist-info/METADATA,sha256=G6MEURvvUmQP39A6ksc-bSvIS4XeKxbP3iFNDF9gytE,3812
|
|
72
|
+
python_semantic_release-9.18.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
73
|
+
python_semantic_release-9.18.0.dist-info/entry_points.txt,sha256=r2Jql3GTQyugQnvf34l2eXk1O_Qx6llR_xixG1ZWgD0,105
|
|
74
|
+
python_semantic_release-9.18.0.dist-info/top_level.txt,sha256=qYA24nyg3eP-ti5UW7Vuj2aXVmM0wqVHx4mREdRZNAA,17
|
|
75
|
+
python_semantic_release-9.18.0.dist-info/RECORD,,
|
semantic_release/__init__.py
CHANGED
|
@@ -4,10 +4,15 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from enum import Enum
|
|
7
|
-
from pathlib import Path
|
|
7
|
+
from pathlib import Path, PurePosixPath
|
|
8
8
|
from re import compile as regexp
|
|
9
9
|
from typing import TYPE_CHECKING, Any, Callable, Literal
|
|
10
10
|
|
|
11
|
+
from urllib3.util import Url
|
|
12
|
+
|
|
13
|
+
from semantic_release.const import PYPI_WEB_DOMAIN
|
|
14
|
+
from semantic_release.helpers import sort_numerically
|
|
15
|
+
|
|
11
16
|
if TYPE_CHECKING: # pragma: no cover
|
|
12
17
|
from jinja2 import Environment
|
|
13
18
|
|
|
@@ -24,6 +29,7 @@ class ReleaseNotesContext:
|
|
|
24
29
|
version: Version
|
|
25
30
|
release: Release
|
|
26
31
|
mask_initial_release: bool
|
|
32
|
+
license_name: str
|
|
27
33
|
filters: tuple[Callable[..., Any], ...] = ()
|
|
28
34
|
|
|
29
35
|
def bind_to_environment(self, env: Environment) -> Environment:
|
|
@@ -84,13 +90,26 @@ def make_changelog_context(
|
|
|
84
90
|
hvcs_type=hvcs_client.__class__.__name__.lower(),
|
|
85
91
|
filters=(
|
|
86
92
|
*hvcs_client.get_changelog_context_filters(),
|
|
93
|
+
create_pypi_url,
|
|
87
94
|
read_file,
|
|
88
95
|
convert_md_to_rst,
|
|
89
96
|
autofit_text_width,
|
|
97
|
+
sort_numerically,
|
|
90
98
|
),
|
|
91
99
|
)
|
|
92
100
|
|
|
93
101
|
|
|
102
|
+
def create_pypi_url(package_name: str, version: str = "") -> str:
|
|
103
|
+
project_name = package_name.strip("/").strip()
|
|
104
|
+
if not project_name:
|
|
105
|
+
raise ValueError("package_name must not be empty!")
|
|
106
|
+
return Url(
|
|
107
|
+
scheme="https",
|
|
108
|
+
host=PYPI_WEB_DOMAIN,
|
|
109
|
+
path=str(PurePosixPath("project", project_name, version.strip("/").strip())),
|
|
110
|
+
).url.rstrip("/")
|
|
111
|
+
|
|
112
|
+
|
|
94
113
|
def read_file(filepath: str) -> str:
|
|
95
114
|
try:
|
|
96
115
|
if not filepath:
|
|
@@ -9,7 +9,9 @@ from git.objects.tag import TagObject
|
|
|
9
9
|
|
|
10
10
|
from semantic_release.commit_parser import ParseError
|
|
11
11
|
from semantic_release.commit_parser.token import ParsedCommit
|
|
12
|
+
from semantic_release.commit_parser.util import force_str
|
|
12
13
|
from semantic_release.enums import LevelBump
|
|
14
|
+
from semantic_release.helpers import validate_types_in_sequence
|
|
13
15
|
from semantic_release.version.algorithm import tags_and_versions
|
|
14
16
|
|
|
15
17
|
if TYPE_CHECKING: # pragma: no cover
|
|
@@ -50,6 +52,12 @@ class ReleaseHistory:
|
|
|
50
52
|
for tag, version in all_git_tags_and_versions
|
|
51
53
|
}
|
|
52
54
|
|
|
55
|
+
ignore_merge_commits = bool(
|
|
56
|
+
hasattr(commit_parser, "options")
|
|
57
|
+
and hasattr(commit_parser.options, "ignore_merge_commits")
|
|
58
|
+
and getattr(commit_parser.options, "ignore_merge_commits") # noqa: B009
|
|
59
|
+
)
|
|
60
|
+
|
|
53
61
|
# Strategy:
|
|
54
62
|
# Loop through commits in history, parsing as we go.
|
|
55
63
|
# Add these commits to `unreleased` as a key-value mapping
|
|
@@ -102,75 +110,111 @@ class ReleaseHistory:
|
|
|
102
110
|
|
|
103
111
|
released.setdefault(the_version, release)
|
|
104
112
|
|
|
105
|
-
# mypy will be happy if we make this an explicit string
|
|
106
|
-
commit_message = str(commit.message)
|
|
107
|
-
|
|
108
113
|
log.info(
|
|
109
114
|
"parsing commit [%s] %s",
|
|
110
115
|
commit.hexsha[:8],
|
|
111
|
-
|
|
112
|
-
)
|
|
113
|
-
parse_result = commit_parser.parse(commit)
|
|
114
|
-
commit_type = (
|
|
115
|
-
"unknown" if isinstance(parse_result, ParseError) else parse_result.type
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
has_exclusion_match = any(
|
|
119
|
-
pattern.match(commit_message) for pattern in exclude_commit_patterns
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
commit_level_bump = (
|
|
123
|
-
LevelBump.NO_RELEASE
|
|
124
|
-
if isinstance(parse_result, ParseError)
|
|
125
|
-
else parse_result.bump
|
|
116
|
+
str(commit.message).replace("\n", " ")[:54],
|
|
126
117
|
)
|
|
118
|
+
# returns a ParseResult or list of ParseResult objects,
|
|
119
|
+
# it is usually one, but we split a commit if a squashed merge is detected
|
|
120
|
+
parse_results = commit_parser.parse(commit)
|
|
121
|
+
|
|
122
|
+
if not any(
|
|
123
|
+
(
|
|
124
|
+
isinstance(parse_results, (ParseError, ParsedCommit)),
|
|
125
|
+
(
|
|
126
|
+
(
|
|
127
|
+
isinstance(parse_results, list)
|
|
128
|
+
or type(parse_results) == tuple
|
|
129
|
+
)
|
|
130
|
+
and validate_types_in_sequence(
|
|
131
|
+
parse_results, (ParseError, ParsedCommit)
|
|
132
|
+
)
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
):
|
|
136
|
+
raise TypeError("Unexpected type returned from commit_parser.parse")
|
|
137
|
+
|
|
138
|
+
results: list[ParseResult] = [
|
|
139
|
+
*(
|
|
140
|
+
[parse_results]
|
|
141
|
+
if isinstance(parse_results, (ParseError, ParsedCommit))
|
|
142
|
+
else parse_results
|
|
143
|
+
),
|
|
144
|
+
]
|
|
145
|
+
|
|
146
|
+
is_squash_commit = bool(len(results) > 1)
|
|
147
|
+
|
|
148
|
+
# iterate through parsed commits to add to changelog definition
|
|
149
|
+
for parsed_result in results:
|
|
150
|
+
commit_message = force_str(parsed_result.commit.message)
|
|
151
|
+
commit_type = (
|
|
152
|
+
"unknown"
|
|
153
|
+
if isinstance(parsed_result, ParseError)
|
|
154
|
+
else parsed_result.type
|
|
155
|
+
)
|
|
156
|
+
log.debug("commit has type '%s'", commit_type)
|
|
127
157
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
# are included, then the changelog will be empty. Even if ther was other
|
|
131
|
-
# commits included, the true reason for a version bump would be missing.
|
|
132
|
-
if has_exclusion_match and commit_level_bump == LevelBump.NO_RELEASE:
|
|
133
|
-
log.info(
|
|
134
|
-
"Excluding commit [%s] %s",
|
|
135
|
-
commit.hexsha[:8],
|
|
136
|
-
commit_message.replace("\n", " ")[:50],
|
|
158
|
+
has_exclusion_match = any(
|
|
159
|
+
pattern.match(commit_message) for pattern in exclude_commit_patterns
|
|
137
160
|
)
|
|
138
|
-
continue
|
|
139
161
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
log.info(
|
|
145
|
-
str.join(
|
|
146
|
-
" ",
|
|
147
|
-
[
|
|
148
|
-
"Excluding commit %s (%s) because parser determined",
|
|
149
|
-
"it should not included in the changelog",
|
|
150
|
-
],
|
|
151
|
-
),
|
|
152
|
-
commit.hexsha[:8],
|
|
153
|
-
commit_message.replace("\n", " ")[:20],
|
|
162
|
+
commit_level_bump = (
|
|
163
|
+
LevelBump.NO_RELEASE
|
|
164
|
+
if isinstance(parsed_result, ParseError)
|
|
165
|
+
else parsed_result.bump
|
|
154
166
|
)
|
|
155
|
-
continue
|
|
156
167
|
|
|
157
|
-
|
|
168
|
+
if ignore_merge_commits and parsed_result.is_merge_commit():
|
|
169
|
+
log.info("Excluding merge commit[%s]", parsed_result.short_hash)
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
# Skip excluded commits except for any commit causing a version bump
|
|
173
|
+
# Reasoning: if a commit causes a version bump, and no other commits
|
|
174
|
+
# are included, then the changelog will be empty. Even if ther was other
|
|
175
|
+
# commits included, the true reason for a version bump would be missing.
|
|
176
|
+
if has_exclusion_match and commit_level_bump == LevelBump.NO_RELEASE:
|
|
177
|
+
log.info(
|
|
178
|
+
"Excluding %s commit[%s] %s",
|
|
179
|
+
"piece of squashed" if is_squash_commit else "",
|
|
180
|
+
parsed_result.short_hash,
|
|
181
|
+
commit_message.split("\n", maxsplit=1)[0][:20],
|
|
182
|
+
)
|
|
183
|
+
continue
|
|
184
|
+
|
|
185
|
+
if (
|
|
186
|
+
isinstance(parsed_result, ParsedCommit)
|
|
187
|
+
and not parsed_result.include_in_changelog
|
|
188
|
+
):
|
|
189
|
+
log.info(
|
|
190
|
+
str.join(
|
|
191
|
+
" ",
|
|
192
|
+
[
|
|
193
|
+
"Excluding commit[%s] because parser determined",
|
|
194
|
+
"it should not included in the changelog",
|
|
195
|
+
],
|
|
196
|
+
),
|
|
197
|
+
parsed_result.short_hash,
|
|
198
|
+
)
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
if the_version is None:
|
|
202
|
+
log.info(
|
|
203
|
+
"[Unreleased] adding commit[%s] to unreleased '%s'",
|
|
204
|
+
parsed_result.short_hash,
|
|
205
|
+
commit_type,
|
|
206
|
+
)
|
|
207
|
+
unreleased[commit_type].append(parsed_result)
|
|
208
|
+
continue
|
|
209
|
+
|
|
158
210
|
log.info(
|
|
159
|
-
"[
|
|
160
|
-
|
|
211
|
+
"[%s] adding commit[%s] to release '%s'",
|
|
212
|
+
the_version,
|
|
213
|
+
parsed_result.short_hash,
|
|
161
214
|
commit_type,
|
|
162
215
|
)
|
|
163
|
-
unreleased[commit_type].append(parse_result)
|
|
164
|
-
continue
|
|
165
|
-
|
|
166
|
-
log.info(
|
|
167
|
-
"[%s] adding '%s' commit(%s) to release",
|
|
168
|
-
the_version,
|
|
169
|
-
commit_type,
|
|
170
|
-
commit.hexsha[:8],
|
|
171
|
-
)
|
|
172
216
|
|
|
173
|
-
|
|
217
|
+
released[the_version]["elements"][commit_type].append(parsed_result)
|
|
174
218
|
|
|
175
219
|
return cls(unreleased=unreleased, released=released)
|
|
176
220
|
|
|
@@ -13,6 +13,7 @@ import semantic_release
|
|
|
13
13
|
from semantic_release.changelog.context import (
|
|
14
14
|
ReleaseNotesContext,
|
|
15
15
|
autofit_text_width,
|
|
16
|
+
create_pypi_url,
|
|
16
17
|
make_changelog_context,
|
|
17
18
|
)
|
|
18
19
|
from semantic_release.changelog.template import environment, recursive_render
|
|
@@ -24,6 +25,7 @@ from semantic_release.cli.const import (
|
|
|
24
25
|
)
|
|
25
26
|
from semantic_release.cli.util import noop_report
|
|
26
27
|
from semantic_release.errors import InternalError
|
|
28
|
+
from semantic_release.helpers import sort_numerically
|
|
27
29
|
|
|
28
30
|
if TYPE_CHECKING: # pragma: no cover
|
|
29
31
|
from jinja2 import Environment
|
|
@@ -228,6 +230,7 @@ def generate_release_notes(
|
|
|
228
230
|
history: ReleaseHistory,
|
|
229
231
|
style: str,
|
|
230
232
|
mask_initial_release: bool,
|
|
233
|
+
license_name: str = "",
|
|
231
234
|
) -> str:
|
|
232
235
|
users_tpl_file = template_dir / DEFAULT_RELEASE_NOTES_TPL_FILE
|
|
233
236
|
|
|
@@ -254,7 +257,13 @@ def generate_release_notes(
|
|
|
254
257
|
version=release["version"],
|
|
255
258
|
release=release,
|
|
256
259
|
mask_initial_release=mask_initial_release,
|
|
257
|
-
|
|
260
|
+
license_name=license_name,
|
|
261
|
+
filters=(
|
|
262
|
+
*hvcs_client.get_changelog_context_filters(),
|
|
263
|
+
create_pypi_url,
|
|
264
|
+
autofit_text_width,
|
|
265
|
+
sort_numerically,
|
|
266
|
+
),
|
|
258
267
|
).bind_to_environment(
|
|
259
268
|
# Use a new, non-configurable environment for release notes -
|
|
260
269
|
# not user-configurable at the moment
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from contextlib import suppress
|
|
5
|
+
from pathlib import Path
|
|
4
6
|
from typing import TYPE_CHECKING
|
|
5
7
|
|
|
6
8
|
import click
|
|
7
|
-
|
|
9
|
+
import tomlkit
|
|
10
|
+
from git import GitCommandError, Repo
|
|
8
11
|
|
|
9
12
|
from semantic_release.changelog.release_history import ReleaseHistory
|
|
10
13
|
from semantic_release.cli.changelog_writer import (
|
|
@@ -21,6 +24,43 @@ if TYPE_CHECKING: # pragma: no cover
|
|
|
21
24
|
log = logging.getLogger(__name__)
|
|
22
25
|
|
|
23
26
|
|
|
27
|
+
def get_license_name_for_release(tag_name: str, project_root: Path) -> str:
|
|
28
|
+
# Retrieve the license name at the time of the specific release tag
|
|
29
|
+
project_metadata: dict[str, str] = {}
|
|
30
|
+
curr_dir = Path.cwd().resolve()
|
|
31
|
+
allowed_directories = [
|
|
32
|
+
dir_path
|
|
33
|
+
for dir_path in [curr_dir, *curr_dir.parents]
|
|
34
|
+
if str(project_root) in str(dir_path)
|
|
35
|
+
]
|
|
36
|
+
for allowed_dir in allowed_directories:
|
|
37
|
+
proj_toml = allowed_dir.joinpath("pyproject.toml")
|
|
38
|
+
with Repo(project_root) as git_repo, suppress(GitCommandError):
|
|
39
|
+
toml_contents = git_repo.git.show(
|
|
40
|
+
f"{tag_name}:{proj_toml.relative_to(project_root)}"
|
|
41
|
+
)
|
|
42
|
+
config_toml = tomlkit.parse(toml_contents)
|
|
43
|
+
project_metadata = config_toml.unwrap().get("project", project_metadata)
|
|
44
|
+
break
|
|
45
|
+
|
|
46
|
+
license_cfg = project_metadata.get(
|
|
47
|
+
"license-expression",
|
|
48
|
+
project_metadata.get(
|
|
49
|
+
"license",
|
|
50
|
+
"",
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if not isinstance(license_cfg, (str, dict)) or license_cfg is None:
|
|
55
|
+
return ""
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
license_cfg.get("text", "") # type: ignore[attr-defined]
|
|
59
|
+
if isinstance(license_cfg, dict)
|
|
60
|
+
else license_cfg or ""
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
24
64
|
def post_release_notes(
|
|
25
65
|
release_tag: str,
|
|
26
66
|
release_notes: str,
|
|
@@ -120,6 +160,10 @@ def changelog(cli_ctx: CliContextObj, release_tag: str | None) -> None:
|
|
|
120
160
|
release_history,
|
|
121
161
|
style=runtime.changelog_style,
|
|
122
162
|
mask_initial_release=runtime.changelog_mask_initial_release,
|
|
163
|
+
license_name=get_license_name_for_release(
|
|
164
|
+
tag_name=release_tag,
|
|
165
|
+
project_root=runtime.repo_dir,
|
|
166
|
+
),
|
|
123
167
|
)
|
|
124
168
|
|
|
125
169
|
try:
|
|
@@ -65,7 +65,7 @@ def is_forced_prerelease(
|
|
|
65
65
|
log.debug(
|
|
66
66
|
"%s: %s",
|
|
67
67
|
is_forced_prerelease.__name__,
|
|
68
|
-
", "
|
|
68
|
+
str.join(", ", iter(f"{k} = {v}" for k, v in local_vars)),
|
|
69
69
|
)
|
|
70
70
|
return (
|
|
71
71
|
as_prerelease
|
|
@@ -699,13 +699,31 @@ def version( # noqa: C901
|
|
|
699
699
|
log.info("Remote does not support releases. Skipping release creation...")
|
|
700
700
|
return
|
|
701
701
|
|
|
702
|
+
license_cfg = runtime.project_metadata.get(
|
|
703
|
+
"license-expression",
|
|
704
|
+
runtime.project_metadata.get(
|
|
705
|
+
"license",
|
|
706
|
+
"",
|
|
707
|
+
),
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
if not isinstance(license_cfg, (str, dict)) or license_cfg is None:
|
|
711
|
+
license_cfg = ""
|
|
712
|
+
|
|
713
|
+
license_name = (
|
|
714
|
+
license_cfg.get("text", "")
|
|
715
|
+
if isinstance(license_cfg, dict)
|
|
716
|
+
else license_cfg or ""
|
|
717
|
+
)
|
|
718
|
+
|
|
702
719
|
release_notes = generate_release_notes(
|
|
703
720
|
hvcs_client,
|
|
704
|
-
release_history.released[new_version],
|
|
705
|
-
runtime.template_dir,
|
|
721
|
+
release=release_history.released[new_version],
|
|
722
|
+
template_dir=runtime.template_dir,
|
|
706
723
|
history=release_history,
|
|
707
724
|
style=runtime.changelog_style,
|
|
708
725
|
mask_initial_release=runtime.changelog_mask_initial_release,
|
|
726
|
+
license_name=license_name,
|
|
709
727
|
)
|
|
710
728
|
|
|
711
729
|
exception: Exception | None = None
|
semantic_release/cli/config.py
CHANGED
|
@@ -15,6 +15,8 @@ from re import (
|
|
|
15
15
|
)
|
|
16
16
|
from typing import Any, ClassVar, Dict, List, Literal, Optional, Tuple, Type, Union
|
|
17
17
|
|
|
18
|
+
# typing_extensions is for Python 3.8, 3.9, 3.10 compatibility
|
|
19
|
+
import tomlkit
|
|
18
20
|
from git import Actor, InvalidGitRepositoryError
|
|
19
21
|
from git.repo.base import Repo
|
|
20
22
|
from jinja2 import Environment
|
|
@@ -26,8 +28,6 @@ from pydantic import (
|
|
|
26
28
|
field_validator,
|
|
27
29
|
model_validator,
|
|
28
30
|
)
|
|
29
|
-
|
|
30
|
-
# typing_extensions is for Python 3.8, 3.9, 3.10 compatibility
|
|
31
31
|
from typing_extensions import Annotated, Self
|
|
32
32
|
from urllib3.util.url import parse_url
|
|
33
33
|
|
|
@@ -523,6 +523,7 @@ def _recursive_getattr(obj: Any, path: str) -> Any:
|
|
|
523
523
|
class RuntimeContext:
|
|
524
524
|
_mask_attrs_: ClassVar[List[str]] = ["hvcs_client.token"]
|
|
525
525
|
|
|
526
|
+
project_metadata: dict[str, Any]
|
|
526
527
|
repo_dir: Path
|
|
527
528
|
commit_parser: CommitParser[ParseResult, ParserOptions]
|
|
528
529
|
version_translator: VersionTranslator
|
|
@@ -599,10 +600,30 @@ class RuntimeContext:
|
|
|
599
600
|
# credentials masking for logging
|
|
600
601
|
masker = MaskingFilter(_use_named_masks=raw.logging_use_named_masks)
|
|
601
602
|
|
|
603
|
+
# TODO: move to config if we change how the generated config is constructed
|
|
604
|
+
# Retrieve project metadata from pyproject.toml
|
|
605
|
+
project_metadata: dict[str, str] = {}
|
|
606
|
+
curr_dir = Path.cwd().resolve()
|
|
607
|
+
allowed_directories = [
|
|
608
|
+
dir_path
|
|
609
|
+
for dir_path in [curr_dir, *curr_dir.parents]
|
|
610
|
+
if str(raw.repo_dir) in str(dir_path)
|
|
611
|
+
]
|
|
612
|
+
for allowed_dir in allowed_directories:
|
|
613
|
+
if (proj_toml := allowed_dir.joinpath("pyproject.toml")).exists():
|
|
614
|
+
config_toml = tomlkit.parse(proj_toml.read_text())
|
|
615
|
+
project_metadata = config_toml.unwrap().get("project", project_metadata)
|
|
616
|
+
break
|
|
617
|
+
|
|
602
618
|
# Retrieve details from repository
|
|
603
619
|
with Repo(str(raw.repo_dir)) as git_repo:
|
|
604
620
|
try:
|
|
605
|
-
|
|
621
|
+
# Get the remote url by calling out to `git remote get-url`. This returns
|
|
622
|
+
# the expanded url, taking into account any insteadOf directives
|
|
623
|
+
# in the git configuration.
|
|
624
|
+
remote_url = raw.remote.url or git_repo.git.remote(
|
|
625
|
+
"get-url", raw.remote.name
|
|
626
|
+
)
|
|
606
627
|
active_branch = git_repo.active_branch.name
|
|
607
628
|
except ValueError as err:
|
|
608
629
|
raise MissingGitRemote(
|
|
@@ -820,6 +841,7 @@ class RuntimeContext:
|
|
|
820
841
|
# )
|
|
821
842
|
|
|
822
843
|
self = cls(
|
|
844
|
+
project_metadata=project_metadata,
|
|
823
845
|
repo_dir=raw.repo_dir,
|
|
824
846
|
commit_parser=commit_parser,
|
|
825
847
|
version_translator=version_translator,
|
|
@@ -27,7 +27,7 @@ class MaskingFilter(logging.Filter):
|
|
|
27
27
|
|
|
28
28
|
def add_mask_for(self, data: str, name: str = "redacted") -> MaskingFilter:
|
|
29
29
|
if data and data not in self._UNWANTED:
|
|
30
|
-
log.debug("Adding redact pattern %r to
|
|
30
|
+
log.debug("Adding redact pattern '%r' to redact_patterns", name)
|
|
31
31
|
self._redact_patterns[name].add(data)
|
|
32
32
|
return self
|
|
33
33
|
|