initial implementation of the local git clones (#48)

This commit is contained in:
Evgenii Alekseev 2021-12-22 15:56:24 +03:00 committed by GitHub
parent 5aac3db2d5
commit 603c5449a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 142 additions and 19 deletions

View File

@ -285,7 +285,7 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=_formatter) formatter_class=_formatter)
parser.add_argument("package", help="filter check by package base", nargs="*") parser.add_argument("package", help="filter check by package base", nargs="*")
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true") parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
parser.set_defaults(handler=handlers.Update, dry_run=True, no_aur=False, no_manual=True) parser.set_defaults(handler=handlers.Update, dry_run=True, no_aur=False, no_local=False, no_manual=True)
return parser return parser
@ -461,6 +461,7 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("package", help="filter check by package base", nargs="*") parser.add_argument("package", help="filter check by package base", nargs="*")
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true") parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
parser.add_argument("--no-aur", help="do not check for AUR updates. Implies --no-vcs", action="store_true") parser.add_argument("--no-aur", help="do not check for AUR updates. Implies --no-vcs", action="store_true")
parser.add_argument("--no-local", help="do not check local packages for updates", action="store_true")
parser.add_argument("--no-manual", help="do not include manual updates", action="store_true") parser.add_argument("--no-manual", help="do not include manual updates", action="store_true")
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true") parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
parser.set_defaults(handler=handlers.Update) parser.set_defaults(handler=handlers.Update)

View File

@ -163,27 +163,31 @@ class Repository(Properties):
packages = self.repository.process_build(level) packages = self.repository.process_build(level)
process_update(packages) process_update(packages)
def updates(self, filter_packages: Iterable[str], no_aur: bool, no_manual: bool, no_vcs: bool, def updates(self, filter_packages: Iterable[str], no_aur: bool, no_local: bool, no_manual: bool, no_vcs: bool,
log_fn: Callable[[str], None]) -> List[Package]: log_fn: Callable[[str], None]) -> List[Package]:
""" """
get list of packages to run update process get list of packages to run update process
:param filter_packages: do not check every package just specified in the list :param filter_packages: do not check every package just specified in the list
:param no_aur: do not check for aur updates :param no_aur: do not check for aur updates
:param no_local: do not check local packages for updates
:param no_manual: do not check for manual updates :param no_manual: do not check for manual updates
:param no_vcs: do not check VCS packages :param no_vcs: do not check VCS packages
:param log_fn: logger function to log updates :param log_fn: logger function to log updates
:return: list of out-of-dated packages :return: list of out-of-dated packages
""" """
updates = [] updates = {}
if not no_aur: if not no_aur:
updates.extend(self.repository.updates_aur(filter_packages, no_vcs)) updates.update({package.base: package for package in self.repository.updates_aur(filter_packages, no_vcs)})
if not no_local:
updates.update({package.base: package for package in self.repository.updates_local()})
if not no_manual: if not no_manual:
updates.extend(self.repository.updates_manual()) updates.update({package.base: package for package in self.repository.updates_manual()})
local_versions = {package.base: package.version for package in self.repository.packages()} local_versions = {package.base: package.version for package in self.repository.packages()}
for package in updates: updated_packages = [package for _, package in sorted(updates.items())]
for package in updated_packages:
UpdatePrinter(package, local_versions.get(package.base)).print( UpdatePrinter(package, local_versions.get(package.base)).print(
verbose=True, log_fn=log_fn, separator=" -> ") verbose=True, log_fn=log_fn, separator=" -> ")
return updates return updated_packages

View File

@ -46,5 +46,5 @@ class Add(Handler):
if not args.now: if not args.now:
return return
packages = application.updates(args.package, True, False, True, application.logger.info) packages = application.updates(args.package, True, True, False, True, application.logger.info)
application.update(packages) application.update(packages)

View File

@ -42,7 +42,7 @@ class Update(Handler):
:param no_report: force disable reporting :param no_report: force disable reporting
""" """
application = Application(architecture, configuration, no_report) application = Application(architecture, configuration, no_report)
packages = application.updates(args.package, args.no_aur, args.no_manual, args.no_vcs, packages = application.updates(args.package, args.no_aur, args.no_local, args.no_manual, args.no_vcs,
Update.log_fn(application, args.dry_run)) Update.log_fn(application, args.dry_run))
if args.dry_run: if args.dry_run:
return return

View File

