diff --git a/specfile/specfile.py b/specfile/specfile.py index d1e69d7c..e13f75c9 100644 --- a/specfile/specfile.py +++ b/specfile/specfile.py @@ -3,6 +3,7 @@ import contextlib import datetime +import re import subprocess import types from pathlib import Path @@ -266,3 +267,95 @@ def add_changelog_entry( elif email is not None: author += f" <{email}>" changelog.append(ChangelogEntry.assemble(timestamp, author, entry, evr)) + + @property + def version(self) -> str: + """Version string as stored in the spec file.""" + with self.tags() as tags: + return tags.version.value + + @version.setter + def version(self, value: str) -> None: + with self.tags() as tags: + tags.version.value = value + + @property + def expanded_version(self) -> str: + """Version string with macros expanded.""" + with self.tags() as tags: + return tags.version.expanded_value + + @staticmethod + def _split_raw_release(raw_release: str) -> Tuple[str, str]: + """ + Splits raw release string into release and dist parts. + + Args: + raw_release: Raw release string. + + Returns: + Tuple of (release, dist). + """ + tokens = re.split(r"(%(?P\{\??)?dist(?(m)\}))$", raw_release, maxsplit=1)[:2] + if len(tokens) == 2: + return tokens[0], tokens[1] + return tokens[0], "" + + @classmethod + def _get_updated_release(cls, raw_release: str, release: str) -> str: + """ + Returns the specified raw release string updated with the specified release. + + Args: + raw_release: Raw release string. + release: New release. + + Returns: + Updated raw release string. + """ + _, dist = cls._split_raw_release(raw_release) + return f"{release}{dist}" + + @property + def release(self) -> str: + """Release string without the dist suffix.""" + release, _ = self._split_raw_release(self.raw_release) + return release + + @release.setter + def release(self, value: str) -> None: + self.raw_release = self._get_updated_release(self.raw_release, value) + + @property + def raw_release(self) -> str: + """Release string as stored in the spec file.""" + with self.tags() as tags: + return tags.release.value + + @raw_release.setter + def raw_release(self, value: str) -> None: + with self.tags() as tags: + tags.release.value = value + + @property + def expanded_release(self) -> str: + """Release string without the dist suffix with macros expanded.""" + return self.expand(self.release) + + @property + def expanded_raw_release(self) -> str: + """Release string with macros expanded.""" + with self.tags() as tags: + return tags.release.expanded_value + + def set_version_and_release(self, version: str, release: str = "1") -> None: + """ + Sets both version and release at the same time. + + Args: + version: Version string. + release: Release string, defaults to '1'. + """ + with self.tags() as tags: + tags.version.value = version + tags.release.value = self._get_updated_release(tags.release.value, release) diff --git a/tests/integration/test_specfile.py b/tests/integration/test_specfile.py index cca8ad5a..13379578 100644 --- a/tests/integration/test_specfile.py +++ b/tests/integration/test_specfile.py @@ -5,6 +5,7 @@ import subprocess import pytest +import rpm from flexmock import flexmock from specfile.exceptions import SpecfileException @@ -172,3 +173,28 @@ def test_add_changelog_entry( spec.add_changelog_entry(entry, author, email, timestamp) with spec.sections() as sections: assert sections.changelog[: len(result)] == result + + +@pytest.mark.parametrize( + "version, release", + [ + ("0.2", "3"), + ("67", "1"), + ("1.4.6", "0.1rc5"), + ], +) +def test_set_version_and_release(spec_minimal, version, release): + spec = Specfile(spec_minimal) + spec.set_version_and_release(version, release) + assert spec.version == version + assert spec.release == release + assert spec.raw_release.startswith(release) + with spec.tags() as tags: + assert tags.version.value == spec.version + assert tags.release.value == spec.raw_release + assert spec._spec.sourceHeader[rpm.RPMTAG_VERSION] == spec.expanded_version + assert spec._spec.sourceHeader[rpm.RPMTAG_RELEASE] == spec.expanded_raw_release + spec.raw_release = release + with spec.tags() as tags: + assert tags.release.value == release + assert spec._spec.sourceHeader[rpm.RPMTAG_RELEASE] == spec.expanded_raw_release diff --git a/tests/unit/test_specfile.py b/tests/unit/test_specfile.py new file mode 100644 index 00000000..0cd0c8aa --- /dev/null +++ b/tests/unit/test_specfile.py @@ -0,0 +1,32 @@ +# Copyright Contributors to the Packit project. +# SPDX-License-Identifier: MIT + +import pytest + +from specfile.specfile import Specfile + + +@pytest.mark.parametrize( + "raw_release, release, dist", + [ + ("1%{?dist}", "1", "%{?dist}"), + ("0.1%{prerelease}%{dist}", "0.1%{prerelease}", "%{dist}"), + ("28%dist", "28", "%dist"), + ("%{release_string}", "%{release_string}", ""), + ], +) +def test_split_raw_release(raw_release, release, dist): + assert Specfile._split_raw_release(raw_release) == (release, dist) + + +@pytest.mark.parametrize( + "raw_release, release, result", + [ + ("1%{?dist}", "28", "28%{?dist}"), + ("0.1%{prerelease}%{dist}", "1", "1%{dist}"), + ("28%dist", "0.1", "0.1%dist"), + ("%{release_string}", "1", "1"), + ], +) +def test_get_updated_release(raw_release, release, result): + assert Specfile._get_updated_release(raw_release, release) == result