Skip to content

Conversation

@miroslavpojer
Copy link
Collaborator

@miroslavpojer miroslavpojer commented Aug 28, 2025

Release Notes:

  • tag and from-tag inputs can be now defined as short versions. The .0 is added automatically.

Fixes #128

Summary by CodeRabbit

  • New Features

    • Accepts shortened version tags (e.g., v1.2 or 1.2) and normalizes them to vMAJOR.MINOR.PATCH (e.g., v1.2.0).
    • Empty version inputs are handled gracefully (returned as empty).
    • Inputs for both primary and "from" tags are consistently normalized.
  • Bug Fixes

    • Clearer validation errors for malformed tags (e.g., v1.2.beta).
  • Documentation

    • README usage examples clarify accepted version formats when patch is 0.
  • Tests

    • Expanded test coverage for version tag parsing: full, shortened, empty, and invalid inputs.

- tag and from-tag inputs can be now defined as short versions. The `.0` is added automatically.
@miroslavpojer miroslavpojer self-assigned this Aug 28, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 28, 2025

Walkthrough

Adds a regex-based version tag normalizer and integrates it into action input parsing (TAG_NAME and FROM_TAG_NAME), updates README comments, and expands tests to cover full, shortened, empty, and invalid tag formats.

Changes

Cohort / File(s) Summary of Changes
Docs
README.md
Clarified usage examples with inline comments indicating vMAJOR.MINOR is accepted when patch is 0.
Action inputs normalization
release_notes_generator/action_inputs.py
Integrated normalize_version_tag for TAG_NAME and FROM_TAG_NAME, adjusted type-ignore location, and added debug logging for from-tag.
Utils: version normalization
release_notes_generator/utils/utils.py
Added _SEMVER_SHORT_RE regex and public normalize_version_tag(tag: str) -> str to validate and normalize vMAJOR.MINOR[.PATCH] → vMAJOR.MINOR.PATCH; raises ValueError on invalid format.
Tests: inputs
tests/test_action_inputs.py
Expanded tests to cover full, shortened (with/without leading "v"), empty, and invalid tag inputs for both getters; renamed an existing test for clarity.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant GA as GitHub Actions Inputs
  participant AI as ActionInputs
  participant U as utils.normalize_version_tag

  User->>GA: Provide TAG_NAME / FROM_TAG_NAME (e.g., "v1.2")
  GA->>AI: get_tag_name() / get_from_tag_name()
  AI->>U: normalize_version_tag(tag)
  alt Valid short or full semver
    U-->>AI: "vMAJOR.MINOR.PATCH" (e.g., "v1.2.0")
    AI-->>GA: return normalized tag
  else Empty input
    U-->>AI: ""
    AI-->>GA: return ""
  else Invalid format
    U-->>AI: ValueError
    AI-->>GA: propagate error
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Assessment against linked issues