@ -20,7 +20,7 @@
import logging import logging
from pathlib import Path from pathlib import Path
from typing import List from typing import List, Optional
from ahriman.core.util import check_output from ahriman.core.util import check_output
@ -64,7 +64,7 @@ class Sources:
patch_path.write_text(patch) patch_path.write_text(patch)
@staticmethod @staticmethod
def fetch(sources_dir: Path, remote: str) -> None: def fetch(sources_dir: Path, remote: Optional[str]) -> None:
""" """
either clone repository or update it to origin/`branch` either clone repository or update it to origin/`branch`
:param sources_dir: local path to fetch :param sources_dir: local path to fetch
@ -81,6 +81,8 @@ class Sources:
Sources.logger.info("update HEAD to remote at %s", sources_dir) Sources.logger.info("update HEAD to remote at %s", sources_dir)
Sources._check_output("git", "fetch", "origin", Sources._branch, Sources._check_output("git", "fetch", "origin", Sources._branch,
exception=None, cwd=sources_dir, logger=Sources.logger) exception=None, cwd=sources_dir, logger=Sources.logger)
elif remote is None:
Sources.logger.warning("%s is not initialized, but no remote provided", sources_dir)
else: else:
Sources.logger.info("clone remote %s to %s", remote, sources_dir) Sources.logger.info("clone remote %s to %s", remote, sources_dir)
Sources._check_output("git", "clone", remote, str(sources_dir), exception=None, logger=Sources.logger) Sources._check_output("git", "clone", remote, str(sources_dir), exception=None, logger=Sources.logger)

View File

@ -19,6 +19,7 @@
# #
from typing import Iterable, List from typing import Iterable, List
from ahriman.core.build_tools.sources import Sources
from ahriman.core.repository.cleaner import Cleaner from ahriman.core.repository.cleaner import Cleaner
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource from ahriman.models.package_source import PackageSource
@ -65,6 +66,31 @@ class UpdateHandler(Cleaner):
return result return result
def updates_local(self) -> List[Package]:
"""
check local packages for updates
:return: list of local packages which are out-of-dated
"""
result: List[Package] = []
packages = {local.base: local for local in self.packages()}
for dirname in self.paths.cache.iterdir():
try:
Sources.fetch(dirname, remote=None)
remote = Package.load(str(dirname), PackageSource.Local, self.pacman, self.aur_url)
local = packages.get(remote.base)
if local is None:
self.reporter.set_unknown(remote)
result.append(remote)
elif local.is_outdated(remote, self.paths):
self.reporter.set_pending(local.base)
result.append(remote)
except Exception:
self.logger.exception("could not procees package at %s", dirname)
return result
def updates_manual(self) -> List[Package]: def updates_manual(self) -> List[Package]:
""" """
check for packages for which manual update has been requested check for packages for which manual update has been requested

View File

