mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-02-24 13:49:48 +00:00
refactor: drop some methods from package class into separated wrappers
This commit is contained in:
@@ -12,6 +12,14 @@ ahriman.core.build\_tools.package\_archive module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.build\_tools.package\_version module
|
||||
-------------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.build_tools.package_version
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.build\_tools.sources module
|
||||
----------------------------------------
|
||||
|
||||
|
||||
117
src/ahriman/core/build_tools/package_version.py
Normal file
117
src/ahriman/core/build_tools/package_version.py
Normal file
@@ -0,0 +1,117 @@
|
||||
#
|
||||
# Copyright (c) 2021-2026 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from pyalpm import vercmp # type: ignore[import-not-found]
|
||||
|
||||
from ahriman.core.build_tools.task import Task
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.log import LazyLogging
|
||||
from ahriman.core.utils import full_version, utcnow
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.pkgbuild import Pkgbuild
|
||||
|
||||
|
||||
class PackageVersion(LazyLogging):
|
||||
"""
|
||||
package version extractor and helper
|
||||
|
||||
Attributes:
|
||||
package(Package): package definitions
|
||||
"""
|
||||
|
||||
def __init__(self, package: Package) -> None:
|
||||
"""
|
||||
Args:
|
||||
package(Package): package definitions
|
||||
"""
|
||||
self.package = package
|
||||
|
||||
def actual_version(self, configuration: Configuration) -> str:
|
||||
"""
|
||||
additional method to handle VCS package versions
|
||||
|
||||
Args:
|
||||
configuration(Configuration): configuration instance
|
||||
|
||||
Returns:
|
||||
str: package version if package is not VCS and current version according to VCS otherwise
|
||||
"""
|
||||
if not self.package.is_vcs:
|
||||
return self.package.version
|
||||
|
||||
_, repository_id = configuration.check_loaded()
|
||||
paths = configuration.repository_paths
|
||||
task = Task(self.package, configuration, repository_id.architecture, paths)
|
||||
|
||||
try:
|
||||
# create fresh chroot environment, fetch sources and - automagically - update PKGBUILD
|
||||
task.init(paths.cache_for(self.package.base), [], None)
|
||||
pkgbuild = Pkgbuild.from_file(paths.cache_for(self.package.base) / "PKGBUILD")
|
||||
|
||||
return full_version(pkgbuild.get("epoch"), pkgbuild["pkgver"], pkgbuild["pkgrel"])
|
||||
except Exception:
|
||||
self.logger.exception("cannot determine version of VCS package")
|
||||
finally:
|
||||
# clear log files generated by devtools
|
||||
for log_file in paths.cache_for(self.package.base).glob("*.log"):
|
||||
log_file.unlink()
|
||||
|
||||
return self.package.version
|
||||
|
||||
def is_newer_than(self, timestamp: float | int) -> bool:
|
||||
"""
|
||||
check if package was built after the specified timestamp
|
||||
|
||||
Args:
|
||||
timestamp(float | int): timestamp to check build date against
|
||||
|
||||
Returns:
|
||||
bool: ``True`` in case if package was built after the specified date and ``False`` otherwise.
|
||||
In case if build date is not set by any of packages, it returns False
|
||||
"""
|
||||
return any(
|
||||
package.build_date > timestamp
|
||||
for package in self.package.packages.values()
|
||||
if package.build_date is not None
|
||||
)
|
||||
|
||||
def is_outdated(self, remote: Package, configuration: Configuration, *,
|
||||
calculate_version: bool = True) -> bool:
|
||||
"""
|
||||
check if package is out-of-dated
|
||||
|
||||
Args:
|
||||
remote(Package): package properties from remote source
|
||||
configuration(Configuration): configuration instance
|
||||
calculate_version(bool, optional): expand version to actual value (by calculating git versions)
|
||||
(Default value = True)
|
||||
|
||||
Returns:
|
||||
bool: ``True`` if the package is out-of-dated and ``False`` otherwise
|
||||
"""
|
||||
vcs_allowed_age = configuration.getint("build", "vcs_allowed_age", fallback=0)
|
||||
min_vcs_build_date = utcnow().timestamp() - vcs_allowed_age
|
||||
|
||||
if calculate_version and not self.is_newer_than(min_vcs_build_date):
|
||||
remote_version = PackageVersion(remote).actual_version(configuration)
|
||||
else:
|
||||
remote_version = remote.version
|
||||
|
||||
result: int = vercmp(self.package.version, remote_version)
|
||||
return result < 0
|
||||
@@ -27,6 +27,7 @@ from ahriman.core.exceptions import CalledProcessError
|
||||
from ahriman.core.log import LazyLogging
|
||||
from ahriman.core.utils import check_output, utcnow, walk
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.pkgbuild import Pkgbuild
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
from ahriman.models.remote_source import RemoteSource
|
||||
from ahriman.models.repository_paths import RepositoryPaths
|
||||
@@ -81,7 +82,7 @@ class Sources(LazyLogging):
|
||||
Returns:
|
||||
list[PkgbuildPatch]: generated patch for PKGBUILD architectures if required
|
||||
"""
|
||||
architectures = Package.supported_architectures(sources_dir)
|
||||
architectures = Pkgbuild.supported_architectures(sources_dir)
|
||||
if "any" in architectures: # makepkg does not like when there is any other arch except for any
|
||||
return []
|
||||
architectures.add(architecture)
|
||||
@@ -161,7 +162,7 @@ class Sources(LazyLogging):
|
||||
cwd=sources_dir, logger=instance.logger)
|
||||
|
||||
# extract local files...
|
||||
files = ["PKGBUILD", ".SRCINFO"] + [str(path) for path in Package.local_files(sources_dir)]
|
||||
files = ["PKGBUILD", ".SRCINFO"] + [str(path) for path in Pkgbuild.local_files(sources_dir)]
|
||||
instance.add(sources_dir, *files)
|
||||
# ...and commit them
|
||||
instance.commit(sources_dir)
|
||||
|
||||
@@ -17,10 +17,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import copy
|
||||
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
from ahriman.core.build_tools.package_version import PackageVersion
|
||||
from ahriman.core.build_tools.sources import Sources
|
||||
from ahriman.core.repository.repository_properties import RepositoryProperties
|
||||
from ahriman.core.utils import package_like
|
||||
@@ -33,6 +36,40 @@ class PackageInfo(RepositoryProperties):
|
||||
handler for the package information
|
||||
"""
|
||||
|
||||
def full_depends(self, package: Package, packages: Iterable[Package]) -> list[str]:
|
||||
"""
|
||||
generate full dependencies list including transitive dependencies
|
||||
|
||||
Args:
|
||||
package(Package): package to check dependencies for
|
||||
packages(Iterable[Package]): repository package list
|
||||
|
||||
Returns:
|
||||
list[str]: all dependencies of the package
|
||||
"""
|
||||
dependencies = {}
|
||||
# load own package dependencies
|
||||
for package_base in packages:
|
||||
for name, repo_package in package_base.packages.items():
|
||||
dependencies[name] = repo_package.depends
|
||||
for provides in repo_package.provides:
|
||||
dependencies[provides] = repo_package.depends
|
||||
# load repository dependencies
|
||||
for database in self.pacman.handle.get_syncdbs():
|
||||
for pacman_package in database.pkgcache:
|
||||
dependencies[pacman_package.name] = pacman_package.depends
|
||||
for provides in pacman_package.provides:
|
||||
dependencies[provides] = pacman_package.depends
|
||||
|
||||
result = set(package.depends)
|
||||
current_depends: set[str] = set()
|
||||
while result != current_depends:
|
||||
current_depends = copy.deepcopy(result)
|
||||
for package_name in current_depends:
|
||||
result.update(dependencies.get(package_name, []))
|
||||
|
||||
return sorted(result)
|
||||
|
||||
def load_archives(self, packages: Iterable[Path]) -> list[Package]:
|
||||
"""
|
||||
load packages from list of archives
|
||||
@@ -58,7 +95,7 @@ class PackageInfo(RepositoryProperties):
|
||||
# force version to max of them
|
||||
self.logger.warning("version of %s differs, found %s and %s",
|
||||
current.base, current.version, local.version)
|
||||
if current.is_outdated(local, self.configuration, calculate_version=False):
|
||||
if PackageVersion(current).is_outdated(local, self.configuration, calculate_version=False):
|
||||
current.version = local.version
|
||||
current.packages.update(local.packages)
|
||||
except Exception:
|
||||
@@ -130,5 +167,5 @@ class PackageInfo(RepositoryProperties):
|
||||
return [
|
||||
package
|
||||
for package in packages
|
||||
if depends_on.intersection(package.full_depends(self.pacman, packages))
|
||||
if depends_on.intersection(self.full_depends(package, packages))
|
||||
]
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#
|
||||
from collections.abc import Iterable
|
||||
|
||||
from ahriman.core.build_tools.package_version import PackageVersion
|
||||
from ahriman.core.build_tools.sources import Sources
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.core.repository.cleaner import Cleaner
|
||||
@@ -68,7 +69,7 @@ class UpdateHandler(PackageInfo, Cleaner):
|
||||
try:
|
||||
remote = load_remote(local)
|
||||
|
||||
if local.is_outdated(remote, self.configuration, calculate_version=vcs):
|
||||
if PackageVersion(local).is_outdated(remote, self.configuration, calculate_version=vcs):
|
||||
self.reporter.set_pending(local.base)
|
||||
self.event(local.base, EventType.PackageOutdated, "Remote version is newer than local")
|
||||
result.append(remote)
|
||||
@@ -157,7 +158,7 @@ class UpdateHandler(PackageInfo, Cleaner):
|
||||
if local.remote.is_remote:
|
||||
continue # avoid checking AUR packages
|
||||
|
||||
if local.is_outdated(remote, self.configuration, calculate_version=vcs):
|
||||
if PackageVersion(local).is_outdated(remote, self.configuration, calculate_version=vcs):
|
||||
self.reporter.set_pending(local.base)
|
||||
self.event(local.base, EventType.PackageOutdated, "Locally pulled sources are outdated")
|
||||
result.append(remote)
|
||||
|
||||
@@ -35,6 +35,7 @@ from pwd import getpwuid
|
||||
from typing import Any, IO, TypeVar
|
||||
|
||||
from ahriman.core.exceptions import CalledProcessError, OptionError, UnsafeRunError
|
||||
from ahriman.core.types import Comparable
|
||||
|
||||
|
||||
__all__ = [
|
||||
@@ -45,6 +46,7 @@ __all__ = [
|
||||
"extract_user",
|
||||
"filter_json",
|
||||
"full_version",
|
||||
"list_flatmap",
|
||||
"minmax",
|
||||
"owner",
|
||||
"package_like",
|
||||
@@ -62,6 +64,7 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
R = TypeVar("R", bound=Comparable)
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@@ -276,6 +279,24 @@ def full_version(epoch: str | int | None, pkgver: str, pkgrel: str) -> str:
|
||||
return f"{prefix}{pkgver}-{pkgrel}"
|
||||
|
||||
|
||||
def list_flatmap(source: Iterable[T], extractor: Callable[[T], list[R]]) -> list[R]:
|
||||
"""
|
||||
extract elements from list of lists, flatten them and apply ``extractor``
|
||||
|
||||
Args:
|
||||
source(Iterable[T]): source list
|
||||
extractor(Callable[[T], list[R]): property extractor
|
||||
|
||||
Returns:
|
||||
list[T]: combined list of unique entries in properties list
|
||||
"""
|
||||
def generator() -> Iterator[R]:
|
||||
for inner in source:
|
||||
yield from extractor(inner)
|
||||
|
||||
return sorted(set(generator()))
|
||||
|
||||
|
||||
def minmax(source: Iterable[T], *, key: Callable[[T], Any] | None = None) -> tuple[T, T]:
|
||||
"""
|
||||
get min and max value from iterable
|
||||
|
||||
@@ -17,23 +17,18 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# pylint: disable=too-many-lines,too-many-public-methods
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
|
||||
from collections.abc import Callable, Iterable, Iterator
|
||||
from collections.abc import Iterable
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from pyalpm import vercmp # type: ignore[import-not-found]
|
||||
from typing import Any, Self
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote import AUR, Official, OfficialSyncdb
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.log import LazyLogging
|
||||
from ahriman.core.utils import dataclass_view, full_version, parse_version, srcinfo_property_list, utcnow
|
||||
from ahriman.core.utils import dataclass_view, full_version, list_flatmap, parse_version, srcinfo_property_list
|
||||
from ahriman.models.package_description import PackageDescription
|
||||
from ahriman.models.package_source import PackageSource
|
||||
from ahriman.models.pkgbuild import Pkgbuild
|
||||
@@ -89,7 +84,7 @@ class Package(LazyLogging):
|
||||
Returns:
|
||||
list[str]: sum of dependencies per each package
|
||||
"""
|
||||
return self._package_list_property(lambda package: package.depends)
|
||||
return list_flatmap(self.packages.values(), lambda package: package.depends)
|
||||
|
||||
@property
|
||||
def depends_build(self) -> set[str]:
|
||||
@@ -109,7 +104,7 @@ class Package(LazyLogging):
|
||||
Returns:
|
||||
list[str]: sum of test dependencies per each package
|
||||
"""
|
||||
return self._package_list_property(lambda package: package.check_depends)
|
||||
return list_flatmap(self.packages.values(), lambda package: package.check_depends)
|
||||
|
||||
@property
|
||||
def depends_make(self) -> list[str]:
|
||||
@@ -119,7 +114,7 @@ class Package(LazyLogging):
|
||||
Returns:
|
||||
list[str]: sum of make dependencies per each package
|
||||
"""
|
||||
return self._package_list_property(lambda package: package.make_depends)
|
||||
return list_flatmap(self.packages.values(), lambda package: package.make_depends)
|
||||
|
||||
@property
|
||||
def depends_opt(self) -> list[str]:
|
||||
@@ -129,7 +124,7 @@ class Package(LazyLogging):
|
||||
Returns:
|
||||
list[str]: sum of optional dependencies per each package
|
||||
"""
|
||||
return self._package_list_property(lambda package: package.opt_depends)
|
||||
return list_flatmap(self.packages.values(), lambda package: package.opt_depends)
|
||||
|
||||
@property
|
||||
def groups(self) -> list[str]:
|
||||
@@ -139,7 +134,7 @@ class Package(LazyLogging):
|
||||
Returns:
|
||||
list[str]: sum of groups per each package
|
||||
"""
|
||||
return self._package_list_property(lambda package: package.groups)
|
||||
return list_flatmap(self.packages.values(), lambda package: package.groups)
|
||||
|
||||
@property
|
||||
def is_single_package(self) -> bool:
|
||||
@@ -174,7 +169,7 @@ class Package(LazyLogging):
|
||||
Returns:
|
||||
list[str]: sum of licenses per each package
|
||||
"""
|
||||
return self._package_list_property(lambda package: package.licenses)
|
||||
return list_flatmap(self.packages.values(), lambda package: package.licenses)
|
||||
|
||||
@property
|
||||
def packages_full(self) -> list[str]:
|
||||
@@ -345,184 +340,6 @@ class Package(LazyLogging):
|
||||
packager=packager,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def local_files(path: Path) -> Iterator[Path]:
|
||||
"""
|
||||
extract list of local files
|
||||
|
||||
Args:
|
||||
path(Path): path to package sources directory
|
||||
|
||||
Yields:
|
||||
Path: list of paths of files which belong to the package and distributed together with this tarball.
|
||||
All paths are relative to the ``path``
|
||||
|
||||
Raises:
|
||||
PackageInfoError: if there are parsing errors
|
||||
"""
|
||||
pkgbuild = Pkgbuild.from_file(path / "PKGBUILD")
|
||||
# we could use arch property, but for consistency it is better to call special method
|
||||
architectures = Package.supported_architectures(path)
|
||||
|
||||
for architecture in architectures:
|
||||
for source in srcinfo_property_list("source", pkgbuild, {}, architecture=architecture):
|
||||
if "::" in source:
|
||||
_, source = source.split("::", maxsplit=1) # in case if filename is specified, remove it
|
||||
|
||||
if urlparse(source).scheme:
|
||||
# basically file schema should use absolute path which is impossible if we are distributing
|
||||
# files together with PKGBUILD. In this case we are going to skip it also
|
||||
continue
|
||||
|
||||
yield Path(source)
|
||||
|
||||
if (install := pkgbuild.get("install")) is not None:
|
||||
yield Path(install)
|
||||
|
||||
@staticmethod
|
||||
def supported_architectures(path: Path) -> set[str]:
|
||||
"""
|
||||
load supported architectures from package sources
|
||||
|
||||
Args:
|
||||
path(Path): path to package sources directory
|
||||
|
||||
Returns:
|
||||
set[str]: list of package supported architectures
|
||||
"""
|
||||
pkgbuild = Pkgbuild.from_file(path / "PKGBUILD")
|
||||
return set(pkgbuild.get("arch", []))
|
||||
|
||||
def _package_list_property(self, extractor: Callable[[PackageDescription], list[str]]) -> list[str]:
|
||||
"""
|
||||
extract list property from single packages and combine them into one list
|
||||
|
||||
Notes:
|
||||
Basically this method is generic for type of ``list[T]``, but there is no trait ``Comparable`` in default
|
||||
packages, thus we limit this method only to new types
|
||||
|
||||
Args:
|
||||
extractor(Callable[[PackageDescription], list[str]): package property extractor
|
||||
|
||||
Returns:
|
||||
list[str]: combined list of unique entries in properties list
|
||||
"""
|
||||
def generator() -> Iterator[str]:
|
||||
for package in self.packages.values():
|
||||
yield from extractor(package)
|
||||
|
||||
return sorted(set(generator()))
|
||||
|
||||
def actual_version(self, configuration: Configuration) -> str:
|
||||
"""
|
||||
additional method to handle VCS package versions
|
||||
|
||||
Args:
|
||||
configuration(Configuration): configuration instance
|
||||
|
||||
Returns:
|
||||
str: package version if package is not VCS and current version according to VCS otherwise
|
||||
"""
|
||||
if not self.is_vcs:
|
||||
return self.version
|
||||
|
||||
from ahriman.core.build_tools.task import Task
|
||||
|
||||
_, repository_id = configuration.check_loaded()
|
||||
paths = configuration.repository_paths
|
||||
task = Task(self, configuration, repository_id.architecture, paths)
|
||||
|
||||
try:
|
||||
# create fresh chroot environment, fetch sources and - automagically - update PKGBUILD
|
||||
task.init(paths.cache_for(self.base), [], None)
|
||||
pkgbuild = Pkgbuild.from_file(paths.cache_for(self.base) / "PKGBUILD")
|
||||
|
||||
return full_version(pkgbuild.get("epoch"), pkgbuild["pkgver"], pkgbuild["pkgrel"])
|
||||
except Exception:
|
||||
self.logger.exception("cannot determine version of VCS package")
|
||||
finally:
|
||||
# clear log files generated by devtools
|
||||
for log_file in paths.cache_for(self.base).glob("*.log"):
|
||||
log_file.unlink()
|
||||
|
||||
return self.version
|
||||
|
||||
def full_depends(self, pacman: Pacman, packages: Iterable[Package]) -> list[str]:
|
||||
"""
|
||||
generate full dependencies list including transitive dependencies
|
||||
|
||||
Args:
|
||||
pacman(Pacman): alpm wrapper instance
|
||||
packages(Iterable[Package]): repository package list
|
||||
|
||||
Returns:
|
||||
list[str]: all dependencies of the package
|
||||
"""
|
||||
dependencies = {}
|
||||
# load own package dependencies
|
||||
for package_base in packages:
|
||||
for name, repo_package in package_base.packages.items():
|
||||
dependencies[name] = repo_package.depends
|
||||
for provides in repo_package.provides:
|
||||
dependencies[provides] = repo_package.depends
|
||||
# load repository dependencies
|
||||
for database in pacman.handle.get_syncdbs():
|
||||
for pacman_package in database.pkgcache:
|
||||
dependencies[pacman_package.name] = pacman_package.depends
|
||||
for provides in pacman_package.provides:
|
||||
dependencies[provides] = pacman_package.depends
|
||||
|
||||
result = set(self.depends)
|
||||
current_depends: set[str] = set()
|
||||
while result != current_depends:
|
||||
current_depends = copy.deepcopy(result)
|
||||
for package in current_depends:
|
||||
result.update(dependencies.get(package, []))
|
||||
|
||||
return sorted(result)
|
||||
|
||||
def is_newer_than(self, timestamp: float | int) -> bool:
|
||||
"""
|
||||
check if package was built after the specified timestamp
|
||||
|
||||
Args:
|
||||
timestamp(float | int): timestamp to check build date against
|
||||
|
||||
Returns:
|
||||
bool: ``True`` in case if package was built after the specified date and ``False`` otherwise.
|
||||
In case if build date is not set by any of packages, it returns False
|
||||
"""
|
||||
return any(
|
||||
package.build_date > timestamp
|
||||
for package in self.packages.values()
|
||||
if package.build_date is not None
|
||||
)
|
||||
|
||||
def is_outdated(self, remote: Package, configuration: Configuration, *,
|
||||
calculate_version: bool = True) -> bool:
|
||||
"""
|
||||
check if package is out-of-dated
|
||||
|
||||
Args:
|
||||
remote(Package): package properties from remote source
|
||||
configuration(Configuration): configuration instance
|
||||
calculate_version(bool, optional): expand version to actual value (by calculating git versions)
|
||||
(Default value = True)
|
||||
|
||||
Returns:
|
||||
bool: ``True`` if the package is out-of-dated and ``False`` otherwise
|
||||
"""
|
||||
vcs_allowed_age = configuration.getint("build", "vcs_allowed_age", fallback=0)
|
||||
min_vcs_build_date = utcnow().timestamp() - vcs_allowed_age
|
||||
|
||||
if calculate_version and not self.is_newer_than(min_vcs_build_date):
|
||||
remote_version = remote.actual_version(configuration)
|
||||
else:
|
||||
remote_version = remote.version
|
||||
|
||||
result: int = vercmp(self.version, remote_version)
|
||||
return result < 0
|
||||
|
||||
def next_pkgrel(self, local_version: str | None) -> str | None:
|
||||
"""
|
||||
generate next pkgrel variable. The package release will be incremented if ``local_version`` is more or equal to
|
||||
|
||||
@@ -22,8 +22,10 @@ from dataclasses import dataclass
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from typing import Any, ClassVar, IO, Self
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from ahriman.core.alpm.pkgbuild_parser import PkgbuildParser, PkgbuildToken
|
||||
from ahriman.core.utils import srcinfo_property_list
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
|
||||
|
||||
@@ -103,6 +105,54 @@ class Pkgbuild(Mapping[str, Any]):
|
||||
|
||||
return cls({key: value for key, value in fields.items() if key})
|
||||
|
||||
@staticmethod
|
||||
def local_files(path: Path) -> Iterator[Path]:
|
||||
"""
|
||||
extract list of local files
|
||||
|
||||
Args:
|
||||
path(Path): path to package sources directory
|
||||
|
||||
Yields:
|
||||
Path: list of paths of files which belong to the package and distributed together with this tarball.
|
||||
All paths are relative to the ``path``
|
||||
|
||||
Raises:
|
||||
PackageInfoError: if there are parsing errors
|
||||
"""
|
||||
pkgbuild = Pkgbuild.from_file(path / "PKGBUILD")
|
||||
# we could use arch property, but for consistency it is better to call special method
|
||||
architectures = Pkgbuild.supported_architectures(path)
|
||||
|
||||
for architecture in architectures:
|
||||
for source in srcinfo_property_list("source", pkgbuild, {}, architecture=architecture):
|
||||
if "::" in source:
|
||||
_, source = source.split("::", maxsplit=1) # in case if filename is specified, remove it
|
||||
|
||||
if urlparse(source).scheme:
|
||||
# basically file schema should use absolute path which is impossible if we are distributing
|
||||
# files together with PKGBUILD. In this case we are going to skip it also
|
||||
continue
|
||||
|
||||
yield Path(source)
|
||||
|
||||
if (install := pkgbuild.get("install")) is not None:
|
||||
yield Path(install)
|
||||
|
||||
@staticmethod
|
||||
def supported_architectures(path: Path) -> set[str]:
|
||||
"""
|
||||
load supported architectures from package sources
|
||||
|
||||
Args:
|
||||
path(Path): path to package sources directory
|
||||
|
||||
Returns:
|
||||
set[str]: list of package supported architectures
|
||||
"""
|
||||
pkgbuild = Pkgbuild.from_file(path / "PKGBUILD")
|
||||
return set(pkgbuild.get("arch", []))
|
||||
|
||||
def packages(self) -> dict[str, Self]:
|
||||
"""
|
||||
extract properties from internal package functions
|
||||
|
||||
@@ -352,6 +352,27 @@ def package_python_schedule(
|
||||
packages=packages)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def package_tpacpi_bat_git() -> Package:
|
||||
"""
|
||||
git package fixture
|
||||
|
||||
Returns:
|
||||
Package: git package test instance
|
||||
"""
|
||||
return Package(
|
||||
base="tpacpi-bat-git",
|
||||
version="3.1.r12.g4959b52-1",
|
||||
remote=RemoteSource(
|
||||
source=PackageSource.AUR,
|
||||
git_url=AUR.remote_git_url("tpacpi-bat-git", "aur"),
|
||||
web_url=AUR.remote_web_url("tpacpi-bat-git"),
|
||||
path=".",
|
||||
branch="master",
|
||||
),
|
||||
packages={"tpacpi-bat-git": PackageDescription()})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def package_description_ahriman() -> PackageDescription:
|
||||
"""
|
||||
|
||||
111
tests/ahriman/core/build_tools/test_package_version.py
Normal file
111
tests/ahriman/core/build_tools/test_package_version.py
Normal file
@@ -0,0 +1,111 @@
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
|
||||
from ahriman.core.build_tools.package_version import PackageVersion
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.utils import utcnow
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.pkgbuild import Pkgbuild
|
||||
|
||||
|
||||
def test_actual_version(package_ahriman: Package, configuration: Configuration) -> None:
|
||||
"""
|
||||
must return same actual_version as version is
|
||||
"""
|
||||
assert PackageVersion(package_ahriman).actual_version(configuration) == package_ahriman.version
|
||||
|
||||
|
||||
def test_actual_version_vcs(package_tpacpi_bat_git: Package, configuration: Configuration,
|
||||
mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must return valid actual_version for VCS package
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_tpacpi-bat-git_pkgbuild"
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
|
||||
mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
|
||||
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init")
|
||||
unlink_mock = mocker.patch("pathlib.Path.unlink")
|
||||
|
||||
assert PackageVersion(package_tpacpi_bat_git).actual_version(configuration) == "3.1.r13.g4959b52-1"
|
||||
init_mock.assert_called_once_with(configuration.repository_paths.cache_for(package_tpacpi_bat_git.base), [], None)
|
||||
unlink_mock.assert_called_once_with()
|
||||
|
||||
|
||||
def test_actual_version_failed(package_tpacpi_bat_git: Package, configuration: Configuration,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return same version in case if exception occurred
|
||||
"""
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.init", side_effect=Exception)
|
||||
mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
|
||||
unlink_mock = mocker.patch("pathlib.Path.unlink")
|
||||
|
||||
assert PackageVersion(package_tpacpi_bat_git).actual_version(configuration) == package_tpacpi_bat_git.version
|
||||
unlink_mock.assert_called_once_with()
|
||||
|
||||
|
||||
def test_is_newer_than(package_ahriman: Package, package_python_schedule: Package) -> None:
|
||||
"""
|
||||
must correctly check if package is newer than specified timestamp
|
||||
"""
|
||||
# base checks, true/false
|
||||
older = package_ahriman.packages[package_ahriman.base].build_date - 1
|
||||
assert PackageVersion(package_ahriman).is_newer_than(older)
|
||||
|
||||
newer = package_ahriman.packages[package_ahriman.base].build_date + 1
|
||||
assert not PackageVersion(package_ahriman).is_newer_than(newer)
|
||||
|
||||
# list check
|
||||
min_date = min(package.build_date for package in package_python_schedule.packages.values())
|
||||
assert PackageVersion(package_python_schedule).is_newer_than(min_date)
|
||||
|
||||
# null list check
|
||||
package_python_schedule.packages["python-schedule"].build_date = None
|
||||
assert PackageVersion(package_python_schedule).is_newer_than(min_date)
|
||||
|
||||
package_python_schedule.packages["python2-schedule"].build_date = None
|
||||
assert not PackageVersion(package_python_schedule).is_newer_than(min_date)
|
||||
|
||||
|
||||
def test_is_outdated_false(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must be not outdated for the same package
|
||||
"""
|
||||
actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version",
|
||||
return_value=package_ahriman.version)
|
||||
assert not PackageVersion(package_ahriman).is_outdated(package_ahriman, configuration)
|
||||
actual_version_mock.assert_called_once_with(configuration)
|
||||
|
||||
|
||||
def test_is_outdated_true(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must be outdated for the new version
|
||||
"""
|
||||
other = Package.from_json(package_ahriman.view())
|
||||
other.version = other.version.replace("-1", "-2")
|
||||
actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version",
|
||||
return_value=other.version)
|
||||
|
||||
assert PackageVersion(package_ahriman).is_outdated(other, configuration)
|
||||
actual_version_mock.assert_called_once_with(configuration)
|
||||
|
||||
|
||||
def test_is_outdated_no_version_calculation(package_ahriman: Package, configuration: Configuration,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not call actual version if calculation is disabled
|
||||
"""
|
||||
actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version")
|
||||
assert not PackageVersion(package_ahriman).is_outdated(package_ahriman, configuration, calculate_version=False)
|
||||
actual_version_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_is_outdated_fresh_package(package_ahriman: Package, configuration: Configuration,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not call actual version if package is never than specified time
|
||||
"""
|
||||
configuration.set_option("build", "vcs_allowed_age", str(int(utcnow().timestamp())))
|
||||
actual_version_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.actual_version")
|
||||
assert not PackageVersion(package_ahriman).is_outdated(package_ahriman, configuration)
|
||||
actual_version_mock.assert_not_called()
|
||||
@@ -55,7 +55,8 @@ def test_extend_architectures(mocker: MockerFixture) -> None:
|
||||
must update available architecture list
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_file", return_value=True)
|
||||
architectures_mock = mocker.patch("ahriman.models.package.Package.supported_architectures", return_value={"x86_64"})
|
||||
architectures_mock = mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures",
|
||||
return_value={"x86_64"})
|
||||
|
||||
assert Sources.extend_architectures(Path("local"), "i686") == [PkgbuildPatch("arch", list({"x86_64", "i686"}))]
|
||||
architectures_mock.assert_called_once_with(Path("local"))
|
||||
@@ -66,7 +67,7 @@ def test_extend_architectures_any(mocker: MockerFixture) -> None:
|
||||
must skip architecture patching in case if there is any architecture
|
||||
"""
|
||||
mocker.patch("pathlib.Path.is_file", return_value=True)
|
||||
mocker.patch("ahriman.models.package.Package.supported_architectures", return_value={"any"})
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value={"any"})
|
||||
assert Sources.extend_architectures(Path("local"), "i686") == []
|
||||
|
||||
|
||||
@@ -191,7 +192,7 @@ def test_init(sources: Sources, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must create empty repository at the specified path
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package.local_files", return_value=[Path("local")])
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.local_files", return_value=[Path("local")])
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=False)
|
||||
add_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.add")
|
||||
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.check_output")
|
||||
@@ -209,7 +210,7 @@ def test_init_skip(mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must skip git init if it was already
|
||||
"""
|
||||
mocker.patch("ahriman.models.package.Package.local_files", return_value=[Path("local")])
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.local_files", return_value=[Path("local")])
|
||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.add")
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.commit")
|
||||
|
||||
@@ -2,12 +2,32 @@ import pytest
|
||||
|
||||
from pathlib import Path
|
||||
from pytest_mock import MockerFixture
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from ahriman.core.repository.package_info import PackageInfo
|
||||
from ahriman.models.changes import Changes
|
||||
from ahriman.models.package import Package
|
||||
|
||||
|
||||
def test_full_depends(package_info: PackageInfo, package_ahriman: Package, package_python_schedule: Package,
|
||||
pyalpm_package_ahriman: MagicMock) -> None:
|
||||
"""
|
||||
must extract all dependencies from the package
|
||||
"""
|
||||
package_python_schedule.packages[package_python_schedule.base].provides = ["python3-schedule"]
|
||||
|
||||
database_mock = MagicMock()
|
||||
database_mock.pkgcache = [pyalpm_package_ahriman]
|
||||
package_info.pacman = MagicMock()
|
||||
package_info.pacman.handle.get_syncdbs.return_value = [database_mock]
|
||||
|
||||
assert package_info.full_depends(package_ahriman, [package_python_schedule]) == package_ahriman.depends
|
||||
|
||||
package_python_schedule.packages[package_python_schedule.base].depends = [package_ahriman.base]
|
||||
expected = sorted(set(package_python_schedule.depends + package_ahriman.depends))
|
||||
assert package_info.full_depends(package_python_schedule, [package_python_schedule]) == expected
|
||||
|
||||
|
||||
def test_load_archives(package_ahriman: Package, package_python_schedule: Package,
|
||||
package_info: PackageInfo, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
|
||||
@@ -24,7 +24,8 @@ def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_pending")
|
||||
event_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.event")
|
||||
package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
package_is_outdated_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated",
|
||||
return_value=True)
|
||||
|
||||
assert update_handler.updates_aur([], vcs=True) == [package_ahriman]
|
||||
packages_mock.assert_called_once_with([])
|
||||
@@ -43,7 +44,7 @@ def test_updates_aur_official(update_handler: UpdateHandler, package_ahriman: Pa
|
||||
"""
|
||||
package_ahriman.remote = RemoteSource(source=PackageSource.Repository)
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.models.package.Package.from_official", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_pending")
|
||||
event_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.event")
|
||||
@@ -74,7 +75,7 @@ def test_updates_aur_up_to_date(update_handler: UpdateHandler, package_ahriman:
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=False)
|
||||
mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=False)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_status_update")
|
||||
|
||||
assert update_handler.updates_aur([], vcs=True) == []
|
||||
@@ -100,7 +101,7 @@ def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Pack
|
||||
"""
|
||||
packages_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
|
||||
return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
|
||||
|
||||
assert update_handler.updates_aur([package_ahriman.base], vcs=True) == [package_ahriman]
|
||||
@@ -129,7 +130,8 @@ def test_updates_aur_ignore_vcs(update_handler: UpdateHandler, package_ahriman:
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
|
||||
mocker.patch("ahriman.models.package.Package.is_vcs", return_value=True)
|
||||
package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=False)
|
||||
package_is_outdated_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated",
|
||||
return_value=False)
|
||||
|
||||
assert not update_handler.updates_aur([], vcs=False)
|
||||
package_is_outdated_mock.assert_called_once_with(
|
||||
@@ -150,7 +152,7 @@ def test_updates_aur_load_by_package(update_handler: UpdateHandler, package_pyth
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
|
||||
return_value=[package_python_schedule])
|
||||
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=package_selector)
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
|
||||
assert update_handler.updates_aur([], vcs=True) == [package_python_schedule]
|
||||
|
||||
|
||||
@@ -232,7 +234,8 @@ def test_updates_local(update_handler: UpdateHandler, package_ahriman: Package,
|
||||
package_load_mock = mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||
status_client_mock = mocker.patch("ahriman.core.status.Client.set_pending")
|
||||
event_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.event")
|
||||
package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
package_is_outdated_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated",
|
||||
return_value=True)
|
||||
|
||||
assert update_handler.updates_local(vcs=True) == [package_ahriman]
|
||||
fetch_mock.assert_called_once_with(Path(package_ahriman.base), pytest.helpers.anyvar(int))
|
||||
@@ -255,7 +258,8 @@ def test_updates_local_ignore_vcs(update_handler: UpdateHandler, package_ahriman
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||
package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=False)
|
||||
package_is_outdated_mock = mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated",
|
||||
return_value=False)
|
||||
|
||||
assert not update_handler.updates_local(vcs=False)
|
||||
package_is_outdated_mock.assert_called_once_with(
|
||||
@@ -269,7 +273,7 @@ def test_updates_local_unknown(update_handler: UpdateHandler, package_ahriman: P
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[])
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||
|
||||
@@ -282,7 +286,7 @@ def test_updates_local_remote(update_handler: UpdateHandler, package_ahriman: Pa
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path(package_ahriman.base)])
|
||||
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.package_version.PackageVersion.is_outdated", return_value=True)
|
||||
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
|
||||
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
|
||||
|
||||
|
||||
@@ -265,6 +265,15 @@ def test_full_version() -> None:
|
||||
assert full_version(1, "0.12.1", "1") == "1:0.12.1-1"
|
||||
|
||||
|
||||
def test_list_flatmap() -> None:
|
||||
"""
|
||||
must flat map iterable correctly
|
||||
"""
|
||||
assert list_flatmap([], lambda e: [e * 2]) == []
|
||||
assert list_flatmap([3, 1, 2], lambda e: [e * 2]) == [2, 4, 6]
|
||||
assert list_flatmap([1, 2, 1], lambda e: [e * 2]) == [2, 4]
|
||||
|
||||
|
||||
def test_minmax() -> None:
|
||||
"""
|
||||
must correctly define minimal and maximal value
|
||||
|
||||
@@ -78,27 +78,6 @@ def internal_status(counters: Counters) -> InternalStatus:
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def package_tpacpi_bat_git() -> Package:
|
||||
"""
|
||||
git package fixture
|
||||
|
||||
Returns:
|
||||
Package: git package test instance
|
||||
"""
|
||||
return Package(
|
||||
base="tpacpi-bat-git",
|
||||
version="3.1.r12.g4959b52-1",
|
||||
remote=RemoteSource(
|
||||
source=PackageSource.AUR,
|
||||
git_url=AUR.remote_git_url("tpacpi-bat-git", "aur"),
|
||||
web_url=AUR.remote_web_url("tpacpi-bat-git"),
|
||||
path=".",
|
||||
branch="master",
|
||||
),
|
||||
packages={"tpacpi-bat-git": PackageDescription()})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pkgbuild_ahriman(resource_path_root: Path) -> Pkgbuild:
|
||||
"""
|
||||
|
||||
@@ -5,13 +5,10 @@ from pytest_mock import MockerFixture
|
||||
from unittest.mock import MagicMock, PropertyMock, call as MockCall
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.utils import utcnow
|
||||
from ahriman.models.aur_package import AURPackage
|
||||
from ahriman.models.package import Package
|
||||
from ahriman.models.package_description import PackageDescription
|
||||
from ahriman.models.pkgbuild import Pkgbuild
|
||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||
|
||||
|
||||
def test_depends(package_python_schedule: Package) -> None:
|
||||
@@ -322,183 +319,6 @@ def test_from_official(package_ahriman: Package, aur_package_ahriman: AURPackage
|
||||
assert package_ahriman.packager == package.packager
|
||||
|
||||
|
||||
def test_local_files(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must extract local file sources
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
|
||||
parsed_pkgbuild.fields["source"] = PkgbuildPatch("source", ["local-file.tar.gz"])
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
|
||||
mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
|
||||
|
||||
assert list(Package.local_files(Path("path"))) == [Path("local-file.tar.gz")]
|
||||
|
||||
|
||||
def test_local_files_empty(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must extract empty local files list when there are no local files
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
|
||||
mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
|
||||
|
||||
assert not list(Package.local_files(Path("path")))
|
||||
|
||||
|
||||
def test_local_files_schema(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must skip local file source when file schema is used
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
|
||||
parsed_pkgbuild.fields["source"] = PkgbuildPatch("source", ["file:///local-file.tar.gz"])
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
|
||||
mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
|
||||
|
||||
assert not list(Package.local_files(Path("path")))
|
||||
|
||||
|
||||
def test_local_files_with_install(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must extract local file sources with install file
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
|
||||
parsed_pkgbuild.fields["install"] = PkgbuildPatch("install", "install")
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
|
||||
mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
|
||||
|
||||
assert list(Package.local_files(Path("path"))) == [Path("install")]
|
||||
|
||||
|
||||
def test_supported_architectures(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must generate list of available architectures
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
|
||||
assert Package.supported_architectures(Path("path")) == \
|
||||
{"i686", "pentium4", "x86_64", "arm", "armv7h", "armv6h", "aarch64", "riscv64"}
|
||||
|
||||
|
||||
def test_actual_version(package_ahriman: Package, configuration: Configuration) -> None:
|
||||
"""
|
||||
must return same actual_version as version is
|
||||
"""
|
||||
assert package_ahriman.actual_version(configuration) == package_ahriman.version
|
||||
|
||||
|
||||
def test_actual_version_vcs(package_tpacpi_bat_git: Package, configuration: Configuration,
|
||||
mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must return valid actual_version for VCS package
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_tpacpi-bat-git_pkgbuild"
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
|
||||
mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
|
||||
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init")
|
||||
unlink_mock = mocker.patch("pathlib.Path.unlink")
|
||||
|
||||
assert package_tpacpi_bat_git.actual_version(configuration) == "3.1.r13.g4959b52-1"
|
||||
init_mock.assert_called_once_with(configuration.repository_paths.cache_for(package_tpacpi_bat_git.base), [], None)
|
||||
unlink_mock.assert_called_once_with()
|
||||
|
||||
|
||||
def test_actual_version_failed(package_tpacpi_bat_git: Package, configuration: Configuration,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return same version in case if exception occurred
|
||||
"""
|
||||
mocker.patch("ahriman.core.build_tools.task.Task.init", side_effect=Exception)
|
||||
mocker.patch("pathlib.Path.glob", return_value=[Path("local")])
|
||||
unlink_mock = mocker.patch("pathlib.Path.unlink")
|
||||
|
||||
assert package_tpacpi_bat_git.actual_version(configuration) == package_tpacpi_bat_git.version
|
||||
unlink_mock.assert_called_once_with()
|
||||
|
||||
|
||||
def test_full_depends(package_ahriman: Package, package_python_schedule: Package, pyalpm_package_ahriman: MagicMock,
|
||||
pyalpm_handle: MagicMock) -> None:
|
||||
"""
|
||||
must extract all dependencies from the package
|
||||
"""
|
||||
package_python_schedule.packages[package_python_schedule.base].provides = ["python3-schedule"]
|
||||
|
||||
database_mock = MagicMock()
|
||||
database_mock.pkgcache = [pyalpm_package_ahriman]
|
||||
pyalpm_handle.handle.get_syncdbs.return_value = [database_mock]
|
||||
|
||||
assert package_ahriman.full_depends(pyalpm_handle, [package_python_schedule]) == package_ahriman.depends
|
||||
|
||||
package_python_schedule.packages[package_python_schedule.base].depends = [package_ahriman.base]
|
||||
expected = sorted(set(package_python_schedule.depends + package_ahriman.depends))
|
||||
assert package_python_schedule.full_depends(pyalpm_handle, [package_python_schedule]) == expected
|
||||
|
||||
|
||||
def test_is_newer_than(package_ahriman: Package, package_python_schedule: Package) -> None:
|
||||
"""
|
||||
must correctly check if package is newer than specified timestamp
|
||||
"""
|
||||
# base checks, true/false
|
||||
assert package_ahriman.is_newer_than(package_ahriman.packages[package_ahriman.base].build_date - 1)
|
||||
assert not package_ahriman.is_newer_than(package_ahriman.packages[package_ahriman.base].build_date + 1)
|
||||
|
||||
# list check
|
||||
min_date = min(package.build_date for package in package_python_schedule.packages.values())
|
||||
assert package_python_schedule.is_newer_than(min_date)
|
||||
|
||||
# null list check
|
||||
package_python_schedule.packages["python-schedule"].build_date = None
|
||||
assert package_python_schedule.is_newer_than(min_date)
|
||||
|
||||
package_python_schedule.packages["python2-schedule"].build_date = None
|
||||
assert not package_python_schedule.is_newer_than(min_date)
|
||||
|
||||
|
||||
def test_is_outdated_false(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must be not outdated for the same package
|
||||
"""
|
||||
actual_version_mock = mocker.patch("ahriman.models.package.Package.actual_version",
|
||||
return_value=package_ahriman.version)
|
||||
assert not package_ahriman.is_outdated(package_ahriman, configuration)
|
||||
actual_version_mock.assert_called_once_with(configuration)
|
||||
|
||||
|
||||
def test_is_outdated_true(package_ahriman: Package, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must be outdated for the new version
|
||||
"""
|
||||
other = Package.from_json(package_ahriman.view())
|
||||
other.version = other.version.replace("-1", "-2")
|
||||
actual_version_mock = mocker.patch("ahriman.models.package.Package.actual_version", return_value=other.version)
|
||||
|
||||
assert package_ahriman.is_outdated(other, configuration)
|
||||
actual_version_mock.assert_called_once_with(configuration)
|
||||
|
||||
|
||||
def test_is_outdated_no_version_calculation(package_ahriman: Package, configuration: Configuration,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not call actual version if calculation is disabled
|
||||
"""
|
||||
actual_version_mock = mocker.patch("ahriman.models.package.Package.actual_version")
|
||||
assert not package_ahriman.is_outdated(package_ahriman, configuration, calculate_version=False)
|
||||
actual_version_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_is_outdated_fresh_package(package_ahriman: Package, configuration: Configuration,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must not call actual version if package is never than specified time
|
||||
"""
|
||||
configuration.set_option("build", "vcs_allowed_age", str(int(utcnow().timestamp())))
|
||||
actual_version_mock = mocker.patch("ahriman.models.package.Package.actual_version")
|
||||
assert not package_ahriman.is_outdated(package_ahriman, configuration)
|
||||
actual_version_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_next_pkgrel(package_ahriman: Package) -> None:
|
||||
"""
|
||||
must correctly bump pkgrel
|
||||
|
||||
@@ -78,6 +78,66 @@ def test_from_io_empty(pkgbuild_ahriman: Pkgbuild, mocker: MockerFixture) -> Non
|
||||
assert Pkgbuild.from_io(StringIO("mock")) == pkgbuild_ahriman
|
||||
|
||||
|
||||
def test_local_files(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must extract local file sources
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
|
||||
parsed_pkgbuild.fields["source"] = PkgbuildPatch("source", ["local-file.tar.gz"])
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value=["any"])
|
||||
|
||||
assert list(Pkgbuild.local_files(Path("path"))) == [Path("local-file.tar.gz")]
|
||||
|
||||
|
||||
def test_local_files_empty(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must extract empty local files list when there are no local files
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value=["any"])
|
||||
|
||||
assert not list(Pkgbuild.local_files(Path("path")))
|
||||
|
||||
|
||||
def test_local_files_schema(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must skip local file source when file schema is used
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
|
||||
parsed_pkgbuild.fields["source"] = PkgbuildPatch("source", ["file:///local-file.tar.gz"])
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value=["any"])
|
||||
|
||||
assert not list(Pkgbuild.local_files(Path("path")))
|
||||
|
||||
|
||||
def test_local_files_with_install(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must extract local file sources with install file
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
parsed_pkgbuild = Pkgbuild.from_file(pkgbuild)
|
||||
parsed_pkgbuild.fields["install"] = PkgbuildPatch("install", "install")
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=parsed_pkgbuild)
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.supported_architectures", return_value=["any"])
|
||||
|
||||
assert list(Pkgbuild.local_files(Path("path"))) == [Path("install")]
|
||||
|
||||
|
||||
def test_supported_architectures(mocker: MockerFixture, resource_path_root: Path) -> None:
|
||||
"""
|
||||
must generate list of available architectures
|
||||
"""
|
||||
pkgbuild = resource_path_root / "models" / "package_yay_pkgbuild"
|
||||
mocker.patch("ahriman.models.pkgbuild.Pkgbuild.from_file", return_value=Pkgbuild.from_file(pkgbuild))
|
||||
assert Pkgbuild.supported_architectures(Path("path")) == \
|
||||
{"i686", "pentium4", "x86_64", "arm", "armv7h", "armv6h", "aarch64", "riscv64"}
|
||||
|
||||
|
||||
def test_packages(pkgbuild_ahriman: Pkgbuild) -> None:
|
||||
"""
|
||||
must correctly load package function
|
||||
|
||||
Reference in New Issue
Block a user