Objective Addressed Explanation
Allow skipping patch version in input assuming 0 (#128)

Assessment against linked issues: Out-of-scope changes

(none)

Suggested reviewers

  • benedeki

Poem

I nibble tags like clover green,
From v1.2 to v1.2.0—so clean! 🐇
Regex burrows, tidy and tight,
Normalized paths in morning light.
Changelog hops with joyful cheer.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/128-Allow-to-skip-patch-version-in-input-assuming-0

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or {PR Summary} to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
release_notes_generator/utils/utils.py (1)

74-101: Don’t force-add a 'v' prefix; preserve user’s prefix and any suffixes.

Unconditionally returning f"v{...}" breaks repos that tag without a 'v'. Preserve the input’s prefix and carry through prerelease/build parts. Consider renaming to a public helper while here.

-def _normalize_version_tag(tag: str) -> str:
+def normalize_version_tag(tag: str) -> str:
@@
-    m = _SEMVER_SHORT_RE.match(tag)
+    m = _SEMVER_SHORT_RE.match(tag)
     if not m:
         raise ValueError(
             f"Invalid version tag format: {tag!r}. " "Expected vMAJOR.MINOR[.PATCH], e.g. 'v0.2' or 'v0.2.0'."
         )
 
-    major = int(m.group("major"))
-    minor = int(m.group("minor"))
-    patch = int(m.group("patch")) if m.group("patch") is not None else 0
+    prefix = m.group("prefix") or ""
+    major = int(m.group("major"))
+    minor = int(m.group("minor"))
+    patch = int(m.group("patch")) if m.group("patch") is not None else 0
+    prerelease = m.group("prerelease")
+    build = m.group("build")
+    pre = f"-{prerelease}" if prerelease else ""
+    bld = f"+{build}" if build else ""
 
-    return f"v{major}.{minor}.{patch}"
+    return f"{prefix}{major}.{minor}.{patch}{pre}{bld}"

Follow-up (tests): add cases for "V1.2", "v1.2.3-rc.1", "1.2.3+build.5", and "1.2-rc.1" → "1.2.0-rc.1".

release_notes_generator/action_inputs.py (1)

352-359: Catch invalid tag formats during validation instead of letting exceptions bubble.

Aligns with existing pattern of aggregating validation errors.

-        tag_name = ActionInputs.get_tag_name()
-        if not isinstance(tag_name, str) or not tag_name.strip():
-            errors.append("Tag name must be a non-empty string.")
-
-        from_tag_name = ActionInputs.get_from_tag_name()
-        if not isinstance(from_tag_name, str):
-            errors.append("From tag name must be a string.")
+        try:
+            tag_name = ActionInputs.get_tag_name()
+        except ValueError as e:
+            errors.append(str(e))
+            tag_name = ""
+        if not isinstance(tag_name, str) or not tag_name.strip():
+            errors.append("Tag name must be a non-empty string.")
+
+        try:
+            from_tag_name = ActionInputs.get_from_tag_name()
+        except ValueError as e:
+            errors.append(str(e))
+            from_tag_name = ""
+        if not isinstance(from_tag_name, str):
+            errors.append("From tag name must be a string.")
🧹 Nitpick comments (5)
README.md (1)

105-106: Clarify accepted short forms to match implementation/tests.

Docs mention only "v0.2"/"v0.1"; tests and code accept bare "0.2"/"0.1" too. Update the inline comments to avoid confusion.

-    tag-name: "v0.2.0"                        # accepts also v0.2 format when patch version is 0
-    from-tag-name: "v0.1.0"                   # accepts also v0.1 format when patch version is 0
+    tag-name: "v0.2.0"                        # also accepts v0.2 or 0.2 when patch version is 0
+    from-tag-name: "v0.1.0"                   # also accepts v0.1 or 0.1 when patch version is 0
release_notes_generator/action_inputs.py (2)

123-125: Use default to ensure str and drop type ignore.

Also switch to the public helper name.

-        raw = get_action_input(TAG_NAME) or ""
-        return _normalize_version_tag(raw)
+        raw = get_action_input(TAG_NAME, default="")
+        return normalize_version_tag(raw)

131-133: Drop the type ignore; default enforces str.

Also use the public helper.

-        raw = get_action_input(FROM_TAG_NAME, default="")
-        return _normalize_version_tag(raw)  # type: ignore[arg-type]
+        raw = get_action_input(FROM_TAG_NAME, default="")
+        return normalize_version_tag(raw)
tests/test_action_inputs.py (2)

116-124: Rename “shorted” → “shortened”.

Minor grammar fix in test names.

-def test_get_tag_name_version_shorted_with_v(mocker):
+def test_get_tag_name_version_shortened_with_v(mocker):
@@
-def test_get_tag_name_version_shorted_no_v(mocker):
+def test_get_tag_name_version_shortened_no_v(mocker):

143-151: Rename “shorted” → “shortened”.

Keep naming consistent with the tag-name tests.

-def test_get_from_tag_name_version_shorted_with_v(mocker):
+def test_get_from_tag_name_version_shortened_with_v(mocker):
@@
-def test_get_from_tag_name_version_shorted_no_v(mocker):
+def test_get_from_tag_name_version_shortened_no_v(mocker):
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4e71f70 and 3e653dc.

📒 Files selected for processing (4)
  • README.md (1 hunks)
  • release_notes_generator/action_inputs.py (3 hunks)
  • release_notes_generator/utils/utils.py (2 hunks)
  • tests/test_action_inputs.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
tests/test_action_inputs.py (1)
release_notes_generator/action_inputs.py (2)
  • get_tag_name (119-124)
  • get_from_tag_name (127-132)
release_notes_generator/action_inputs.py (2)
release_notes_generator/utils/utils.py (1)
  • _normalize_version_tag (74-100)
release_notes_generator/utils/gh_action.py (1)
  • get_action_input (26-38)
🔇 Additional comments (4)
release_notes_generator/action_inputs.py (1)

420-423: LGTM: extra debug context.

Logging from-tag improves diagnosability.

tests/test_action_inputs.py (3)

111-114: LGTM: full version passthrough covered.


126-135: LGTM: invalid format raises ValueError with clear message.


158-163: LGTM: mirrors invalid-format coverage for from-tag.

Comment on lines +60 to +71
_SEMVER_SHORT_RE = re.compile(
r"""
^\s* # optional leading whitespace
v? # optional leading 'v'
(?P<major>\d+) # major
\. # dot
(?P<minor>\d+) # minor
(?:\.(?P<patch>\d+))? # optional .patch
\s*$ # optional trailing whitespace
""",
re.VERBOSE,
)
Copy link

@coderabbitai coderabbitai bot Aug 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Avoid enforcing lowercase 'v'; support uppercase 'V', pre-release, and build metadata.

Current regex disallows common tags like V1.2.3, v1.2.3-rc.1, or v1.2.3+build. It also makes it hard to preserve/omit the prefix consistently.

-_SEMVER_SHORT_RE = re.compile(
-    r"""
-    ^\s*                # optional leading whitespace
-    v?                  # optional leading 'v'
-    (?P<major>\d+)      # major
-    \.                  # dot
-    (?P<minor>\d+)      # minor
-    (?:\.(?P<patch>\d+))?  # optional .patch
-    \s*$                # optional trailing whitespace
-""",
-    re.VERBOSE,
-)
+_SEMVER_SHORT_RE = re.compile(
+    r"""
+    ^\s*
+    (?P<prefix>[vV])?                  # optional leading 'v' or 'V'
+    (?P<major>0|[1-9]\d*)              # major (no leading zeros unless zero)
+    \.
+    (?P<minor>0|[1-9]\d*)              # minor
+    (?:\.(?P<patch>0|[1-9]\d*))?       # optional .patch
+    (?:-(?P<prerelease>[0-9A-Za-z.-]+))?  # optional -prerelease
+    (?:\+(?P<build>[0-9A-Za-z.-]+))?      # optional +build
+    \s*$
+""",
+    re.VERBOSE,
+)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_SEMVER_SHORT_RE = re.compile(
r"""
^\s* # optional leading whitespace
v? # optional leading 'v'
(?P<major>\d+) # major
\. # dot
(?P<minor>\d+) # minor
(?:\.(?P<patch>\d+))? # optional .patch
\s*$ # optional trailing whitespace
""",
re.VERBOSE,
)
_SEMVER_SHORT_RE = re.compile(
r"""
^\s*
(?P<prefix>[vV])? # optional leading 'v' or 'V'
(?P<major>0|[1-9]\d*) # major (no leading zeros unless zero)
\.
(?P<minor>0|[1-9]\d*) # minor
(?:\.(?P<patch>0|[1-9]\d*))? # optional .patch
(?:-(?P<prerelease>[0-9A-Za-z.-]+))? # optional -prerelease
(?:\+(?P<build>[0-9A-Za-z.-]+))? # optional +build
\s*$
""",
re.VERBOSE,
)
🤖 Prompt for AI Agents
In release_notes_generator/utils/utils.py around lines 60 to 71, the SEMVER
short regex currently only accepts a lowercase 'v' and no pre-release or build
metadata; update the pattern to accept an optional prefix (v or V) and optional
pre-release (-...) and build (+...) components and capture them with named
groups so the caller can consistently preserve or omit the prefix and metadata.
Concretely: change the leading v? to accept either case (use re.IGNORECASE or
[vV] and/or add a named group like (?P<prefix>v)?), add optional named groups
(?P<prerelease>-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))? and
(?P<build>\+(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))? after the patch, keep patch
optional as before, and retain re.VERBOSE (or combine with re.IGNORECASE) so the
function can parse tags like V1.2.3, v1.2.3-rc.1, and v1.2.3+build and return
consistent captures for prefix, prerelease, and build.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to like it is now.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@miroslavpojer, understood! I respect your decision to keep the regex pattern simple and focused on your specific use case. The current implementation effectively handles the core requirement of allowing patch versions to be omitted while defaulting to .0, which aligns perfectly with the PR objectives.

