mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-03-12 13:03:39 +00:00
refactor: separate package_archives method from trigger
This commit is contained in:
@@ -17,14 +17,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from collections.abc import Callable
|
|
||||||
from functools import cmp_to_key
|
|
||||||
|
|
||||||
from ahriman.core import context
|
from ahriman.core import context
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.repository import Repository
|
||||||
from ahriman.core.triggers import Trigger
|
from ahriman.core.triggers import Trigger
|
||||||
from ahriman.core.utils import package_like
|
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.repository_id import RepositoryId
|
from ahriman.models.repository_id import RepositoryId
|
||||||
from ahriman.models.result import Result
|
from ahriman.models.result import Result
|
||||||
@@ -78,27 +74,20 @@ class ArchiveRotationTrigger(Trigger):
|
|||||||
"""
|
"""
|
||||||
return list(cls.CONFIGURATION_SCHEMA.keys())
|
return list(cls.CONFIGURATION_SCHEMA.keys())
|
||||||
|
|
||||||
def archives_remove(self, package: Package, pacman: Pacman) -> None:
|
def archives_remove(self, package: Package, repository: Repository) -> None:
|
||||||
"""
|
"""
|
||||||
remove older versions of the specified package
|
remove older versions of the specified package
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
package(Package): package which has been updated to check for older versions
|
package(Package): package which has been updated to check for older versions
|
||||||
pacman(Pacman): alpm wrapper instance
|
repository(Repository): repository instance
|
||||||
"""
|
"""
|
||||||
# explicit guard to skip process in case if rotation is disabled
|
# explicit guard to skip process in case if rotation is disabled
|
||||||
# this guard is supposed to speedup process
|
# this guard is supposed to speedup process
|
||||||
if self.keep_built_packages == 0:
|
if self.keep_built_packages == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
packages: dict[tuple[str, str], Package] = {}
|
to_remove = repository.package_archives(package.base)
|
||||||
# we can't use here load_archives, because it ignores versions
|
|
||||||
for full_path in filter(package_like, self.paths.archive_for(package.base).iterdir()):
|
|
||||||
local = Package.from_archive(full_path, pacman)
|
|
||||||
packages.setdefault((local.base, local.version), local).packages.update(local.packages)
|
|
||||||
|
|
||||||
comparator: Callable[[Package, Package], int] = lambda left, right: left.vercmp(right.version)
|
|
||||||
to_remove = sorted(packages.values(), key=cmp_to_key(comparator))
|
|
||||||
|
|
||||||
for single in to_remove[:-self.keep_built_packages]:
|
for single in to_remove[:-self.keep_built_packages]:
|
||||||
self.logger.info("removing version %s of package %s", single.version, single.base)
|
self.logger.info("removing version %s of package %s", single.version, single.base)
|
||||||
@@ -115,7 +104,7 @@ class ArchiveRotationTrigger(Trigger):
|
|||||||
packages(list[Package]): list of all available packages
|
packages(list[Package]): list of all available packages
|
||||||
"""
|
"""
|
||||||
ctx = context.get()
|
ctx = context.get()
|
||||||
pacman = ctx.get(Pacman)
|
repository = ctx.get(Repository)
|
||||||
|
|
||||||
for package in result.success:
|
for package in result.success:
|
||||||
self.archives_remove(package, pacman)
|
self.archives_remove(package, repository)
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
#
|
#
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from collections.abc import Iterable
|
from collections.abc import Callable, Iterable
|
||||||
|
from functools import cmp_to_key
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
@@ -102,6 +103,27 @@ class PackageInfo(RepositoryProperties):
|
|||||||
self.logger.exception("could not load package from %s", full_path)
|
self.logger.exception("could not load package from %s", full_path)
|
||||||
return list(result.values())
|
return list(result.values())
|
||||||
|
|
||||||
|
def package_archives(self, package_base: str) -> list[Package]:
|
||||||
|
"""
|
||||||
|
load list of packages known for this package base. This method unlike
|
||||||
|
:func:`ahriman.core.repository.package_info.PackageInfo.load_archives` scans archive directory and loads all
|
||||||
|
versions available for the ``package_base``
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str): package base
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Package]: list of packages belonging to this base, sorted by version by ascension
|
||||||
|
"""
|
||||||
|
packages: dict[tuple[str, str], Package] = {}
|
||||||
|
# we can't use here load_archives, because it ignores versions
|
||||||
|
for full_path in filter(package_like, self.paths.archive_for(package_base).iterdir()):
|
||||||
|
local = Package.from_archive(full_path, self.pacman)
|
||||||
|
packages.setdefault((local.base, local.version), local).packages.update(local.packages)
|
||||||
|
|
||||||
|
comparator: Callable[[Package, Package], int] = lambda left, right: left.vercmp(right.version)
|
||||||
|
return sorted(packages.values(), key=cmp_to_key(comparator))
|
||||||
|
|
||||||
def package_changes(self, package: Package, last_commit_sha: str) -> Changes | None:
|
def package_changes(self, package: Package, last_commit_sha: str) -> Changes | None:
|
||||||
"""
|
"""
|
||||||
extract package change for the package since last commit if available
|
extract package change for the package since last commit if available
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ import pytest
|
|||||||
from dataclasses import replace
|
from dataclasses import replace
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from typing import Any
|
|
||||||
from unittest.mock import call as MockCall
|
from unittest.mock import call as MockCall
|
||||||
|
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.housekeeping import ArchiveRotationTrigger
|
from ahriman.core.housekeeping import ArchiveRotationTrigger
|
||||||
|
from ahriman.core.repository import Repository
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.result import Result
|
from ahriman.models.result import Result
|
||||||
|
|
||||||
@@ -21,26 +20,24 @@ def test_configuration_sections(configuration: Configuration) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_archives_remove(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
def test_archives_remove(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
||||||
pacman: Pacman, mocker: MockerFixture) -> None:
|
repository: Repository, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must remove older packages
|
must remove older packages
|
||||||
"""
|
"""
|
||||||
def package(version: Any, *args: Any, **kwargs: Any) -> Package:
|
packages = []
|
||||||
generated = replace(package_ahriman, version=str(version))
|
for i in range(5):
|
||||||
|
generated = replace(package_ahriman, version=str(i))
|
||||||
generated.packages = {
|
generated.packages = {
|
||||||
key: replace(value, filename=str(version))
|
key: replace(value, filename=str(i))
|
||||||
for key, value in generated.packages.items()
|
for key, value in generated.packages.items()
|
||||||
}
|
}
|
||||||
return generated
|
packages.append(generated)
|
||||||
|
|
||||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=packages)
|
||||||
mocker.patch("ahriman.core.housekeeping.archive_rotation_trigger.package_like", return_value=True)
|
|
||||||
mocker.patch("pathlib.Path.glob", return_value=[Path(str(i)) for i in range(5)])
|
mocker.patch("pathlib.Path.glob", return_value=[Path(str(i)) for i in range(5)])
|
||||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path(str(i)) for i in range(5)])
|
|
||||||
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=package)
|
|
||||||
unlink_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
|
unlink_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
|
||||||
|
|
||||||
archive_rotation_trigger.archives_remove(package_ahriman, pacman)
|
archive_rotation_trigger.archives_remove(package_ahriman, repository)
|
||||||
unlink_mock.assert_has_calls([
|
unlink_mock.assert_has_calls([
|
||||||
MockCall(Path("0")),
|
MockCall(Path("0")),
|
||||||
MockCall(Path("1")),
|
MockCall(Path("1")),
|
||||||
@@ -48,28 +45,15 @@ def test_archives_remove(archive_rotation_trigger: ArchiveRotationTrigger, packa
|
|||||||
|
|
||||||
|
|
||||||
def test_archives_remove_keep(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
def test_archives_remove_keep(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
||||||
pacman: Pacman, mocker: MockerFixture) -> None:
|
repository: Repository, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must keep all packages if set to
|
must keep all packages if set to
|
||||||
"""
|
"""
|
||||||
def package(version: Any, *args: Any, **kwargs: Any) -> Package:
|
archives_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives")
|
||||||
generated = replace(package_ahriman, version=str(version))
|
|
||||||
generated.packages = {
|
|
||||||
key: replace(value, filename=str(version))
|
|
||||||
for key, value in generated.packages.items()
|
|
||||||
}
|
|
||||||
return generated
|
|
||||||
|
|
||||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
|
||||||
mocker.patch("ahriman.core.housekeeping.archive_rotation_trigger.package_like", return_value=True)
|
|
||||||
mocker.patch("pathlib.Path.glob", return_value=[Path(str(i)) for i in range(5)])
|
|
||||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path(str(i)) for i in range(5)])
|
|
||||||
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=package)
|
|
||||||
unlink_mock = mocker.patch("pathlib.Path.unlink", autospec=True)
|
|
||||||
|
|
||||||
archive_rotation_trigger.keep_built_packages = 0
|
archive_rotation_trigger.keep_built_packages = 0
|
||||||
archive_rotation_trigger.archives_remove(package_ahriman, pacman)
|
archive_rotation_trigger.archives_remove(package_ahriman, repository)
|
||||||
unlink_mock.assert_not_called()
|
archives_mock.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_on_result(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
def test_on_result(archive_rotation_trigger: ArchiveRotationTrigger, package_ahriman: Package,
|
||||||
|
|||||||
@@ -91,6 +91,30 @@ def test_load_archives_different_version(package_info: PackageInfo, package_pyth
|
|||||||
assert packages[0].version == package_python_schedule.version
|
assert packages[0].version == package_python_schedule.version
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_archives(package_info: PackageInfo, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must load package archives sorted by version
|
||||||
|
"""
|
||||||
|
from dataclasses import replace
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
def package(version: Any, *args: Any, **kwargs: Any) -> Package:
|
||||||
|
generated = replace(package_ahriman, version=str(version))
|
||||||
|
generated.packages = {
|
||||||
|
key: replace(value, filename=str(version))
|
||||||
|
for key, value in generated.packages.items()
|
||||||
|
}
|
||||||
|
return generated
|
||||||
|
|
||||||
|
mocker.patch("ahriman.core.repository.package_info.package_like", return_value=True)
|
||||||
|
mocker.patch("pathlib.Path.iterdir", return_value=[Path(str(i)) for i in range(5)])
|
||||||
|
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=package)
|
||||||
|
|
||||||
|
result = package_info.package_archives(package_ahriman.base)
|
||||||
|
assert len(result) == 5
|
||||||
|
assert [p.version for p in result] == [str(i) for i in range(5)]
|
||||||
|
|
||||||
|
|
||||||
def test_package_changes(package_info: PackageInfo, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_changes(package_info: PackageInfo, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must load package changes
|
must load package changes
|
||||||
|
|||||||
Reference in New Issue
Block a user