Skip to content

Commit eda69f4

Browse files
#128 - Allow to skip patch version in input assuming 0 (#158)
* #128 - Allow to skip patch version in input assuming 0 - tag and from-tag inputs can be now defined as short versions. The `.0` is added automatically.
1 parent 4e71f70 commit eda69f4

File tree

4 files changed

+102
-5
lines changed

4 files changed

+102
-5
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ Add the following step to your GitHub workflow (in example are used non-default
102102
env:
103103
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
104104
with:
105-
tag-name: "v0.2.0"
106-
from-tag-name: "v0.1.0"
105+
tag-name: "v0.2.0" # accepts also v0.2 format when patch version is 0
106+
from-tag-name: "v0.1.0" # accepts also v0.1 format when patch version is 0
107107
chapters: |
108108
- {"title": "Breaking Changes 💥", "label": "breaking-change"}
109109
- {"title": "New Features 🎉", "label": "enhancement"}

release_notes_generator/action_inputs.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
)
5353
from release_notes_generator.utils.enums import DuplicityScopeEnum
5454
from release_notes_generator.utils.gh_action import get_action_input
55+
from release_notes_generator.utils.utils import normalize_version_tag
5556

5657
logger = logging.getLogger(__name__)
5758

@@ -119,14 +120,16 @@ def get_tag_name() -> str:
119120
"""
120121
Get the tag name from the action inputs.
121122
"""
122-
return get_action_input(TAG_NAME) or ""
123+
raw = get_action_input(TAG_NAME) or ""
124+
return normalize_version_tag(raw)
123125

124126
@staticmethod
125127
def get_from_tag_name() -> str:
126128
"""
127129
Get the from-tag name from the action inputs.
128130
"""
129-
return get_action_input(FROM_TAG_NAME, default="") # type: ignore[return-value] # string is returned as default
131+
raw = get_action_input(FROM_TAG_NAME, default="")
132+
return normalize_version_tag(raw) # type: ignore[arg-type]
130133

131134
@staticmethod
132135
def is_from_tag_name_defined() -> bool:
@@ -416,6 +419,7 @@ def validate_inputs() -> None:
416419

417420
logger.debug("Repository: %s/%s", ActionInputs._owner, ActionInputs._repo_name)
418421
logger.debug("Tag name: %s", tag_name)
422+
logger.debug("From tag name: %s", from_tag_name)
419423
logger.debug("Chapters: %s", chapters)
420424
logger.debug("Published at: %s", published_at)
421425
logger.debug("Skip release notes labels: %s", ActionInputs.get_skip_release_notes_labels())

release_notes_generator/utils/utils.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"""
2020

2121
import logging
22+
import re
2223

2324
from typing import Optional
2425

@@ -54,3 +55,46 @@ def get_change_url(
5455
changelog_url = f"https://github.com/{repo.full_name}/compare/{rls.tag_name}...{tag_name}"
5556

5657
return changelog_url
58+
59+
60+
_SEMVER_SHORT_RE = re.compile(
61+
r"""
62+
^\s* # optional leading whitespace
63+
v? # optional leading 'v'
64+
(?P<major>\d+) # major
65+
\. # dot
66+
(?P<minor>\d+) # minor
67+
(?:\.(?P<patch>\d+))? # optional .patch
68+
\s*$ # optional trailing whitespace
69+
""",
70+
re.VERBOSE,
71+
)
72+
73+
74+
def normalize_version_tag(tag: str) -> str:
75+
"""
76+
Normalize a tag to full 'vMAJOR.MINOR.PATCH' form.
77+
78+
Accepts:
79+
- 'v1.2.3' -> 'v1.2.3'
80+
- 'v1.2' -> 'v1.2.0'
81+
- '1.2.3' -> 'v1.2.3'
82+
- '1.2' -> 'v1.2.0'
83+
84+
Returns empty string if input is empty/whitespace.
85+
Raises ValueError on malformed versions.
86+
"""
87+
if not tag or tag.strip() == "":
88+
return ""
89+
90+
m = _SEMVER_SHORT_RE.match(tag)
91+
if not m:
92+
raise ValueError(
93+
f"Invalid version tag format: {tag!r}. " "Expected vMAJOR.MINOR[.PATCH], e.g. 'v0.2' or 'v0.2.0'."
94+
)
95+
96+
major = int(m.group("major"))
97+
minor = int(m.group("minor"))
98+
patch = int(m.group("patch")) if m.group("patch") is not None else 0
99+
100+
return f"v{major}.{minor}.{patch}"

tests/test_action_inputs.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,60 @@ def test_get_github_token(mocker):
108108
assert ActionInputs.get_github_token() == "fake-token"
109109

110110

111-
def test_get_tag_name(mocker):
111+
def test_get_tag_name_version_full(mocker):
112112
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="v1.0.0")
113113
assert ActionInputs.get_tag_name() == "v1.0.0"
114114

115115

116+
def test_get_tag_name_version_shorted_with_v(mocker):
117+
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="v1.2")
118+
assert ActionInputs.get_tag_name() == "v1.2.0"
119+
120+
121+
def test_get_tag_name_version_shorted_no_v(mocker):
122+
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="1.2")
123+
assert ActionInputs.get_tag_name() == "v1.2.0"
124+
125+
126+
def test_get_tag_name_empty(mocker):
127+
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="")
128+
assert ActionInputs.get_tag_name() == ""
129+
130+
131+
def test_get_tag_name_invalid_format(mocker):
132+
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="v1.2.beta")
133+
with pytest.raises(ValueError) as excinfo:
134+
ActionInputs.get_tag_name()
135+
assert "Invalid version tag format: 'v1.2.beta'. Expected vMAJOR.MINOR[.PATCH], e.g. 'v0.2' or 'v0.2.0'." in str(excinfo.value)
136+
137+
138+
def test_get_tag_from_name_version_full(mocker):
139+
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="v1.0.0")
140+
assert ActionInputs.get_from_tag_name() == "v1.0.0"
141+
142+
143+
def test_get_from_tag_name_version_shorted_with_v(mocker):
144+
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="v1.2")
145+
assert ActionInputs.get_from_tag_name() == "v1.2.0"
146+
147+
148+
def test_get_from_tag_name_version_shorted_no_v(mocker):
149+
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="1.2")
150+
assert ActionInputs.get_from_tag_name() == "v1.2.0"
151+
152+
153+
def test_get_from_tag_name_empty(mocker):
154+
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="")
155+
assert ActionInputs.get_from_tag_name() == ""
156+
157+
158+
def test_get_from_tag_name_invalid_format(mocker):
159+
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="v1.2.beta")
160+
with pytest.raises(ValueError) as excinfo:
161+
ActionInputs.get_from_tag_name()
162+
assert "Invalid version tag format: 'v1.2.beta'. Expected vMAJOR.MINOR[.PATCH], e.g. 'v0.2' or 'v0.2.0'." in str(excinfo.value)
163+
164+
116165
def test_get_chapters_success(mocker):
117166
mocker.patch("release_notes_generator.action_inputs.get_action_input", return_value="[{\"title\": \"Title\", \"label\": \"Label\"}]")
118167
assert ActionInputs.get_chapters() == [{"title": "Title", "label": "Label"}]

0 commit comments

Comments
 (0)