Keeping it straightforward and avoiding additional complexity like pre-release versions or build metadata makes sense for your current needs.

🐰
nodding thoughtfully


✏️ Learnings added
Learnt from: miroslavpojer
PR: AbsaOSS/generate-release-notes#158
File: release_notes_generator/utils/utils.py:60-71
Timestamp: 2025-08-28T08:39:30.135Z
Learning: In the generate-release-notes project, the user miroslavpojer prefers to keep the SEMVER regex pattern simple, focusing only on basic major.minor[.patch] format with lowercase 'v' prefix support, rather than expanding it to handle uppercase 'V', pre-release versions, or build metadata.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
release_notes_generator/utils/utils.py (2)

87-94: Trim once and make the error message reflect accepted inputs.

Single trim avoids double work, and error text should mention both with/without 'v' forms since you accept them.

-    if not tag or tag.strip() == "":
-        return ""
-
-    m = _SEMVER_SHORT_RE.match(tag)
+    s = tag.strip()
+    if not s:
+        return ""
+
+    m = _SEMVER_SHORT_RE.match(s)
     if not m:
-        raise ValueError(
-            f"Invalid version tag format: {tag!r}. " "Expected vMAJOR.MINOR[.PATCH], e.g. 'v0.2' or 'v0.2.0'."
-        )
+        raise ValueError(
+            f"Invalid version tag format: {tag!r}. "
+            "Expected [v]MAJOR.MINOR[.PATCH], e.g. 'v0.2', '0.2', '1.2.3', or 'v1.2.3'."
+        )

