Compare commits

...

4 Commits
1.6.0 ... 1.6.1

Author SHA1 Message Date
d480eb7bc3 Release 1.6.1 2021-10-27 03:16:53 +03:00
8b0f9bfd78 update license headers 2021-10-27 03:14:39 +03:00
a2639f8dbb add update printer which will print current version if any 2021-10-27 03:11:43 +03:00
65ba590ace use PackageSource enum for Package.load method
When using add function it sill tries to load data with invalid source
2021-10-27 02:49:23 +03:00
16 changed files with 2561 additions and 2341 deletions

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 388 KiB

After

Width:  |  Height:  |  Size: 398 KiB

View File

@ -1,7 +1,7 @@
# Maintainer: Evgeniy Alekseev
pkgname='ahriman'
pkgver=1.6.0
pkgver=1.6.1
pkgrel=1
pkgdesc="ArcH Linux ReposItory MANager"
arch=('any')

View File

@ -65,11 +65,10 @@ class Packages(Properties):
:param without_dependencies: if set, dependency check will be disabled
"""
aur_url = self.configuration.get("alpm", "aur_url")
package = Package.load(source, self.repository.pacman, aur_url)
Sources.load(self.repository.paths.manual_for(package.base), package.git_url,
self.repository.paths.patches_for(package.base))
package = Package.load(source, PackageSource.AUR, self.repository.pacman, aur_url)
local_path = self.repository.paths.manual_for(package.base)
Sources.load(local_path, package.git_url, self.repository.paths.patches_for(package.base))
self._process_dependencies(local_path, known_packages, without_dependencies)
def _add_directory(self, source: str, *_: Any) -> None:
@ -88,11 +87,10 @@ class Packages(Properties):
:param known_packages: list of packages which are known by the service
:param without_dependencies: if set, dependency check will be disabled
"""
local_path = Path(source)
aur_url = self.configuration.get("alpm", "aur_url")
package = Package.load(local_path, self.repository.pacman, aur_url)
package = Package.load(source, PackageSource.Local, self.repository.pacman, aur_url)
cache_dir = self.repository.paths.cache_for(package.base)
shutil.copytree(local_path, cache_dir) # copy package to store in caches
shutil.copytree(Path(source), cache_dir) # copy package to store in caches
Sources.init(cache_dir) # we need to run init command in directory where we do have permissions
dst = self.repository.paths.manual_for(package.base)

View File

@ -23,9 +23,11 @@ from pathlib import Path
from typing import Callable, Iterable, List
from ahriman.application.application.properties import Properties
from ahriman.application.formatters.update_printer import UpdatePrinter
from ahriman.core.build_tools.sources import Sources
from ahriman.core.tree import Tree
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
class Repository(Properties):
@ -133,7 +135,10 @@ class Repository(Properties):
def process_update(paths: Iterable[Path]) -> None:
if not paths:
return # don't need to process if no update supplied
updated = [Package.load(path, self.repository.pacman, self.repository.aur_url) for path in paths]
updated = [
Package.load(str(path), PackageSource.Archive, self.repository.pacman, self.repository.aur_url)
for path in paths
]
self.repository.process_update(paths)
self._finalize(updated)
@ -166,7 +171,9 @@ class Repository(Properties):
if not no_manual:
updates.extend(self.repository.updates_manual())
local_versions = {package.base: package.version for package in self.repository.packages()}
for package in updates:
log_fn(f"{package.base} = {package.version}")
UpdatePrinter(package, local_versions.get(package.base)).print(
verbose=True, log_fn=log_fn, separator=" -> ")
return updates

View File

@ -0,0 +1,53 @@
#
# Copyright (c) 2021 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 typing import List, Optional
from ahriman.application.formatters.printer import Printer
from ahriman.models.package import Package
from ahriman.models.property import Property
class UpdatePrinter(Printer):
"""
print content of the package update
"""
def __init__(self, remote: Package, local_version: Optional[str]) -> None:
"""
default constructor
:param remote: remote (new) package object
:param local_version: local version of the package if any
"""
self.content = remote
self.local_version = local_version or "N/A"
def properties(self) -> List[Property]:
"""
convert content into printable data
:return: list of content properties
"""
return [Property(self.local_version, self.content.version, is_required=True)]
def title(self) -> Optional[str]:
"""
generate entry title from content
:return: content title if it can be generated and None otherwise
"""
return self.content.base

