mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-23 23:07:17 +00:00
handle dependencies recursively
This commit is contained in:
parent
32aaa4a1f8
commit
1e85c1db70
@ -346,6 +346,8 @@ def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser = root.add_parser("repo-rebuild", aliases=["rebuild"], help="rebuild repository",
|
||||
description="force rebuild whole repository", formatter_class=_formatter)
|
||||
parser.add_argument("--depends-on", help="only rebuild packages that depend on specified package", action="append")
|
||||
parser.add_argument("--dry-run", help="just perform check for packages without rebuild process itself",
|
||||
action="store_true")
|
||||
parser.set_defaults(handler=handlers.Rebuild)
|
||||
return parser
|
||||
|
||||
|
@ -22,6 +22,7 @@ import argparse
|
||||
from typing import Type
|
||||
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.formatters.update_printer import UpdatePrinter
|
||||
from ahriman.application.handlers.handler import Handler
|
||||
from ahriman.core.configuration import Configuration
|
||||
|
||||
@ -44,9 +45,10 @@ class Rebuild(Handler):
|
||||
depends_on = set(args.depends_on) if args.depends_on else None
|
||||
|
||||
application = Application(architecture, configuration, no_report)
|
||||
packages = [
|
||||
package
|
||||
for package in application.repository.packages()
|
||||
if depends_on is None or depends_on.intersection(package.depends)
|
||||
] # we have to use explicit list here for testing purpose
|
||||
application.update(packages)
|
||||
updates = application.repository.packages_depends_on(depends_on)
|
||||
if args.dry_run:
|
||||
for package in updates:
|
||||
UpdatePrinter(package, package.version).print(verbose=True)
|
||||
return
|
||||
|
||||
application.update(updates)
|
||||
|
@ -18,7 +18,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from pathlib import Path
|
||||
from typing import Dict, Iterable, List
|
||||
from typing import Dict, Iterable, List, Optional
|
||||
|
||||
from ahriman.core.repository.executor import Executor
|
||||
from ahriman.core.repository.update_handler import UpdateHandler
|
||||
@ -68,3 +68,20 @@ class Repository(Executor, UpdateHandler):
|
||||
:return: list of filenames from the directory
|
||||
"""
|
||||
return list(filter(package_like, self.paths.packages.iterdir()))
|
||||
|
||||
def packages_depends_on(self, depends_on: Optional[Iterable[str]]) -> List[Package]:
|
||||
"""
|
||||
extract list of packages which depends on specified package
|
||||
:param: depends_on: dependencies of the packages
|
||||
:return: list of repository packages which depend on specified packages
|
||||
"""
|
||||
packages = self.packages()
|
||||
if depends_on is None:
|
||||
return packages # no list provided extract everything by default
|
||||
depends_on = set(depends_on)
|
||||
|
||||
return [
|
||||
package
|
||||
for package in packages
|
||||
if depends_on is None or depends_on.intersection(package.full_depends(self.pacman, packages))
|
||||
]
|
||||
|
@ -20,13 +20,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import aur # type: ignore
|
||||
import copy
|
||||
import logging
|
||||
|
||||
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
|
||||
from typing import Any, Dict, Iterable, List, Optional, Set, Type
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.exceptions import InvalidPackageInfo
|
||||
@ -257,6 +258,36 @@ class Package:
|
||||
|
||||
return self.version
|
||||
|
||||
def full_depends(self, pacman: Pacman, packages: Iterable[Package]) -> List[str]:
|
||||
"""
|
||||
generate full dependencies list including transitive dependencies
|
||||
:param pacman: alpm wrapper instance
|
||||
:param packages: repository package list
|
||||
:return: 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_outdated(self, remote: Package, paths: RepositoryPaths, calculate_version: bool = True) -> bool:
|
||||
"""
|
||||
check if package is out-of-dated
|
||||
|
@ -14,6 +14,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
||||
:return: generated arguments for these test cases
|
||||
"""
|
||||
args.depends_on = []
|
||||
args.dry_run = False
|
||||
return args
|
||||
|
||||
|
||||
@ -23,7 +24,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
"""
|
||||
args = _default_args(args)
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages")
|
||||
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depends_on")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
|
||||
Rebuild.run(args, "x86_64", configuration, True)
|
||||
@ -31,34 +32,43 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
||||
application_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_run_filter(args: argparse.Namespace, configuration: Configuration,
|
||||
package_ahriman: Package, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration,
|
||||
package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command with depends filter
|
||||
must run command without update itself
|
||||
"""
|
||||
args = _default_args(args)
|
||||
args.depends_on = ["python-aur"]
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages",
|
||||
return_value=[package_ahriman, package_python_schedule])
|
||||
args.dry_run = True
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages_depends_on", return_value=[package_ahriman])
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
|
||||
Rebuild.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once_with([package_ahriman])
|
||||
application_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_run_without_filter(args: argparse.Namespace, configuration: Configuration,
|
||||
package_ahriman: Package, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
def test_run_filter(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command with depends on filter
|
||||
"""
|
||||
args = _default_args(args)
|
||||
args.depends_on = ["python-aur"]
|
||||
mocker.patch("ahriman.application.application.Application.update")
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depends_on")
|
||||
|
||||
Rebuild.run(args, "x86_64", configuration, True)
|
||||
application_packages_mock.assert_called_once_with({"python-aur"})
|
||||
|
||||
|
||||
def test_run_without_filter(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must run command for all packages if no filter supplied
|
||||
"""
|
||||
args = _default_args(args)
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages",
|
||||
return_value=[package_ahriman, package_python_schedule])
|
||||
mocker.patch("ahriman.application.application.Application.update")
|
||||
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
|
||||
application_mock = mocker.patch("ahriman.application.application.Application.update")
|
||||
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depends_on")
|
||||
|
||||
Rebuild.run(args, "x86_64", configuration, True)
|
||||
application_mock.assert_called_once_with([package_ahriman, package_python_schedule])
|
||||
application_packages_mock.assert_called_once_with(None)
|
||||
|
@ -80,3 +80,23 @@ def test_packages_built(repository: Repository, mocker: MockerFixture) -> None:
|
||||
"""
|
||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path("a.tar.xz"), Path("b.pkg.tar.xz")])
|
||||
assert repository.packages_built() == [Path("b.pkg.tar.xz")]
|
||||
|
||||
|
||||
def test_packages_depends_on(repository: Repository, package_ahriman: Package, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must filter packages by depends list
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages",
|
||||
return_value=[package_ahriman, package_python_schedule])
|
||||
assert repository.packages_depends_on(["python-aur"]) == [package_ahriman]
|
||||
|
||||
|
||||
def test_packages_depends_on_empty(repository: Repository, package_ahriman: Package, package_python_schedule: Package,
|
||||
mocker: MockerFixture) -> None:
|
||||
"""
|
||||
must return all packages in case if no filter is provided
|
||||
"""
|
||||
mocker.patch("ahriman.core.repository.repository.Repository.packages",
|
||||
return_value=[package_ahriman, package_python_schedule])
|
||||
assert repository.packages_depends_on(None) == [package_ahriman, package_python_schedule]
|
||||
|
@ -82,7 +82,9 @@ def pyalpm_package_ahriman(package_ahriman: Package) -> MagicMock:
|
||||
"""
|
||||
mock = MagicMock()
|
||||
type(mock).base = PropertyMock(return_value=package_ahriman.base)
|
||||
type(mock).depends = PropertyMock(return_value=["python-aur"])
|
||||
type(mock).name = PropertyMock(return_value=package_ahriman.base)
|
||||
type(mock).provides = PropertyMock(return_value=["python-ahriman"])
|
||||
type(mock).version = PropertyMock(return_value=package_ahriman.version)
|
||||
return mock
|
||||
|
||||
|
@ -294,6 +294,24 @@ def test_actual_version_vcs_failed(package_tpacpi_bat_git: Package, repository_p
|
||||
assert package_tpacpi_bat_git.actual_version(repository_paths) == package_tpacpi_bat_git.version
|
||||
|
||||
|
||||
def test_full_depends(package_ahriman: Package, package_python_schedule: Package, pyalpm_package_ahriman: MagicMock,
|
||||
pyalpm_handle: MagicMock, mocker: MockerFixture) -> 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 + ["python-aur"]))
|
||||
assert package_python_schedule.full_depends(pyalpm_handle, [package_python_schedule]) == expected
|
||||
|
||||
|
||||
def test_is_outdated_false(package_ahriman: Package, repository_paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
must be not outdated for the same package
|
||||
|
Loading…
Reference in New Issue
Block a user