35-42: Docstring return description is incorrect for get_change_url.

Function returns an empty string on missing repository, not None. Adjust wording for accuracy.

-    @return: An optional string containing the URL to view the changes. Returns a None if the repository is not set.
+    @return: A string containing the URL to view the changes. Returns an empty string if the repository is not set.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3e653dc and c91591d.

📒 Files selected for processing (2)
  • release_notes_generator/action_inputs.py (3 hunks)
  • release_notes_generator/utils/utils.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • release_notes_generator/action_inputs.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: miroslavpojer
PR: AbsaOSS/generate-release-notes#158
File: release_notes_generator/utils/utils.py:60-71
Timestamp: 2025-08-28T08:39:30.135Z
Learning: In the generate-release-notes project, the user miroslavpojer prefers to keep the SEMVER regex pattern simple, focusing only on basic major.minor[.patch] format with lowercase 'v' prefix support, rather than expanding it to handle uppercase 'V', pre-release versions, or build metadata.
📚 Learning: 2025-08-28T08:39:30.135Z
Learnt from: miroslavpojer
PR: AbsaOSS/generate-release-notes#158
File: release_notes_generator/utils/utils.py:60-71
Timestamp: 2025-08-28T08:39:30.135Z
Learning: In the generate-release-notes project, the user miroslavpojer prefers to keep the SEMVER regex pattern simple, focusing only on basic major.minor[.patch] format with lowercase 'v' prefix support, rather than expanding it to handle uppercase 'V', pre-release versions, or build metadata.

Applied to files:

  • release_notes_generator/utils/utils.py
🔇 Additional comments (2)
release_notes_generator/utils/utils.py (2)

22-22: Import for regex usage — LGTM.


60-71: Regex scope matches the PR objective and stated preference.

Pattern cleanly supports MAJOR.MINOR[.PATCH] with optional lowercase 'v' and rejects pre-release/build metadata, aligning with your preference to keep the regex simple and lowercase-only.

@miroslavpojer miroslavpojer merged commit eda69f4 into master Aug 28, 2025
7 checks passed
@miroslavpojer miroslavpojer deleted the feature/128-Allow-to-skip-patch-version-in-input-assuming-0 branch August 28, 2025 08:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow to skip patch version in input assuming 0

2 participants