View File

@ -29,6 +29,7 @@ from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
from ahriman.models.action import Action
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
class Patch(Handler):
@ -55,23 +56,24 @@ class Patch(Handler):
elif args.action == Action.Remove:
Patch.patch_set_remove(application, args.package)
elif args.action == Action.Update:
Patch.patch_set_create(application, Path(args.package), args.track)
Patch.patch_set_create(application, args.package, args.track)
@staticmethod
def patch_set_create(application: Application, sources_dir: Path, track: List[str]) -> None:
def patch_set_create(application: Application, sources_dir: str, track: List[str]) -> None:
"""
create patch set for the package base
:param application: application instance
:param sources_dir: path to directory with the package sources
:param track: track files which match the glob before creating the patch
"""
package = Package.load(sources_dir, application.repository.pacman, application.repository.aur_url)
package = Package.load(sources_dir, PackageSource.Local, application.repository.pacman,
application.repository.aur_url)
patch_dir = application.repository.paths.patches_for(package.base)
Patch.patch_set_remove(application, package.base) # remove old patches
patch_dir.mkdir(mode=0o755, parents=True)
Sources.patch_create(sources_dir, patch_dir / "00-main.patch", *track)
Sources.patch_create(Path(sources_dir), patch_dir / "00-main.patch", *track)
@staticmethod
def patch_set_list(application: Application, package_base: str) -> None:

View File

@ -27,6 +27,7 @@ from ahriman.core.report.report import Report
from ahriman.core.repository.cleaner import Cleaner
from ahriman.core.upload.upload import Upload
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
class Executor(Cleaner):
@ -155,7 +156,7 @@ class Executor(Cleaner):
updates: Dict[str, Package] = {}
for filename in packages:
try:
local = Package.load(filename, self.pacman, self.aur_url)
local = Package.load(str(filename), PackageSource.Archive, self.pacman, self.aur_url)
updates.setdefault(local.base, local).packages.update(local.packages)
except Exception:
self.logger.exception("could not load package from %s", filename)

View File

@ -24,6 +24,7 @@ from ahriman.core.repository.executor import Executor
from ahriman.core.repository.update_handler import UpdateHandler
from ahriman.core.util import package_like
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
class Repository(Executor, UpdateHandler):
@ -39,7 +40,7 @@ class Repository(Executor, UpdateHandler):
result: Dict[str, Package] = {}
for full_path in filter(package_like, self.paths.repository.iterdir()):
try:
local = Package.load(full_path, self.pacman, self.aur_url)
local = Package.load(str(full_path), PackageSource.Archive, self.pacman, self.aur_url)
result.setdefault(local.base, local).packages.update(local.packages)
except Exception:
self.logger.exception("could not load package from %s", full_path)

View File

@ -21,6 +21,7 @@ from typing import Iterable, List
from ahriman.core.repository.cleaner import Cleaner
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
class UpdateHandler(Cleaner):
@ -53,7 +54,7 @@ class UpdateHandler(Cleaner):
continue
try:
remote = Package.load(local.base, self.pacman, self.aur_url)
remote = Package.load(local.base, PackageSource.AUR, self.pacman, self.aur_url)
if local.is_outdated(remote, self.paths):
self.reporter.set_pending(local.base)
result.append(remote)
@ -72,16 +73,16 @@ class UpdateHandler(Cleaner):
result: List[Package] = []
known_bases = {package.base for package in self.packages()}
for filename in self.paths.manual.iterdir():
for dirname in self.paths.manual.iterdir():
try:
local = Package.load(filename, self.pacman, self.aur_url)
local = Package.load(str(dirname), PackageSource.Local, self.pacman, self.aur_url)
result.append(local)
if local.base not in known_bases:
self.reporter.set_unknown(local)
else:
self.reporter.set_pending(local.base)
except Exception:
self.logger.exception("could not add package from %s", filename)
self.logger.exception("could not add package from %s", dirname)
self.clear_manual()
return result