@ -205,10 +205,12 @@ def test_updates_all(application_repository: Repository, package_ahriman: Packag
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur", updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur",
return_value=[package_ahriman]) return_value=[package_ahriman])
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=False, no_manual=False, no_vcs=False, log_fn=print) application_repository.updates([], no_aur=False, no_local=False, no_manual=False, no_vcs=False, log_fn=print)
updates_aur_mock.assert_called_once_with([], False) updates_aur_mock.assert_called_once_with([], False)
updates_local_mock.assert_called_once()
updates_manual_mock.assert_called_once() updates_manual_mock.assert_called_once()
@ -218,10 +220,12 @@ def test_updates_disabled(application_repository: Repository, mocker: MockerFixt
""" """
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=True, no_manual=True, no_vcs=False, log_fn=print) application_repository.updates([], no_aur=True, no_local=True, no_manual=True, no_vcs=False, log_fn=print)
updates_aur_mock.assert_not_called() updates_aur_mock.assert_not_called()
updates_local_mock.assert_not_called()
updates_manual_mock.assert_not_called() updates_manual_mock.assert_not_called()
@ -231,10 +235,27 @@ def test_updates_no_aur(application_repository: Repository, mocker: MockerFixtur
""" """
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=True, no_manual=False, no_vcs=False, log_fn=print) application_repository.updates([], no_aur=True, no_local=False, no_manual=False, no_vcs=False, log_fn=print)
updates_aur_mock.assert_not_called() updates_aur_mock.assert_not_called()
updates_local_mock.assert_called_once()
updates_manual_mock.assert_called_once()
def test_updates_no_local(application_repository: Repository, mocker: MockerFixture) -> None:
"""
must get updates without local packages
"""
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=False, no_local=True, no_manual=False, no_vcs=False, log_fn=print)
updates_aur_mock.assert_called_once_with([], False)
updates_local_mock.assert_not_called()
updates_manual_mock.assert_called_once() updates_manual_mock.assert_called_once()
@ -244,10 +265,12 @@ def test_updates_no_manual(application_repository: Repository, mocker: MockerFix
""" """
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=False, no_manual=True, no_vcs=False, log_fn=print) application_repository.updates([], no_aur=False, no_local=False, no_manual=True, no_vcs=False, log_fn=print)
updates_aur_mock.assert_called_once_with([], False) updates_aur_mock.assert_called_once_with([], False)
updates_local_mock.assert_called_once()
updates_manual_mock.assert_not_called() updates_manual_mock.assert_not_called()
@ -257,21 +280,26 @@ def test_updates_no_vcs(application_repository: Repository, mocker: MockerFixtur
""" """
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates([], no_aur=False, no_manual=False, no_vcs=True, log_fn=print) application_repository.updates([], no_aur=False, no_local=False, no_manual=False, no_vcs=True, log_fn=print)
updates_aur_mock.assert_called_once_with([], True) updates_aur_mock.assert_called_once_with([], True)
updates_local_mock.assert_called_once()
updates_manual_mock.assert_called_once() updates_manual_mock.assert_called_once()
def test_updates_with_filter(application_repository: Repository, mocker: MockerFixture) -> None: def test_updates_with_filter(application_repository: Repository, mocker: MockerFixture) -> None:
""" """
must get updates without VCS must get updates with filter
""" """
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
application_repository.updates(["filter"], no_aur=False, no_manual=False, no_vcs=False, log_fn=print) application_repository.updates(["filter"], no_aur=False, no_local=False, no_manual=False, no_vcs=False,
log_fn=print)
updates_aur_mock.assert_called_once_with(["filter"], False) updates_aur_mock.assert_called_once_with(["filter"], False)
updates_local_mock.assert_called_once()
updates_manual_mock.assert_called_once() updates_manual_mock.assert_called_once()

View File

@ -16,6 +16,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
args.package = [] args.package = []
args.dry_run = False args.dry_run = False
args.no_aur = False args.no_aur = False
args.no_local = False
args.no_manual = False args.no_manual = False
args.no_vcs = False args.no_vcs = False
return args return args

View File

@ -86,6 +86,23 @@ def test_fetch_new(mocker: MockerFixture) -> None:
]) ])
def test_fetch_new_without_remote(mocker: MockerFixture) -> None:
"""
must fetch nothing in case if no remote set
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
local = Path("local")
Sources.fetch(local, None)
check_output_mock.assert_has_calls([
mock.call("git", "checkout", "--force", Sources._branch,
exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
mock.call("git", "reset", "--hard", f"origin/{Sources._branch}",
exception=None, cwd=local, logger=pytest.helpers.anyvar(int))
])
def test_has_remotes(mocker: MockerFixture) -> None: def test_has_remotes(mocker: MockerFixture) -> None:
""" """
must ask for remotes must ask for remotes

View File

@ -81,6 +81,50 @@ def test_updates_aur_ignore_vcs(update_handler: UpdateHandler, package_ahriman:
package_is_outdated_mock.assert_not_called() package_is_outdated_mock.assert_not_called()
def test_updates_local(update_handler: UpdateHandler, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must check for updates for locally stored packages
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
package_load_mock = mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
assert update_handler.updates_local() == [package_ahriman]
fetch_mock.assert_called_once_with(package_ahriman.base, remote=None)
package_load_mock.assert_called_once()
status_client_mock.assert_called_once()
def test_updates_local_unknown(update_handler: UpdateHandler, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must return unknown package as out-dated
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[])
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
assert update_handler.updates_local() == [package_ahriman]
status_client_mock.assert_called_once()
def test_updates_local_with_failures(update_handler: UpdateHandler, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must process local through the packages with failure
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages")
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", side_effect=Exception())
assert not update_handler.updates_local()
def test_updates_manual_clear(update_handler: UpdateHandler, mocker: MockerFixture) -> None: def test_updates_manual_clear(update_handler: UpdateHandler, mocker: MockerFixture) -> None:
""" """
requesting manual updates must clear packages directory requesting manual updates must clear packages directory
@ -125,7 +169,7 @@ def test_updates_manual_status_unknown(update_handler: UpdateHandler, package_ah
def test_updates_manual_with_failures(update_handler: UpdateHandler, package_ahriman: Package, def test_updates_manual_with_failures(update_handler: UpdateHandler, package_ahriman: Package,
mocker: MockerFixture) -> None: mocker: MockerFixture) -> None:
""" """
must process through the packages with failure must process manual through the packages with failure
""" """
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base]) mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.base])
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[]) mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[])