diff --git a/specfile/specfile.py b/specfile/specfile.py index 094727a..92b8d8f 100644 --- a/specfile/specfile.py +++ b/specfile/specfile.py @@ -989,3 +989,50 @@ def handle_prerelease(version): return version[:base_end] + "~" + version[suffix_start:] self.update_tag("Version", handle_prerelease(version)) + + @staticmethod + def _bump_release_string(release_string: str) -> str: + """ + Bumps release string. Follows the logic of `rpmdev-bumpspec`. + + Args: + release_string: Release string to be bumped. + + Returns: + Bumped release string. + """ + m = re.match( + r"^(?P%release_func\s+)?(?P
0\.)?(?P\d+)(?P.*)$",
+            release_string,
+        )
+        if m and (
+            m.group("pre")
+            or all(x not in m.group("post") for x in ["alpha", "beta", "rc"])
+        ):
+            return (
+                (m.group("func") or "")
+                + (m.group("pre") or "")
+                + str(int(m.group("rel")) + 1)
+                + m.group("post")
+            )
+        m = re.match(r"^(?P
.+\.)(?P\d+)$", release_string)
+        if m:
+            return m.group("pre") + str(int(m.group("rel")) + 1)
+        return release_string + ".1"
+
+    def bump_release(self) -> None:
+        """
+        Tries to bump release. Follows the logic of `rpmdev-bumpspec`, first trying to update
+        macro definitions that seem to define a release, then trying to update value
+        of the Release tag.
+        """
+        if self.has_autorelease:
+            return
+
+        with self.macro_definitions() as macro_definitions:
+            for md in macro_definitions:
+                if md.name.lower() in ["release", "baserelease"]:
+                    md.body = self._bump_release_string(md.body)
+                    return
+
+        self.raw_release = self._bump_release_string(self.raw_release)
diff --git a/tests/unit/test_specfile.py b/tests/unit/test_specfile.py
index b308e18..b02c337 100644
--- a/tests/unit/test_specfile.py
+++ b/tests/unit/test_specfile.py
@@ -32,3 +32,31 @@ def test_split_raw_release(raw_release, release, dist, minorbump):
 )
 def test_get_updated_release(raw_release, release, result):
     assert Specfile._get_updated_release(raw_release, release) == result
+
+
+@pytest.mark.parametrize(
+    "release, bumped_release",
+    [
+        ("1%{?dist}", "2%{?dist}"),
+        ("0.1%{?dist}", "0.2%{?dist}"),
+        ("%release_func 26", "%release_func 27"),
+        ("0.24.rc1%{?dist}", "0.25.rc1%{?dist}"),
+        ("0.2.%{prerel}%{?dist}", "0.3.%{prerel}%{?dist}"),
+        (
+            "0.8.%{commitdate}%{shortcommit}%{?dist}",
+            "0.9.%{commitdate}%{shortcommit}%{?dist}",
+        ),
+        (
+            "3.%{git_date}git%{git_commit_short}%{?dist}",
+            "4.%{git_date}git%{git_commit_short}%{?dist}",
+        ),
+        ("1%{?rcrel}%{?dist}.1", "1%{?rcrel}%{?dist}.2"),
+        (
+            "%{?beta_ver:0.}%{fedora_rel}%{?beta_ver:.%beta_ver}%{?dist}%{flagrel}%{?extrarel}",
+            "%{?beta_ver:0.}%{fedora_rel}%{?beta_ver:.%beta_ver}%{?dist}%{flagrel}%{?extrarel}.1",
+        ),
+        ("4.rc2%{?dist}", "4.rc2%{?dist}.1"),
+    ],
+)
+def test_bump_release_string(release, bumped_release):
+    assert Specfile._bump_release_string(release) == bumped_release