View File

@ -26,12 +26,13 @@ from dataclasses import asdict, dataclass
from pathlib import Path
from pyalpm import vercmp # type: ignore
from srcinfo.parse import parse_srcinfo # type: ignore
from typing import Any, Dict, List, Optional, Set, Type, Union
from typing import Any, Dict, List, Optional, Set, Type
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.core.util import check_output
from ahriman.models.package_description import PackageDescription
from ahriman.models.package_source import PackageSource
from ahriman.models.repository_paths import RepositoryPaths
@ -164,21 +165,24 @@ class Package:
packages=packages)
@classmethod
def load(cls: Type[Package], path: Union[Path, str], pacman: Pacman, aur_url: str) -> Package:
def load(cls: Type[Package], package: str, source: PackageSource, pacman: Pacman, aur_url: str) -> Package:
"""
package constructor from available sources
:param path: one of path to sources directory, path to archive or package name/base
:param package: one of path to sources directory, path to archive or package name/base
:param source: source of the package required to define the load method
:param pacman: alpm wrapper instance (required to load from archive)
:param aur_url: AUR root url
:return: package properties
"""
try:
maybe_path = Path(path)
if maybe_path.is_dir():
return cls.from_build(maybe_path, aur_url)
if maybe_path.is_file():
return cls.from_archive(maybe_path, pacman, aur_url)
return cls.from_aur(str(path), aur_url)
resolved_source = source.resolve(package)
if resolved_source == PackageSource.Archive:
return cls.from_archive(Path(package), pacman, aur_url)
if resolved_source == PackageSource.AUR:
return cls.from_aur(package, aur_url)
if resolved_source == PackageSource.Local:
return cls.from_build(Path(package), aur_url)
raise InvalidPackageInfo(f"Unsupported local package source {resolved_source}")
except InvalidPackageInfo:
raise
except Exception as e:

View File

@ -1,3 +1,7 @@
#
# Copyright (c) 2021 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

View File

@ -17,4 +17,4 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
__version__ = "1.6.0"
__version__ = "1.6.1"

View File

@ -201,6 +201,7 @@ def test_updates_all(application_repository: Repository, package_ahriman: Packag
"""
must get updates for all
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur",
return_value=[package_ahriman])
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
@ -214,6 +215,7 @@ def test_updates_disabled(application_repository: Repository, mocker: MockerFixt
"""
must get updates without anything
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
@ -226,6 +228,7 @@ def test_updates_no_aur(application_repository: Repository, mocker: MockerFixtur
"""
must get updates without aur
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
@ -238,6 +241,7 @@ def test_updates_no_manual(application_repository: Repository, mocker: MockerFix
"""
must get updates without manual
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
@ -250,6 +254,7 @@ def test_updates_no_vcs(application_repository: Repository, mocker: MockerFixtur
"""
must get updates without VCS
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
@ -262,6 +267,7 @@ def test_updates_with_filter(application_repository: Repository, mocker: MockerF
"""
must get updates without VCS
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")

View File

@ -5,6 +5,7 @@ from ahriman.application.formatters.aur_printer import AurPrinter
from ahriman.application.formatters.configuration_printer import ConfigurationPrinter
from ahriman.application.formatters.package_printer import PackagePrinter
from ahriman.application.formatters.status_printer import StatusPrinter
from ahriman.application.formatters.update_printer import UpdatePrinter
from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package
@ -39,9 +40,18 @@ def package_ahriman_printer(package_ahriman: Package) -> PackagePrinter:
@pytest.fixture
def status_printer(package_ahriman: Package) -> StatusPrinter:
def status_printer() -> StatusPrinter:
"""
fixture for build status printer
:return: build status printer test instance
"""
return StatusPrinter(BuildStatus())
@pytest.fixture
def update_printer(package_ahriman: Package) -> UpdatePrinter:
"""
fixture for build status printer
:return: build status printer test instance
"""
return UpdatePrinter(package_ahriman, None)

View File

@ -0,0 +1,15 @@
from ahriman.application.formatters.update_printer import UpdatePrinter
def test_properties(update_printer: UpdatePrinter) -> None:
"""
must return empty properties list
"""
assert update_printer.properties()
def test_title(update_printer: UpdatePrinter) -> None:
"""
must return non empty title
"""
assert update_printer.title() is not None

View File

@ -6,6 +6,7 @@ from unittest.mock import MagicMock, PropertyMock
from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.repository_paths import RepositoryPaths
@ -156,14 +157,24 @@ def test_from_json_view_3(package_tpacpi_bat_git: Package) -> None:
assert Package.from_json(package_tpacpi_bat_git.view()) == package_tpacpi_bat_git
def test_load_resolve(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
"""
must resolve source before package loading
"""
resolve_mock = mocker.patch("ahriman.models.package_source.PackageSource.resolve",
return_value=PackageSource.Archive)
mocker.patch("ahriman.models.package.Package.from_archive")
Package.load("path", PackageSource.Archive, pyalpm_handle, package_ahriman.aur_url)
resolve_mock.assert_called_once_with("path")
def test_load_from_archive(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
"""
must load package from package archive
"""
mocker.patch("pathlib.Path.is_file", return_value=True)
load_mock = mocker.patch("ahriman.models.package.Package.from_archive")
Package.load(Path("path"), pyalpm_handle, package_ahriman.aur_url)
Package.load("path", PackageSource.Archive, pyalpm_handle, package_ahriman.aur_url)
load_mock.assert_called_once()
@ -172,8 +183,7 @@ def test_load_from_aur(package_ahriman: Package, pyalpm_handle: MagicMock, mocke
must load package from AUR
"""
load_mock = mocker.patch("ahriman.models.package.Package.from_aur")
Package.load(Path("path"), pyalpm_handle, package_ahriman.aur_url)
Package.load("path", PackageSource.AUR, pyalpm_handle, package_ahriman.aur_url)
load_mock.assert_called_once()
@ -181,10 +191,8 @@ def test_load_from_build(package_ahriman: Package, pyalpm_handle: MagicMock, moc
"""
must load package from build directory
"""
mocker.patch("pathlib.Path.is_dir", return_value=True)
load_mock = mocker.patch("ahriman.models.package.Package.from_build")
Package.load(Path("path"), pyalpm_handle, package_ahriman.aur_url)
Package.load("path", PackageSource.Local, pyalpm_handle, package_ahriman.aur_url)
load_mock.assert_called_once()
@ -192,13 +200,26 @@ def test_load_failure(package_ahriman: Package, pyalpm_handle: MagicMock, mocker
"""
must raise InvalidPackageInfo on exception
"""
mocker.patch("pathlib.Path.is_dir", side_effect=InvalidPackageInfo("exception!"))
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=InvalidPackageInfo("exception!"))
with pytest.raises(InvalidPackageInfo):
Package.load(Path("path"), pyalpm_handle, package_ahriman.aur_url)
Package.load("path", PackageSource.AUR, pyalpm_handle, package_ahriman.aur_url)
mocker.patch("pathlib.Path.is_dir", side_effect=Exception())
def test_load_failure_exception(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
"""
must raise InvalidPackageInfo on random eexception
"""
mocker.patch("ahriman.models.package.Package.from_aur", side_effect=Exception())
with pytest.raises(InvalidPackageInfo):
Package.load(Path("path"), pyalpm_handle, package_ahriman.aur_url)
Package.load("path", PackageSource.AUR, pyalpm_handle, package_ahriman.aur_url)
def test_load_invalid_source(package_ahriman: Package, pyalpm_handle: MagicMock) -> None:
"""
must raise InvalidPackageInfo on unsupported source
"""
with pytest.raises(InvalidPackageInfo):
Package.load("path", PackageSource.Remote, pyalpm_handle, package_ahriman.aur_url)
def test_dependencies_failed(mocker: MockerFixture) -> None: