mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-11-04 07:43:42 +00:00 
			
		
		
		
	implement local reporter mode
This commit is contained in:
		@ -446,7 +446,7 @@ def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    parser = root.add_parser("patch-list", help="list patch sets",
 | 
					    parser = root.add_parser("patch-list", help="list patch sets",
 | 
				
			||||||
                             description="list available patches for the package", formatter_class=_formatter)
 | 
					                             description="list available patches for the package", formatter_class=_formatter)
 | 
				
			||||||
    parser.add_argument("package", help="package base", nargs="?")
 | 
					    parser.add_argument("package", help="package base")
 | 
				
			||||||
    parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
 | 
					    parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
 | 
				
			||||||
    parser.add_argument("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables",
 | 
					    parser.add_argument("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables",
 | 
				
			||||||
                        action="append")
 | 
					                        action="append")
 | 
				
			||||||
 | 
				
			|||||||
@ -161,8 +161,7 @@ class Application(ApplicationPackages, ApplicationRepository):
 | 
				
			|||||||
                    package = Package.from_aur(package_name, username)
 | 
					                    package = Package.from_aur(package_name, username)
 | 
				
			||||||
                with_dependencies[package.base] = package
 | 
					                with_dependencies[package.base] = package
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # register package in local database
 | 
					                # register package in the database
 | 
				
			||||||
                self.database.package_base_update(package)
 | 
					 | 
				
			||||||
                self.repository.reporter.set_unknown(package)
 | 
					                self.repository.reporter.set_unknown(package)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return list(with_dependencies.values())
 | 
					        return list(with_dependencies.values())
 | 
				
			||||||
 | 
				
			|||||||
@ -65,7 +65,7 @@ class ApplicationPackages(ApplicationProperties):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        package = Package.from_aur(source, username)
 | 
					        package = Package.from_aur(source, username)
 | 
				
			||||||
        self.database.build_queue_insert(package)
 | 
					        self.database.build_queue_insert(package)
 | 
				
			||||||
        self.database.package_base_update(package)
 | 
					        self.reporter.set_unknown(package)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _add_directory(self, source: str, *_: Any) -> None:
 | 
					    def _add_directory(self, source: str, *_: Any) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -139,7 +139,7 @@ class ApplicationPackages(ApplicationProperties):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        package = Package.from_official(source, self.repository.pacman, username)
 | 
					        package = Package.from_official(source, self.repository.pacman, username)
 | 
				
			||||||
        self.database.build_queue_insert(package)
 | 
					        self.database.build_queue_insert(package)
 | 
				
			||||||
        self.database.package_base_update(package)
 | 
					        self.reporter.set_unknown(package)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def add(self, names: Iterable[str], source: PackageSource, username: str | None = None) -> None:
 | 
					    def add(self, names: Iterable[str], source: PackageSource, username: str | None = None) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
				
			|||||||
@ -21,6 +21,7 @@ from ahriman.core.configuration import Configuration
 | 
				
			|||||||
from ahriman.core.database import SQLite
 | 
					from ahriman.core.database import SQLite
 | 
				
			||||||
from ahriman.core.log import LazyLogging
 | 
					from ahriman.core.log import LazyLogging
 | 
				
			||||||
from ahriman.core.repository import Repository
 | 
					from ahriman.core.repository import Repository
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.models.pacman_synchronization import PacmanSynchronization
 | 
					from ahriman.models.pacman_synchronization import PacmanSynchronization
 | 
				
			||||||
from ahriman.models.repository_id import RepositoryId
 | 
					from ahriman.models.repository_id import RepositoryId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -63,3 +64,13 @@ class ApplicationProperties(LazyLogging):
 | 
				
			|||||||
            str: repository architecture
 | 
					            str: repository architecture
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return self.repository_id.architecture
 | 
					        return self.repository_id.architecture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def reporter(self) -> Client:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        instance of the web/database client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            Client: repository reposter
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self.repository.reporter
 | 
				
			||||||
 | 
				
			|||||||
@ -39,10 +39,8 @@ class ApplicationRepository(ApplicationProperties):
 | 
				
			|||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            packages(Iterable[Package]): list of packages to retrieve changes
 | 
					            packages(Iterable[Package]): list of packages to retrieve changes
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        last_commit_hashes = self.database.hashes_get()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for package in packages:
 | 
					        for package in packages:
 | 
				
			||||||
            last_commit_sha = last_commit_hashes.get(package.base)
 | 
					            last_commit_sha = self.reporter.package_changes_get(package.base).last_commit_sha
 | 
				
			||||||
            if last_commit_sha is None:
 | 
					            if last_commit_sha is None:
 | 
				
			||||||
                continue  # skip check in case if we can't calculate diff
 | 
					                continue  # skip check in case if we can't calculate diff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -50,7 +50,8 @@ class Add(Handler):
 | 
				
			|||||||
        application.add(args.package, args.source, args.username)
 | 
					        application.add(args.package, args.source, args.username)
 | 
				
			||||||
        patches = [PkgbuildPatch.from_env(patch) for patch in args.variable] if args.variable is not None else []
 | 
					        patches = [PkgbuildPatch.from_env(patch) for patch in args.variable] if args.variable is not None else []
 | 
				
			||||||
        for package in args.package:  # for each requested package insert patch
 | 
					        for package in args.package:  # for each requested package insert patch
 | 
				
			||||||
            application.database.patches_insert(package, patches)
 | 
					            for patch in patches:
 | 
				
			||||||
 | 
					                application.reporter.package_patches_add(package, patch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not args.now:
 | 
					        if not args.now:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
				
			|||||||
@ -116,25 +116,29 @@ class Patch(Handler):
 | 
				
			|||||||
            package_base(str): package base
 | 
					            package_base(str): package base
 | 
				
			||||||
            patch(PkgbuildPatch): patch descriptor
 | 
					            patch(PkgbuildPatch): patch descriptor
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        application.database.patches_insert(package_base, [patch])
 | 
					        application.reporter.package_patches_add(package_base, patch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def patch_set_list(application: Application, package_base: str | None, variables: list[str] | None,
 | 
					    def patch_set_list(application: Application, package_base: str, variables: list[str] | None,
 | 
				
			||||||
                       exit_code: bool) -> None:
 | 
					                       exit_code: bool) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        list patches available for the package base
 | 
					        list patches available for the package base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            application(Application): application instance
 | 
					            application(Application): application instance
 | 
				
			||||||
            package_base(str | None): package base
 | 
					            package_base(str): package base
 | 
				
			||||||
            variables(list[str] | None): extract patches only for specified PKGBUILD variables
 | 
					            variables(list[str] | None): extract patches only for specified PKGBUILD variables
 | 
				
			||||||
            exit_code(bool): exit with error on empty search result
 | 
					            exit_code(bool): exit with error on empty search result
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        patches = application.database.patches_list(package_base, variables)
 | 
					        patches = []
 | 
				
			||||||
 | 
					        if variables is not None:
 | 
				
			||||||
 | 
					            for variable in variables:
 | 
				
			||||||
 | 
					                patches.extend(application.reporter.package_patches_get(package_base, variable))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            patches = application.reporter.package_patches_get(package_base, variables)
 | 
				
			||||||
        Patch.check_if_empty(exit_code, not patches)
 | 
					        Patch.check_if_empty(exit_code, not patches)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for base, patch in patches.items():
 | 
					        PatchPrinter(package_base, patches)(verbose=True, separator=" = ")
 | 
				
			||||||
            PatchPrinter(base, patch)(verbose=True, separator=" = ")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def patch_set_remove(application: Application, package_base: str, variables: list[str] | None) -> None:
 | 
					    def patch_set_remove(application: Application, package_base: str, variables: list[str] | None) -> None:
 | 
				
			||||||
@ -146,4 +150,8 @@ class Patch(Handler):
 | 
				
			|||||||
            package_base(str): package base
 | 
					            package_base(str): package base
 | 
				
			||||||
            variables(list[str] | None): remove patches only for specified PKGBUILD variables
 | 
					            variables(list[str] | None): remove patches only for specified PKGBUILD variables
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        application.database.patches_remove(package_base, variables)
 | 
					        if variables is not None:
 | 
				
			||||||
 | 
					            for variable in variables:  # iterate over single variable
 | 
				
			||||||
 | 
					                application.reporter.package_patches_remove(package_base, variable)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            application.reporter.package_patches_remove(package_base, variables)  # just pass as is
 | 
				
			||||||
 | 
				
			|||||||
@ -76,7 +76,7 @@ class Rebuild(Handler):
 | 
				
			|||||||
        if from_database:
 | 
					        if from_database:
 | 
				
			||||||
            return [
 | 
					            return [
 | 
				
			||||||
                package
 | 
					                package
 | 
				
			||||||
                for (package, last_status) in application.database.packages_get()
 | 
					                for (package, last_status) in application.reporter.package_get(None)
 | 
				
			||||||
                if status is None or last_status.status == status
 | 
					                if status is None or last_status.status == status
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -56,7 +56,7 @@ class StatusUpdate(Handler):
 | 
				
			|||||||
                    if (local := next((package for package in packages if package.base == base), None)) is not None:
 | 
					                    if (local := next((package for package in packages if package.base == base), None)) is not None:
 | 
				
			||||||
                        client.package_add(local, args.status)
 | 
					                        client.package_add(local, args.status)
 | 
				
			||||||
                    else:
 | 
					                    else:
 | 
				
			||||||
                        client.package_update(base, args.status)
 | 
					                        client.package_set(base, args.status)
 | 
				
			||||||
            case Action.Update:
 | 
					            case Action.Update:
 | 
				
			||||||
                # update service status
 | 
					                # update service status
 | 
				
			||||||
                client.status_update(args.status)
 | 
					                client.status_update(args.status)
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,6 @@ from pathlib import Path
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from ahriman.core.build_tools.sources import Sources
 | 
					from ahriman.core.build_tools.sources import Sources
 | 
				
			||||||
from ahriman.core.configuration import Configuration
 | 
					from ahriman.core.configuration import Configuration
 | 
				
			||||||
from ahriman.core.database import SQLite
 | 
					 | 
				
			||||||
from ahriman.core.exceptions import BuildError
 | 
					from ahriman.core.exceptions import BuildError
 | 
				
			||||||
from ahriman.core.log import LazyLogging
 | 
					from ahriman.core.log import LazyLogging
 | 
				
			||||||
from ahriman.core.util import check_output
 | 
					from ahriman.core.util import check_output
 | 
				
			||||||
@ -116,20 +115,20 @@ class Task(LazyLogging):
 | 
				
			|||||||
        # e.g. in some cases packagelist command produces debug packages which were not actually built
 | 
					        # e.g. in some cases packagelist command produces debug packages which were not actually built
 | 
				
			||||||
        return list(filter(lambda path: path.is_file(), map(Path, packages)))
 | 
					        return list(filter(lambda path: path.is_file(), map(Path, packages)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def init(self, sources_dir: Path, database: SQLite, local_version: str | None) -> str | None:
 | 
					    def init(self, sources_dir: Path, patches: list[PkgbuildPatch], local_version: str | None) -> str | None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        fetch package from git
 | 
					        fetch package from git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            sources_dir(Path): local path to fetch
 | 
					            sources_dir(Path): local path to fetch
 | 
				
			||||||
            database(SQLite): database instance
 | 
					            patches(list[PkgbuildPatch]): list of patches for the package
 | 
				
			||||||
            local_version(str | None): local version of the package. If set and equal to current version, it will
 | 
					            local_version(str | None): local version of the package. If set and equal to current version, it will
 | 
				
			||||||
                automatically bump pkgrel
 | 
					                automatically bump pkgrel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Returns:
 | 
					        Returns:
 | 
				
			||||||
            str | None: current commit sha if available
 | 
					            str | None: current commit sha if available
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        last_commit_sha = Sources.load(sources_dir, self.package, database.patches_get(self.package.base), self.paths)
 | 
					        last_commit_sha = Sources.load(sources_dir, self.package, patches, self.paths)
 | 
				
			||||||
        if local_version is None:
 | 
					        if local_version is None:
 | 
				
			||||||
            return last_commit_sha  # there is no local package or pkgrel increment is disabled
 | 
					            return last_commit_sha  # there is no local package or pkgrel increment is disabled
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -117,27 +117,3 @@ class ChangesOperations(Operations):
 | 
				
			|||||||
                })
 | 
					                })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return self.with_connection(run, commit=True)
 | 
					        return self.with_connection(run, commit=True)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def hashes_get(self, repository_id: RepositoryId | None = None) -> dict[str, str]:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        extract last commit hashes if available
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Args:
 | 
					 | 
				
			||||||
            repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Returns:
 | 
					 | 
				
			||||||
            dict[str, str]: map of package base to its last commit hash
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        repository_id = repository_id or self._repository_id
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def run(connection: Connection) -> dict[str, str]:
 | 
					 | 
				
			||||||
            return {
 | 
					 | 
				
			||||||
                row["package_base"]: row["last_commit_sha"]
 | 
					 | 
				
			||||||
                for row in connection.execute(
 | 
					 | 
				
			||||||
                    """select package_base, last_commit_sha from package_changes where repository = :repository""",
 | 
					 | 
				
			||||||
                    {"repository": repository_id.id}
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return self.with_connection(run)
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ from typing import Any, TypeVar
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from ahriman.core.log import LazyLogging
 | 
					from ahriman.core.log import LazyLogging
 | 
				
			||||||
from ahriman.models.repository_id import RepositoryId
 | 
					from ahriman.models.repository_id import RepositoryId
 | 
				
			||||||
 | 
					from ahriman.models.repository_paths import RepositoryPaths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
T = TypeVar("T")
 | 
					T = TypeVar("T")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -38,7 +38,7 @@ class Operations(LazyLogging):
 | 
				
			|||||||
        path(Path): path to the database file
 | 
					        path(Path): path to the database file
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, path: Path, repository_id: RepositoryId) -> None:
 | 
					    def __init__(self, path: Path, repository_id: RepositoryId, repository_paths: RepositoryPaths) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        default constructor
 | 
					        default constructor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -48,6 +48,7 @@ class Operations(LazyLogging):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        self.path = path
 | 
					        self.path = path
 | 
				
			||||||
        self._repository_id = repository_id
 | 
					        self._repository_id = repository_id
 | 
				
			||||||
 | 
					        self._repository_paths = repository_paths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def factory(cursor: sqlite3.Cursor, row: tuple[Any, ...]) -> dict[str, Any]:
 | 
					    def factory(cursor: sqlite3.Cursor, row: tuple[Any, ...]) -> dict[str, Any]:
 | 
				
			||||||
 | 
				
			|||||||
@ -150,34 +150,6 @@ class PackageOperations(Operations):
 | 
				
			|||||||
            """,
 | 
					            """,
 | 
				
			||||||
            package_list)
 | 
					            package_list)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def _package_update_insert_status(connection: Connection, package_base: str, status: BuildStatus,
 | 
					 | 
				
			||||||
                                      repository_id: RepositoryId) -> None:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        insert base package status into table
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Args:
 | 
					 | 
				
			||||||
            connection(Connection): database connection
 | 
					 | 
				
			||||||
            package_base(str): package base name
 | 
					 | 
				
			||||||
            status(BuildStatus): new build status
 | 
					 | 
				
			||||||
            repository_id(RepositoryId): repository unique identifier
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        connection.execute(
 | 
					 | 
				
			||||||
            """
 | 
					 | 
				
			||||||
            insert into package_statuses
 | 
					 | 
				
			||||||
            (package_base, status, last_updated, repository)
 | 
					 | 
				
			||||||
            values
 | 
					 | 
				
			||||||
            (:package_base, :status, :last_updated, :repository)
 | 
					 | 
				
			||||||
            on conflict (package_base, repository) do update set
 | 
					 | 
				
			||||||
            status = :status, last_updated = :last_updated
 | 
					 | 
				
			||||||
            """,
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                "package_base": package_base,
 | 
					 | 
				
			||||||
                "status": status.status.value,
 | 
					 | 
				
			||||||
                "last_updated": status.timestamp,
 | 
					 | 
				
			||||||
                "repository": repository_id.id,
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def _packages_get_select_package_bases(connection: Connection, repository_id: RepositoryId) -> dict[str, Package]:
 | 
					    def _packages_get_select_package_bases(connection: Connection, repository_id: RepositoryId) -> dict[str, Package]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -277,20 +249,18 @@ class PackageOperations(Operations):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return self.with_connection(run, commit=True)
 | 
					        return self.with_connection(run, commit=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_update(self, package: Package, status: BuildStatus, repository_id: RepositoryId | None = None) -> None:
 | 
					    def package_update(self, package: Package, repository_id: RepositoryId | None = None) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        update package status
 | 
					        update package status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package(Package): package properties
 | 
					            package(Package): package properties
 | 
				
			||||||
            status(BuildStatus): new build status
 | 
					 | 
				
			||||||
            repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
 | 
					            repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        repository_id = repository_id or self._repository_id
 | 
					        repository_id = repository_id or self._repository_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def run(connection: Connection) -> None:
 | 
					        def run(connection: Connection) -> None:
 | 
				
			||||||
            self._package_update_insert_base(connection, package, repository_id)
 | 
					            self._package_update_insert_base(connection, package, repository_id)
 | 
				
			||||||
            self._package_update_insert_status(connection, package.base, status, repository_id)
 | 
					 | 
				
			||||||
            self._package_update_insert_packages(connection, package, repository_id)
 | 
					            self._package_update_insert_packages(connection, package, repository_id)
 | 
				
			||||||
            self._package_remove_packages(connection, package.base, package.packages.keys(), repository_id)
 | 
					            self._package_remove_packages(connection, package.base, package.packages.keys(), repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -336,3 +306,33 @@ class PackageOperations(Operations):
 | 
				
			|||||||
            package_base: package.remote
 | 
					            package_base: package.remote
 | 
				
			||||||
            for package_base, package in self.with_connection(run).items()
 | 
					            for package_base, package in self.with_connection(run).items()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def status_update(self, package_base: str, status: BuildStatus, repository_id: RepositoryId | None = None) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        insert base package status into table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base name
 | 
				
			||||||
 | 
					            status(BuildStatus): new build status
 | 
				
			||||||
 | 
					            repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        repository_id = repository_id or self._repository_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def run(connection: Connection) -> None:
 | 
				
			||||||
 | 
					            connection.execute(
 | 
				
			||||||
 | 
					                """
 | 
				
			||||||
 | 
					                insert into package_statuses
 | 
				
			||||||
 | 
					                (package_base, status, last_updated, repository)
 | 
				
			||||||
 | 
					                values
 | 
				
			||||||
 | 
					                (:package_base, :status, :last_updated, :repository)
 | 
				
			||||||
 | 
					                on conflict (package_base, repository) do update set
 | 
				
			||||||
 | 
					                status = :status, last_updated = :last_updated
 | 
				
			||||||
 | 
					                """,
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "package_base": package_base,
 | 
				
			||||||
 | 
					                    "status": status.status.value,
 | 
				
			||||||
 | 
					                    "last_updated": status.timestamp,
 | 
				
			||||||
 | 
					                    "repository": repository_id.id,
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.with_connection(run, commit=True)
 | 
				
			||||||
 | 
				
			|||||||
@ -66,7 +66,7 @@ class SQLite(
 | 
				
			|||||||
        path = cls.database_path(configuration)
 | 
					        path = cls.database_path(configuration)
 | 
				
			||||||
        _, repository_id = configuration.check_loaded()
 | 
					        _, repository_id = configuration.check_loaded()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        database = cls(path, repository_id)
 | 
					        database = cls(path, repository_id, configuration.repository_paths)
 | 
				
			||||||
        database.init(configuration)
 | 
					        database.init(configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return database
 | 
					        return database
 | 
				
			||||||
@ -119,3 +119,6 @@ class SQLite(
 | 
				
			|||||||
        self.logs_remove(package_base, None)
 | 
					        self.logs_remove(package_base, None)
 | 
				
			||||||
        self.changes_remove(package_base)
 | 
					        self.changes_remove(package_base)
 | 
				
			||||||
        self.dependencies_remove(package_base)
 | 
					        self.dependencies_remove(package_base)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # remove local cache too
 | 
				
			||||||
 | 
					        self._repository_paths.tree_clear(package_base)
 | 
				
			||||||
 | 
				
			|||||||
@ -25,9 +25,9 @@ from tempfile import TemporaryDirectory
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from ahriman.core.build_tools.sources import Sources
 | 
					from ahriman.core.build_tools.sources import Sources
 | 
				
			||||||
from ahriman.core.configuration import Configuration
 | 
					from ahriman.core.configuration import Configuration
 | 
				
			||||||
from ahriman.core.database import SQLite
 | 
					 | 
				
			||||||
from ahriman.core.exceptions import GitRemoteError
 | 
					from ahriman.core.exceptions import GitRemoteError
 | 
				
			||||||
from ahriman.core.log import LazyLogging
 | 
					from ahriman.core.log import LazyLogging
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
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
 | 
				
			||||||
from ahriman.models.remote_source import RemoteSource
 | 
					from ahriman.models.remote_source import RemoteSource
 | 
				
			||||||
@ -40,20 +40,20 @@ class RemotePush(LazyLogging):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Attributes:
 | 
					    Attributes:
 | 
				
			||||||
        commit_author(tuple[str, str] | None): optional commit author in form of git config
 | 
					        commit_author(tuple[str, str] | None): optional commit author in form of git config
 | 
				
			||||||
        database(SQLite): database instance
 | 
					 | 
				
			||||||
        remote_source(RemoteSource): repository remote source (remote pull url and branch)
 | 
					        remote_source(RemoteSource): repository remote source (remote pull url and branch)
 | 
				
			||||||
 | 
					        reporter(Client): reporter client used for additional information retrieval
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, database: SQLite, configuration: Configuration, section: str) -> None:
 | 
					    def __init__(self, reporter: Client, configuration: Configuration, section: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        default constructor
 | 
					        default constructor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            database(SQLite): database instance
 | 
					            reporter(Client): reporter client
 | 
				
			||||||
            configuration(Configuration): configuration instance
 | 
					            configuration(Configuration): configuration instance
 | 
				
			||||||
            section(str): settings section name
 | 
					            section(str): settings section name
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.database = database
 | 
					        self.reporter = reporter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        commit_email = configuration.get(section, "commit_email", fallback="ahriman@localhost")
 | 
					        commit_email = configuration.get(section, "commit_email", fallback="ahriman@localhost")
 | 
				
			||||||
        commit_user = configuration.get(section, "commit_user", fallback="ahriman")
 | 
					        commit_user = configuration.get(section, "commit_user", fallback="ahriman")
 | 
				
			||||||
@ -92,7 +92,7 @@ class RemotePush(LazyLogging):
 | 
				
			|||||||
            else:
 | 
					            else:
 | 
				
			||||||
                shutil.rmtree(git_file)
 | 
					                shutil.rmtree(git_file)
 | 
				
			||||||
        # ...copy all patches...
 | 
					        # ...copy all patches...
 | 
				
			||||||
        for patch in self.database.patches_get(package.base):
 | 
					        for patch in self.reporter.package_patches_get(package.base, None):
 | 
				
			||||||
            filename = f"ahriman-{package.base}.patch" if patch.key is None else f"ahriman-{patch.key}.patch"
 | 
					            filename = f"ahriman-{package.base}.patch" if patch.key is None else f"ahriman-{patch.key}.patch"
 | 
				
			||||||
            patch.write(package_target_dir / filename)
 | 
					            patch.write(package_target_dir / filename)
 | 
				
			||||||
        # ...and finally return path to the copied directory
 | 
					        # ...and finally return path to the copied directory
 | 
				
			||||||
 | 
				
			|||||||
@ -19,8 +19,8 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
from ahriman.core import context
 | 
					from ahriman.core import context
 | 
				
			||||||
from ahriman.core.configuration import Configuration
 | 
					from ahriman.core.configuration import Configuration
 | 
				
			||||||
from ahriman.core.database import SQLite
 | 
					 | 
				
			||||||
from ahriman.core.gitremote.remote_push import RemotePush
 | 
					from ahriman.core.gitremote.remote_push import RemotePush
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.core.triggers import Trigger
 | 
					from ahriman.core.triggers import Trigger
 | 
				
			||||||
from ahriman.models.package import Package
 | 
					from ahriman.models.package import Package
 | 
				
			||||||
from ahriman.models.repository_id import RepositoryId
 | 
					from ahriman.models.repository_id import RepositoryId
 | 
				
			||||||
@ -110,10 +110,10 @@ class RemotePushTrigger(Trigger):
 | 
				
			|||||||
            GitRemoteError: if database is not set in context
 | 
					            GitRemoteError: if database is not set in context
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        ctx = context.get()
 | 
					        ctx = context.get()
 | 
				
			||||||
        database = ctx.get(SQLite)
 | 
					        reporter = ctx.get(Client)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for target in self.targets:
 | 
					        for target in self.targets:
 | 
				
			||||||
            section, _ = self.configuration.gettype(
 | 
					            section, _ = self.configuration.gettype(
 | 
				
			||||||
                target, self.repository_id, fallback=self.CONFIGURATION_SCHEMA_FALLBACK)
 | 
					                target, self.repository_id, fallback=self.CONFIGURATION_SCHEMA_FALLBACK)
 | 
				
			||||||
            runner = RemotePush(database, self.configuration, section)
 | 
					            runner = RemotePush(reporter, self.configuration, section)
 | 
				
			||||||
            runner.run(result)
 | 
					            runner.run(result)
 | 
				
			||||||
 | 
				
			|||||||
@ -92,7 +92,7 @@ class HttpLogHandler(logging.Handler):
 | 
				
			|||||||
            return  # in case if no package base supplied we need just skip log message
 | 
					            return  # in case if no package base supplied we need just skip log message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.reporter.package_logs(log_record_id, record)
 | 
					            self.reporter.package_logs_add(log_record_id, record.created, record.getMessage())
 | 
				
			||||||
        except Exception:
 | 
					        except Exception:
 | 
				
			||||||
            if self.suppress_errors:
 | 
					            if self.suppress_errors:
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
 | 
				
			|||||||
@ -58,7 +58,8 @@ class Executor(PackageInfo, Cleaner):
 | 
				
			|||||||
            self.reporter.set_building(package.base)
 | 
					            self.reporter.set_building(package.base)
 | 
				
			||||||
            task = Task(package, self.configuration, self.architecture, self.paths)
 | 
					            task = Task(package, self.configuration, self.architecture, self.paths)
 | 
				
			||||||
            local_version = local_versions.get(package.base) if bump_pkgrel else None
 | 
					            local_version = local_versions.get(package.base) if bump_pkgrel else None
 | 
				
			||||||
            commit_sha = task.init(local_path, self.database, local_version)
 | 
					            patches = self.reporter.package_patches_get(package.base, None)
 | 
				
			||||||
 | 
					            commit_sha = task.init(local_path, patches, local_version)
 | 
				
			||||||
            built = task.build(local_path, PACKAGER=packager_id)
 | 
					            built = task.build(local_path, PACKAGER=packager_id)
 | 
				
			||||||
            for src in built:
 | 
					            for src in built:
 | 
				
			||||||
                dst = self.paths.packages / src.name
 | 
					                dst = self.paths.packages / src.name
 | 
				
			||||||
@ -80,7 +81,7 @@ class Executor(PackageInfo, Cleaner):
 | 
				
			|||||||
                    self.reporter.package_changes_set(single.base, Changes(last_commit_sha))
 | 
					                    self.reporter.package_changes_set(single.base, Changes(last_commit_sha))
 | 
				
			||||||
                    # update dependencies list
 | 
					                    # update dependencies list
 | 
				
			||||||
                    dependencies = PackageArchive(self.paths.build_directory, single).depends_on()
 | 
					                    dependencies = PackageArchive(self.paths.build_directory, single).depends_on()
 | 
				
			||||||
                    self.database.dependencies_insert(dependencies)
 | 
					                    self.reporter.package_dependencies_set(dependencies)
 | 
				
			||||||
                    # update result set
 | 
					                    # update result set
 | 
				
			||||||
                    result.add_updated(single)
 | 
					                    result.add_updated(single)
 | 
				
			||||||
                except Exception:
 | 
					                except Exception:
 | 
				
			||||||
@ -102,9 +103,7 @@ class Executor(PackageInfo, Cleaner):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        def remove_base(package_base: str) -> None:
 | 
					        def remove_base(package_base: str) -> None:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                self.paths.tree_clear(package_base)  # remove all internal files
 | 
					                self.reporter.package_remove(package_base)
 | 
				
			||||||
                self.database.package_clear(package_base)
 | 
					 | 
				
			||||||
                self.reporter.package_remove(package_base)  # we only update status page in case of base removal
 | 
					 | 
				
			||||||
            except Exception:
 | 
					            except Exception:
 | 
				
			||||||
                self.logger.exception("could not remove base %s", package_base)
 | 
					                self.logger.exception("could not remove base %s", package_base)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -43,15 +43,14 @@ class PackageInfo(RepositoryProperties):
 | 
				
			|||||||
        Returns:
 | 
					        Returns:
 | 
				
			||||||
            list[Package]: list of read packages
 | 
					            list[Package]: list of read packages
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        sources = self.database.remotes_get()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        result: dict[str, Package] = {}
 | 
					        result: dict[str, Package] = {}
 | 
				
			||||||
        # we are iterating over bases, not single packages
 | 
					        # we are iterating over bases, not single packages
 | 
				
			||||||
        for full_path in packages:
 | 
					        for full_path in packages:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                local = Package.from_archive(full_path, self.pacman)
 | 
					                local = Package.from_archive(full_path, self.pacman)
 | 
				
			||||||
                if (source := sources.get(local.base)) is not None:
 | 
					                remote, _ = next(iter(self.reporter.package_get(local.base)), (None, None))
 | 
				
			||||||
                    local.remote = source
 | 
					                if remote is not None:  # update source with remote
 | 
				
			||||||
 | 
					                    local.remote = remote.remote
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                current = result.setdefault(local.base, local)
 | 
					                current = result.setdefault(local.base, local)
 | 
				
			||||||
                if current.version != local.version:
 | 
					                if current.version != local.version:
 | 
				
			||||||
@ -78,7 +77,8 @@ class PackageInfo(RepositoryProperties):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name:
 | 
					        with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name:
 | 
				
			||||||
            dir_path = Path(dir_name)
 | 
					            dir_path = Path(dir_name)
 | 
				
			||||||
            current_commit_sha = Sources.load(dir_path, package, self.database.patches_get(package.base), self.paths)
 | 
					            patches = self.reporter.package_patches_get(package.base, None)
 | 
				
			||||||
 | 
					            current_commit_sha = Sources.load(dir_path, package, patches, self.paths)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            changes: str | None = None
 | 
					            changes: str | None = None
 | 
				
			||||||
            if current_commit_sha != last_commit_sha:
 | 
					            if current_commit_sha != last_commit_sha:
 | 
				
			||||||
 | 
				
			|||||||
@ -26,6 +26,7 @@ from ahriman.core.database import SQLite
 | 
				
			|||||||
from ahriman.core.repository.executor import Executor
 | 
					from ahriman.core.repository.executor import Executor
 | 
				
			||||||
from ahriman.core.repository.update_handler import UpdateHandler
 | 
					from ahriman.core.repository.update_handler import UpdateHandler
 | 
				
			||||||
from ahriman.core.sign.gpg import GPG
 | 
					from ahriman.core.sign.gpg import GPG
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.models.pacman_synchronization import PacmanSynchronization
 | 
					from ahriman.models.pacman_synchronization import PacmanSynchronization
 | 
				
			||||||
from ahriman.models.repository_id import RepositoryId
 | 
					from ahriman.models.repository_id import RepositoryId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,6 +93,7 @@ class Repository(Executor, UpdateHandler):
 | 
				
			|||||||
        ctx.set(Configuration, self.configuration)
 | 
					        ctx.set(Configuration, self.configuration)
 | 
				
			||||||
        ctx.set(Pacman, self.pacman)
 | 
					        ctx.set(Pacman, self.pacman)
 | 
				
			||||||
        ctx.set(GPG, self.sign)
 | 
					        ctx.set(GPG, self.sign)
 | 
				
			||||||
 | 
					        ctx.set(Client, self.reporter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ctx.set(type(self), self)
 | 
					        ctx.set(type(self), self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -75,7 +75,7 @@ class RepositoryProperties(LazyLogging):
 | 
				
			|||||||
        self.pacman = Pacman(repository_id, configuration, refresh_database=refresh_pacman_database)
 | 
					        self.pacman = Pacman(repository_id, configuration, refresh_database=refresh_pacman_database)
 | 
				
			||||||
        self.sign = GPG(configuration)
 | 
					        self.sign = GPG(configuration)
 | 
				
			||||||
        self.repo = Repo(self.name, self.paths, self.sign.repository_sign_args)
 | 
					        self.repo = Repo(self.name, self.paths, self.sign.repository_sign_args)
 | 
				
			||||||
        self.reporter = Client.load(repository_id, configuration, report=report)
 | 
					        self.reporter = Client.load(repository_id, configuration, database, report=report)
 | 
				
			||||||
        self.triggers = TriggerLoader.load(repository_id, configuration)
 | 
					        self.triggers = TriggerLoader.load(repository_id, configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
 | 
				
			|||||||
@ -98,18 +98,16 @@ class UpdateHandler(PackageInfo, Cleaner):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            return files
 | 
					            return files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dependencies = {dependency.package_base: dependency for dependency in self.database.dependencies_get()}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        result: list[Package] = []
 | 
					        result: list[Package] = []
 | 
				
			||||||
        for package in self.packages(filter_packages):
 | 
					        for package in self.packages(filter_packages):
 | 
				
			||||||
            if package.base not in dependencies:
 | 
					            dependencies = next(iter(self.reporter.package_dependencies_get(package.base)), None)
 | 
				
			||||||
 | 
					            if dependencies is None:
 | 
				
			||||||
                continue  # skip check if no package dependencies found
 | 
					                continue  # skip check if no package dependencies found
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            required = dependencies[package.base].paths
 | 
					            required_packages = {dep for dep_packages in dependencies.paths.values() for dep in dep_packages}
 | 
				
			||||||
            required_packages = {dep for dep_packages in required.values() for dep in dep_packages}
 | 
					 | 
				
			||||||
            filesystem = extract_files(required_packages)
 | 
					            filesystem = extract_files(required_packages)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for path, packages in required.items():
 | 
					            for path, packages in dependencies.paths.items():
 | 
				
			||||||
                found = filesystem.get(path, set())
 | 
					                found = filesystem.get(path, set())
 | 
				
			||||||
                if found.intersection(packages):
 | 
					                if found.intersection(packages):
 | 
				
			||||||
                    continue
 | 
					                    continue
 | 
				
			||||||
 | 
				
			|||||||
@ -17,16 +17,18 @@
 | 
				
			|||||||
# You should have received a copy of the GNU General Public License
 | 
					# You should have received a copy of the GNU General Public License
 | 
				
			||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
					# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					# pylint: disable=too-many-public-methods
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from ahriman.core.configuration import Configuration
 | 
					from ahriman.core.configuration import Configuration
 | 
				
			||||||
 | 
					from ahriman.core.database import SQLite
 | 
				
			||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
					from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
				
			||||||
from ahriman.models.changes import Changes
 | 
					from ahriman.models.changes import Changes
 | 
				
			||||||
 | 
					from ahriman.models.dependencies import Dependencies
 | 
				
			||||||
from ahriman.models.internal_status import InternalStatus
 | 
					from ahriman.models.internal_status import InternalStatus
 | 
				
			||||||
from ahriman.models.log_record_id import LogRecordId
 | 
					from ahriman.models.log_record_id import LogRecordId
 | 
				
			||||||
from ahriman.models.package import Package
 | 
					from ahriman.models.package import Package
 | 
				
			||||||
 | 
					from ahriman.models.pkgbuild_patch import PkgbuildPatch
 | 
				
			||||||
from ahriman.models.repository_id import RepositoryId
 | 
					from ahriman.models.repository_id import RepositoryId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -36,22 +38,31 @@ class Client:
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def load(repository_id: RepositoryId, configuration: Configuration, *, report: bool) -> Client:
 | 
					    def load(repository_id: RepositoryId, configuration: Configuration, database: SQLite | None = None, *,
 | 
				
			||||||
 | 
					             report: bool = True) -> Client:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        load client from settings
 | 
					        load client from settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            repository_id(RepositoryId): repository unique identifier
 | 
					            repository_id(RepositoryId): repository unique identifier
 | 
				
			||||||
            configuration(Configuration): configuration instance
 | 
					            configuration(Configuration): configuration instance
 | 
				
			||||||
            report(bool): force enable or disable reporting
 | 
					            database(SQLite | None, optional): database instance (Default value = None)
 | 
				
			||||||
 | 
					            report(bool, optional): force enable or disable reporting (Default value = True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Returns:
 | 
					        Returns:
 | 
				
			||||||
            Client: client according to current settings
 | 
					            Client: client according to current settings
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					        def make_local_client() -> Client:
 | 
				
			||||||
 | 
					            if database is None:
 | 
				
			||||||
 | 
					                return Client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            from ahriman.core.status.local_client import LocalClient
 | 
				
			||||||
 | 
					            return LocalClient(repository_id, database)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not report:
 | 
					        if not report:
 | 
				
			||||||
            return Client()
 | 
					            return make_local_client()
 | 
				
			||||||
        if not configuration.getboolean("status", "enabled", fallback=True):  # global switch
 | 
					        if not configuration.getboolean("status", "enabled", fallback=True):  # global switch
 | 
				
			||||||
            return Client()
 | 
					            return make_local_client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # new-style section
 | 
					        # new-style section
 | 
				
			||||||
        address = configuration.get("status", "address", fallback=None)
 | 
					        address = configuration.get("status", "address", fallback=None)
 | 
				
			||||||
@ -65,7 +76,8 @@ class Client:
 | 
				
			|||||||
        if address or legacy_address or (host and port) or socket:
 | 
					        if address or legacy_address or (host and port) or socket:
 | 
				
			||||||
            from ahriman.core.status.web_client import WebClient
 | 
					            from ahriman.core.status.web_client import WebClient
 | 
				
			||||||
            return WebClient(repository_id, configuration)
 | 
					            return WebClient(repository_id, configuration)
 | 
				
			||||||
        return Client()
 | 
					
 | 
				
			||||||
 | 
					        return make_local_client()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_add(self, package: Package, status: BuildStatusEnum) -> None:
 | 
					    def package_add(self, package: Package, status: BuildStatusEnum) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -74,7 +86,11 @@ class Client:
 | 
				
			|||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package(Package): package properties
 | 
					            package(Package): package properties
 | 
				
			||||||
            status(BuildStatusEnum): current package build status
 | 
					            status(BuildStatusEnum): current package build status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_changes_get(self, package_base: str) -> Changes:
 | 
					    def package_changes_get(self, package_base: str) -> Changes:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -85,9 +101,11 @@ class Client:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Returns:
 | 
					        Returns:
 | 
				
			||||||
            Changes: package changes if available and empty object otherwise
 | 
					            Changes: package changes if available and empty object otherwise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        del package_base
 | 
					        raise NotImplementedError
 | 
				
			||||||
        return Changes()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_changes_set(self, package_base: str, changes: Changes) -> None:
 | 
					    def package_changes_set(self, package_base: str, changes: Changes) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -96,7 +114,38 @@ class Client:
 | 
				
			|||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package_base(str): package base to update
 | 
					            package_base(str): package base to update
 | 
				
			||||||
            changes(Changes): changes descriptor
 | 
					            changes(Changes): changes descriptor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_dependencies_get(self, package_base: str) -> list[Dependencies]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to retrieve
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[Dependencies]: package implicit dependencies if available
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_dependencies_set(self, dependencies: Dependencies) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        update package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            dependencies(Dependencies): dependencies descriptor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
 | 
					    def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -107,35 +156,118 @@ class Client:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Returns:
 | 
					        Returns:
 | 
				
			||||||
            list[tuple[Package, BuildStatus]]: list of current package description and status if it has been found
 | 
					            list[tuple[Package, BuildStatus]]: list of current package description and status if it has been found
 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        del package_base
 | 
					 | 
				
			||||||
        return []
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_logs(self, log_record_id: LogRecordId, record: logging.LogRecord) -> None:
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_logs_add(self, log_record_id: LogRecordId, created: float, message: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        post log record
 | 
					        post log record
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            log_record_id(LogRecordId): log record id
 | 
					            log_record_id(LogRecordId): log record id
 | 
				
			||||||
            record(logging.LogRecord): log record to post to api
 | 
					            created(float): log created timestamp
 | 
				
			||||||
 | 
					            message(str): log message
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					            limit(int, optional): limit records to the specified count, -1 means unlimited (Default value = -1)
 | 
				
			||||||
 | 
					            offset(int, optional): records offset (Default value = 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[tuple[float, str]]: package logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_logs_remove(self, package_base: str, version: str | None) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        remove package logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					            version(str | None): package version to remove logs. If None set, all logs will be removed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_patches_add(self, package_base: str, patch: PkgbuildPatch) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        create or update package patch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to update
 | 
				
			||||||
 | 
					            patch(PkgbuildPatch): package patch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_patches_get(self, package_base: str, variable: str | None) -> list[PkgbuildPatch]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package patches
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to retrieve
 | 
				
			||||||
 | 
					            variable(str | None): optional filter by patch variable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[PkgbuildPatch]: list of patches for the specified package
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_patches_remove(self, package_base: str, variable: str | None) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        remove package patch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to update
 | 
				
			||||||
 | 
					            variable(str | None): patch name. If None set, all patches will be removed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_remove(self, package_base: str) -> None:
 | 
					    def package_remove(self, package_base: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        remove packages from watcher
 | 
					        remove packages from watcher
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package_base(str): package base to remove
 | 
					            package_base(str): package base to remove
 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_update(self, package_base: str, status: BuildStatusEnum) -> None:
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_set(self, package_base: str, status: BuildStatusEnum) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        update package build status. Unlike :func:`package_add()` it does not update package properties
 | 
					        update package build status. Unlike :func:`package_add()` it does not update package properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package_base(str): package base to update
 | 
					            package_base(str): package base to update
 | 
				
			||||||
            status(BuildStatusEnum): current package build status
 | 
					            status(BuildStatusEnum): current package build status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            NotImplementedError: not implemented method
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def set_building(self, package_base: str) -> None:
 | 
					    def set_building(self, package_base: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -144,7 +276,7 @@ class Client:
 | 
				
			|||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package_base(str): package base to update
 | 
					            package_base(str): package base to update
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return self.package_update(package_base, BuildStatusEnum.Building)
 | 
					        return self.package_set(package_base, BuildStatusEnum.Building)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def set_failed(self, package_base: str) -> None:
 | 
					    def set_failed(self, package_base: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -153,7 +285,7 @@ class Client:
 | 
				
			|||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package_base(str): package base to update
 | 
					            package_base(str): package base to update
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return self.package_update(package_base, BuildStatusEnum.Failed)
 | 
					        return self.package_set(package_base, BuildStatusEnum.Failed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def set_pending(self, package_base: str) -> None:
 | 
					    def set_pending(self, package_base: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -162,7 +294,7 @@ class Client:
 | 
				
			|||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package_base(str): package base to update
 | 
					            package_base(str): package base to update
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return self.package_update(package_base, BuildStatusEnum.Pending)
 | 
					        return self.package_set(package_base, BuildStatusEnum.Pending)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def set_success(self, package: Package) -> None:
 | 
					    def set_success(self, package: Package) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										207
									
								
								src/ahriman/core/status/local_client.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								src/ahriman/core/status/local_client.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,207 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2024 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 ahriman.core.database import SQLite
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
 | 
					from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
				
			||||||
 | 
					from ahriman.models.changes import Changes
 | 
				
			||||||
 | 
					from ahriman.models.dependencies import Dependencies
 | 
				
			||||||
 | 
					from ahriman.models.log_record_id import LogRecordId
 | 
				
			||||||
 | 
					from ahriman.models.package import Package
 | 
				
			||||||
 | 
					from ahriman.models.pkgbuild_patch import PkgbuildPatch
 | 
				
			||||||
 | 
					from ahriman.models.repository_id import RepositoryId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LocalClient(Client):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    local database handler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Attributes:
 | 
				
			||||||
 | 
					        database(SQLite): database instance
 | 
				
			||||||
 | 
					        repository_id(RepositoryId): repository unique identifier
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, repository_id: RepositoryId, database: SQLite) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        default constructor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            repository_id(RepositoryId): repository unique identifier
 | 
				
			||||||
 | 
					            database(SQLite): database instance:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.database = database
 | 
				
			||||||
 | 
					        self.repository_id = repository_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_add(self, package: Package, status: BuildStatusEnum) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        add new package with status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package(Package): package properties
 | 
				
			||||||
 | 
					            status(BuildStatusEnum): current package build status
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.database.package_update(package, self.repository_id)
 | 
				
			||||||
 | 
					        self.database.status_update(package.base, BuildStatus(status), self.repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_changes_get(self, package_base: str) -> Changes:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to retrieve
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            Changes: package changes if available and empty object otherwise
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self.database.changes_get(package_base, self.repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_changes_set(self, package_base: str, changes: Changes) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        update package changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to update
 | 
				
			||||||
 | 
					            changes(Changes): changes descriptor
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.database.changes_insert(package_base, changes, self.repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_dependencies_get(self, package_base: str) -> list[Dependencies]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to retrieve
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[Dependencies]: package implicit dependencies if available
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self.database.dependencies_get(package_base, self.repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_dependencies_set(self, dependencies: Dependencies) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        update package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            dependencies(Dependencies): dependencies descriptor
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.database.dependencies_insert(dependencies, self.repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str | None): package base to get
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[tuple[Package, BuildStatus]]: list of current package description and status if it has been found
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        packages = self.database.packages_get(self.repository_id)
 | 
				
			||||||
 | 
					        if package_base is None:
 | 
				
			||||||
 | 
					            return packages
 | 
				
			||||||
 | 
					        return [(package, status) for package, status in packages if package.base == package_base]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_logs_add(self, log_record_id: LogRecordId, created: float, message: str) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        post log record
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            log_record_id(LogRecordId): log record id
 | 
				
			||||||
 | 
					            created(float): log created timestamp
 | 
				
			||||||
 | 
					            message(str): log message
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.database.logs_insert(log_record_id, created, message, self.repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					            limit(int, optional): limit records to the specified count, -1 means unlimited (Default value = -1)
 | 
				
			||||||
 | 
					            offset(int, optional): records offset (Default value = 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[tuple[float, str]]: package logs
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self.database.logs_get(package_base, limit, offset, self.repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_logs_remove(self, package_base: str, version: str | None) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        remove package logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					            version(str | None): package version to remove logs. If None set, all logs will be removed
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.database.logs_remove(package_base, version, self.repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_patches_add(self, package_base: str, patch: PkgbuildPatch) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        create or update package patch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to update
 | 
				
			||||||
 | 
					            patch(PkgbuildPatch): package patch
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.database.patches_insert(package_base, [patch])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_patches_get(self, package_base: str, variable: str | None) -> list[PkgbuildPatch]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package patches
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to retrieve
 | 
				
			||||||
 | 
					            variable(str | None): optional filter by patch variable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[PkgbuildPatch]: list of patches for the specified package
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        variables = [variable] if variable is not None else None
 | 
				
			||||||
 | 
					        return self.database.patches_list(package_base, variables).get(package_base, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_patches_remove(self, package_base: str, variable: str | None) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        remove package patch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to update
 | 
				
			||||||
 | 
					            variable(str | None): patch name. If None set, all patches will be removed
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        variables = [variable] if variable is not None else None
 | 
				
			||||||
 | 
					        self.database.patches_remove(package_base, variables)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_remove(self, package_base: str) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        remove packages from watcher
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to remove
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.database.package_clear(package_base)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_set(self, package_base: str, status: BuildStatusEnum) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        update package build status. Unlike :func:`package_add()` it does not update package properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to update
 | 
				
			||||||
 | 
					            status(BuildStatusEnum): current package build status
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.database.status_update(package_base, BuildStatus(status), self.repository_id)
 | 
				
			||||||
@ -19,15 +19,15 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
from threading import Lock
 | 
					from threading import Lock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ahriman.core.database import SQLite
 | 
					 | 
				
			||||||
from ahriman.core.exceptions import UnknownPackageError
 | 
					from ahriman.core.exceptions import UnknownPackageError
 | 
				
			||||||
from ahriman.core.log import LazyLogging
 | 
					from ahriman.core.log import LazyLogging
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
					from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
				
			||||||
from ahriman.models.changes import Changes
 | 
					from ahriman.models.changes import Changes
 | 
				
			||||||
 | 
					from ahriman.models.dependencies import Dependencies
 | 
				
			||||||
from ahriman.models.log_record_id import LogRecordId
 | 
					from ahriman.models.log_record_id import LogRecordId
 | 
				
			||||||
from ahriman.models.package import Package
 | 
					from ahriman.models.package import Package
 | 
				
			||||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
 | 
					from ahriman.models.pkgbuild_patch import PkgbuildPatch
 | 
				
			||||||
from ahriman.models.repository_id import RepositoryId
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Watcher(LazyLogging):
 | 
					class Watcher(LazyLogging):
 | 
				
			||||||
@ -35,21 +35,18 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
    package status watcher
 | 
					    package status watcher
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Attributes:
 | 
					    Attributes:
 | 
				
			||||||
        database(SQLite): database instance
 | 
					        client(Client): reporter instance
 | 
				
			||||||
        repository_id(RepositoryId): repository unique identifier
 | 
					 | 
				
			||||||
        status(BuildStatus): daemon status
 | 
					        status(BuildStatus): daemon status
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, repository_id: RepositoryId, database: SQLite) -> None:
 | 
					    def __init__(self, client: Client) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        default constructor
 | 
					        default constructor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            repository_id(RepositoryId): repository unique identifier
 | 
					            client(Client): reporter instance
 | 
				
			||||||
            database(SQLite): database instance
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.repository_id = repository_id
 | 
					        self.client = client
 | 
				
			||||||
        self.database = database
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._lock = Lock()
 | 
					        self._lock = Lock()
 | 
				
			||||||
        self._known: dict[str, tuple[Package, BuildStatus]] = {}
 | 
					        self._known: dict[str, tuple[Package, BuildStatus]] = {}
 | 
				
			||||||
@ -76,7 +73,7 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            self._known = {
 | 
					            self._known = {
 | 
				
			||||||
                package.base: (package, status)
 | 
					                package.base: (package, status)
 | 
				
			||||||
                for package, status in self.database.packages_get(self.repository_id)
 | 
					                for package, status in self.client.package_get(None)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]:
 | 
					    def logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]:
 | 
				
			||||||
@ -91,8 +88,8 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
        Returns:
 | 
					        Returns:
 | 
				
			||||||
            list[tuple[float, str]]: package logs
 | 
					            list[tuple[float, str]]: package logs
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.package_get(package_base)
 | 
					        _ = self.package_get(package_base)
 | 
				
			||||||
        return self.database.logs_get(package_base, limit, offset, self.repository_id)
 | 
					        return self.client.package_logs_get(package_base, limit, offset)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def logs_remove(self, package_base: str, version: str | None) -> None:
 | 
					    def logs_remove(self, package_base: str, version: str | None) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -100,24 +97,24 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package_base(str): package base
 | 
					            package_base(str): package base
 | 
				
			||||||
            version(str): package versio
 | 
					            version(str): package version
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.database.logs_remove(package_base, version, self.repository_id)
 | 
					        self.client.package_logs_remove(package_base, version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def logs_update(self, log_record_id: LogRecordId, created: float, record: str) -> None:
 | 
					    def logs_update(self, log_record_id: LogRecordId, created: float, message: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        make new log record into database
 | 
					        make new log record into database
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            log_record_id(LogRecordId): log record id
 | 
					            log_record_id(LogRecordId): log record id
 | 
				
			||||||
            created(float): log created timestamp
 | 
					            created(float): log created timestamp
 | 
				
			||||||
            record(str): log record
 | 
					            message(str): log message
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if self._last_log_record_id != log_record_id:
 | 
					        if self._last_log_record_id != log_record_id:
 | 
				
			||||||
            # there is new log record, so we remove old ones
 | 
					            # there is new log record, so we remove old ones
 | 
				
			||||||
            self.logs_remove(log_record_id.package_base, log_record_id.version)
 | 
					            self.logs_remove(log_record_id.package_base, log_record_id.version)
 | 
				
			||||||
        self._last_log_record_id = log_record_id
 | 
					        self._last_log_record_id = log_record_id
 | 
				
			||||||
        self.database.logs_insert(log_record_id, created, record, self.repository_id)
 | 
					        self.client.package_logs_add(log_record_id, created, message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_changes_get(self, package_base: str) -> Changes:
 | 
					    def package_changes_get(self, package_base: str) -> Changes:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -129,8 +126,35 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
        Returns:
 | 
					        Returns:
 | 
				
			||||||
            Changes: package changes if available
 | 
					            Changes: package changes if available
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.package_get(package_base)
 | 
					        _ = self.package_get(package_base)
 | 
				
			||||||
        return self.database.changes_get(package_base, self.repository_id)
 | 
					        return self.client.package_changes_get(package_base)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_dependencies_get(self, package_base: str) -> Dependencies:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        retrieve package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            Dependencies: package dependencies if available
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        _ = self.package_get(package_base)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return next(iter(self.client.package_dependencies_get(package_base)))
 | 
				
			||||||
 | 
					        except StopIteration:
 | 
				
			||||||
 | 
					            return Dependencies(package_base)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_dependencies_set(self, package_base: str, dependencies: Dependencies) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        update package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					            dependencies(Dependencies): package dependencies
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        _ = self.package_get(package_base)
 | 
				
			||||||
 | 
					        self.client.package_dependencies_set(dependencies)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_get(self, package_base: str) -> tuple[Package, BuildStatus]:
 | 
					    def package_get(self, package_base: str) -> tuple[Package, BuildStatus]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -160,7 +184,7 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            self._known.pop(package_base, None)
 | 
					            self._known.pop(package_base, None)
 | 
				
			||||||
        self.database.package_remove(package_base, self.repository_id)
 | 
					        self.client.package_remove(package_base)
 | 
				
			||||||
        self.logs_remove(package_base, None)
 | 
					        self.logs_remove(package_base, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_update(self, package_base: str, status: BuildStatusEnum, package: Package | None) -> None:
 | 
					    def package_update(self, package_base: str, status: BuildStatusEnum, package: Package | None) -> None:
 | 
				
			||||||
@ -174,10 +198,9 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        if package is None:
 | 
					        if package is None:
 | 
				
			||||||
            package, _ = self.package_get(package_base)
 | 
					            package, _ = self.package_get(package_base)
 | 
				
			||||||
        full_status = BuildStatus(status)
 | 
					 | 
				
			||||||
        with self._lock:
 | 
					        with self._lock:
 | 
				
			||||||
            self._known[package_base] = (package, full_status)
 | 
					            self._known[package_base] = (package, BuildStatus(status))
 | 
				
			||||||
        self.database.package_update(package, full_status, self.repository_id)
 | 
					        self.client.package_set(package_base, status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def patches_get(self, package_base: str, variable: str | None) -> list[PkgbuildPatch]:
 | 
					    def patches_get(self, package_base: str, variable: str | None) -> list[PkgbuildPatch]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -192,8 +215,7 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        # patches are package base based, we don't know (and don't differentiate) to which package does them belong
 | 
					        # patches are package base based, we don't know (and don't differentiate) to which package does them belong
 | 
				
			||||||
        # so here we skip checking if package exists or not
 | 
					        # so here we skip checking if package exists or not
 | 
				
			||||||
        variables = [variable] if variable is not None else None
 | 
					        return self.client.package_patches_get(package_base, variable)
 | 
				
			||||||
        return self.database.patches_list(package_base, variables).get(package_base, [])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def patches_remove(self, package_base: str, variable: str) -> None:
 | 
					    def patches_remove(self, package_base: str, variable: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -203,7 +225,7 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
            package_base(str): package base
 | 
					            package_base(str): package base
 | 
				
			||||||
            variable(str): patch variable name
 | 
					            variable(str): patch variable name
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.database.patches_remove(package_base, [variable])
 | 
					        self.client.package_patches_remove(package_base, variable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def patches_update(self, package_base: str, patch: PkgbuildPatch) -> None:
 | 
					    def patches_update(self, package_base: str, patch: PkgbuildPatch) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -213,7 +235,7 @@ class Watcher(LazyLogging):
 | 
				
			|||||||
            package_base(str): package base
 | 
					            package_base(str): package base
 | 
				
			||||||
            patch(PkgbuildPatch): package patch
 | 
					            patch(PkgbuildPatch): package patch
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        self.database.patches_insert(package_base, [patch])
 | 
					        self.client.package_patches_add(package_base, patch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def status_update(self, status: BuildStatusEnum) -> None:
 | 
					    def status_update(self, status: BuildStatusEnum) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,6 @@
 | 
				
			|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
					# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
import contextlib
 | 
					import contextlib
 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from urllib.parse import quote_plus as urlencode
 | 
					from urllib.parse import quote_plus as urlencode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -27,9 +26,11 @@ from ahriman.core.http import SyncAhrimanClient
 | 
				
			|||||||
from ahriman.core.status.client import Client
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
					from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
				
			||||||
from ahriman.models.changes import Changes
 | 
					from ahriman.models.changes import Changes
 | 
				
			||||||
 | 
					from ahriman.models.dependencies import Dependencies
 | 
				
			||||||
from ahriman.models.internal_status import InternalStatus
 | 
					from ahriman.models.internal_status import InternalStatus
 | 
				
			||||||
from ahriman.models.log_record_id import LogRecordId
 | 
					from ahriman.models.log_record_id import LogRecordId
 | 
				
			||||||
from ahriman.models.package import Package
 | 
					from ahriman.models.package import Package
 | 
				
			||||||
 | 
					from ahriman.models.pkgbuild_patch import PkgbuildPatch
 | 
				
			||||||
from ahriman.models.repository_id import RepositoryId
 | 
					from ahriman.models.repository_id import RepositoryId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,10 +93,22 @@ class WebClient(Client, SyncAhrimanClient):
 | 
				
			|||||||
            package_base(str): package base
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Returns:
 | 
					        Returns:
 | 
				
			||||||
            str: full url for web service for logs
 | 
					            str: full url for web service for changes
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return f"{self.address}/api/v1/packages/{urlencode(package_base)}/changes"
 | 
					        return f"{self.address}/api/v1/packages/{urlencode(package_base)}/changes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _dependencies_url(self, package_base: str) -> str:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get url for the dependencies api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            str: full url for web service for dependencies
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return f"{self.address}/api/v1/packages/{urlencode(package_base)}/dependencies"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _logs_url(self, package_base: str) -> str:
 | 
					    def _logs_url(self, package_base: str) -> str:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        get url for the logs api
 | 
					        get url for the logs api
 | 
				
			||||||
@ -110,7 +123,7 @@ class WebClient(Client, SyncAhrimanClient):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def _package_url(self, package_base: str = "") -> str:
 | 
					    def _package_url(self, package_base: str = "") -> str:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        url generator
 | 
					        package url generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            package_base(str, optional): package base to generate url (Default value = "")
 | 
					            package_base(str, optional): package base to generate url (Default value = "")
 | 
				
			||||||
@ -121,6 +134,20 @@ class WebClient(Client, SyncAhrimanClient):
 | 
				
			|||||||
        suffix = f"/{urlencode(package_base)}" if package_base else ""
 | 
					        suffix = f"/{urlencode(package_base)}" if package_base else ""
 | 
				
			||||||
        return f"{self.address}/api/v1/packages{suffix}"
 | 
					        return f"{self.address}/api/v1/packages{suffix}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _patches_url(self, package_base: str, variable: str = "") -> str:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        patches url generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					            variable(str, optional): patch variable name to generate url (Default value = "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            str: full url of web service for the package patch
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        suffix = f"/{urlencode(variable)}" if variable else ""
 | 
				
			||||||
 | 
					        return f"{self.address}/api/v1/packages/{urlencode(package_base)}/patches{suffix}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _status_url(self) -> str:
 | 
					    def _status_url(self) -> str:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        get url for the status api
 | 
					        get url for the status api
 | 
				
			||||||
@ -177,6 +204,37 @@ class WebClient(Client, SyncAhrimanClient):
 | 
				
			|||||||
            self.make_request("POST", self._changes_url(package_base),
 | 
					            self.make_request("POST", self._changes_url(package_base),
 | 
				
			||||||
                              params=self.repository_id.query(), json=changes.view())
 | 
					                              params=self.repository_id.query(), json=changes.view())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_dependencies_get(self, package_base: str) -> list[Dependencies]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to retrieve
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[Dependencies]: package implicit dependencies if available
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        with contextlib.suppress(Exception):
 | 
				
			||||||
 | 
					            response = self.make_request("GET", self._dependencies_url(package_base),
 | 
				
			||||||
 | 
					                                         params=self.repository_id.query())
 | 
				
			||||||
 | 
					            response_json = response.json()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            dependencies = response_json if package_base is None else [response_json]
 | 
				
			||||||
 | 
					            return [Dependencies.from_json(dependencies) for dependencies in dependencies]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_dependencies_set(self, dependencies: Dependencies) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        update package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            dependencies(Dependencies): dependencies descriptor
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        with contextlib.suppress(Exception):
 | 
				
			||||||
 | 
					            self.make_request("POST", self._dependencies_url(dependencies.package_base),
 | 
				
			||||||
 | 
					                              params=self.repository_id.query(), json=dependencies.view())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
 | 
					    def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        get package status
 | 
					        get package status
 | 
				
			||||||
@ -199,17 +257,18 @@ class WebClient(Client, SyncAhrimanClient):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return []
 | 
					        return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_logs(self, log_record_id: LogRecordId, record: logging.LogRecord) -> None:
 | 
					    def package_logs_add(self, log_record_id: LogRecordId, created: float, message: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        post log record
 | 
					        post log record
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Args:
 | 
					        Args:
 | 
				
			||||||
            log_record_id(LogRecordId): log record id
 | 
					            log_record_id(LogRecordId): log record id
 | 
				
			||||||
            record(logging.LogRecord): log record to post to api
 | 
					            created(float): log created timestamp
 | 
				
			||||||
 | 
					            message(str): log message
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        payload = {
 | 
					        payload = {
 | 
				
			||||||
            "created": record.created,
 | 
					            "created": created,
 | 
				
			||||||
            "message": record.getMessage(),
 | 
					            "message": message,
 | 
				
			||||||
            "version": log_record_id.version,
 | 
					            "version": log_record_id.version,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -219,6 +278,83 @@ class WebClient(Client, SyncAhrimanClient):
 | 
				
			|||||||
        self.make_request("POST", self._logs_url(log_record_id.package_base),
 | 
					        self.make_request("POST", self._logs_url(log_record_id.package_base),
 | 
				
			||||||
                          params=self.repository_id.query(), json=payload, suppress_errors=True)
 | 
					                          params=self.repository_id.query(), json=payload, suppress_errors=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					            limit(int, optional): limit records to the specified count, -1 means unlimited (Default value = -1)
 | 
				
			||||||
 | 
					            offset(int, optional): records offset (Default value = 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[tuple[float, str]]: package logs
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        with contextlib.suppress(Exception):
 | 
				
			||||||
 | 
					            query = self.repository_id.query() + [("limit", str(limit)), ("offset", str(offset))]
 | 
				
			||||||
 | 
					            response = self.make_request("GET", self._logs_url(package_base), params=query)
 | 
				
			||||||
 | 
					            response_json = response.json()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return [(record["created"], record["message"]) for record in response_json]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_logs_remove(self, package_base: str, version: str | None) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        remove package logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base
 | 
				
			||||||
 | 
					            version(str | None): package version to remove logs. If None set, all logs will be removed
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        with contextlib.suppress(Exception):
 | 
				
			||||||
 | 
					            query = self.repository_id.query()
 | 
				
			||||||
 | 
					            if version is not None:
 | 
				
			||||||
 | 
					                query += [("version", version)]
 | 
				
			||||||
 | 
					            self.make_request("DELETE", self._logs_url(package_base), params=query)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_patches_add(self, package_base: str, patch: PkgbuildPatch) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        create or update package patch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to update
 | 
				
			||||||
 | 
					            patch(PkgbuildPatch): package patch
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        with contextlib.suppress(Exception):
 | 
				
			||||||
 | 
					            self.make_request("POST", self._patches_url(package_base), json=patch.view())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_patches_get(self, package_base: str, variable: str | None) -> list[PkgbuildPatch]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package patches
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to retrieve
 | 
				
			||||||
 | 
					            variable(str | None): optional filter by patch variable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            list[PkgbuildPatch]: list of patches for the specified package
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        with contextlib.suppress(Exception):
 | 
				
			||||||
 | 
					            response = self.make_request("GET", self._patches_url(package_base, variable or ""))
 | 
				
			||||||
 | 
					            response_json = response.json()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            patches = response_json if variable is None else [response_json]
 | 
				
			||||||
 | 
					            return [PkgbuildPatch.from_json(patch) for patch in patches]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_patches_remove(self, package_base: str, variable: str | None) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        remove package patch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            package_base(str): package base to update
 | 
				
			||||||
 | 
					            variable(str | None): patch name. If None set, all patches will be removed
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        with contextlib.suppress(Exception):
 | 
				
			||||||
 | 
					            self.make_request("DELETE", self._patches_url(package_base, variable or ""))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_remove(self, package_base: str) -> None:
 | 
					    def package_remove(self, package_base: str) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        remove packages from watcher
 | 
					        remove packages from watcher
 | 
				
			||||||
@ -229,7 +365,7 @@ class WebClient(Client, SyncAhrimanClient):
 | 
				
			|||||||
        with contextlib.suppress(Exception):
 | 
					        with contextlib.suppress(Exception):
 | 
				
			||||||
            self.make_request("DELETE", self._package_url(package_base), params=self.repository_id.query())
 | 
					            self.make_request("DELETE", self._package_url(package_base), params=self.repository_id.query())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def package_update(self, package_base: str, status: BuildStatusEnum) -> None:
 | 
					    def package_set(self, package_base: str, status: BuildStatusEnum) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        update package build status. Unlike :func:`package_add()` it does not update package properties
 | 
					        update package build status. Unlike :func:`package_add()` it does not update package properties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -18,13 +18,13 @@
 | 
				
			|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
					# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
import shutil
 | 
					import shutil
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ahriman.core import context
 | 
					from ahriman.core import context
 | 
				
			||||||
from ahriman.core.build_tools.sources import Sources
 | 
					from ahriman.core.build_tools.sources import Sources
 | 
				
			||||||
from ahriman.core.configuration import Configuration
 | 
					from ahriman.core.configuration import Configuration
 | 
				
			||||||
from ahriman.core.database import SQLite
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.core.support.pkgbuild.pkgbuild_generator import PkgbuildGenerator
 | 
					from ahriman.core.support.pkgbuild.pkgbuild_generator import PkgbuildGenerator
 | 
				
			||||||
from ahriman.models.build_status import BuildStatus
 | 
					 | 
				
			||||||
from ahriman.models.package import Package
 | 
					from ahriman.models.package import Package
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -48,23 +48,39 @@ class PackageCreator:
 | 
				
			|||||||
        self.configuration = configuration
 | 
					        self.configuration = configuration
 | 
				
			||||||
        self.generator = generator
 | 
					        self.generator = generator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_create(self, path: Path) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        create package files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            path(Path): path to directory with package files
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # clear old tree if any
 | 
				
			||||||
 | 
					        shutil.rmtree(path, ignore_errors=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # create local tree
 | 
				
			||||||
 | 
					        path.mkdir(mode=0o755, parents=True, exist_ok=True)
 | 
				
			||||||
 | 
					        self.generator.write_pkgbuild(path)
 | 
				
			||||||
 | 
					        Sources.init(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def package_register(self, path: Path) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        register package in build worker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            path(Path): path to directory with package files
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        ctx = context.get()
 | 
				
			||||||
 | 
					        reporter = ctx.get(Client)
 | 
				
			||||||
 | 
					        _, repository_id = self.configuration.check_loaded()
 | 
				
			||||||
 | 
					        package = Package.from_build(path, repository_id.architecture, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reporter.set_unknown(package)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run(self) -> None:
 | 
					    def run(self) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        create new local package
 | 
					        create new local package
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        local_path = self.configuration.repository_paths.cache_for(self.generator.pkgname)
 | 
					        local_path = self.configuration.repository_paths.cache_for(self.generator.pkgname)
 | 
				
			||||||
 | 
					        self.package_create(local_path)
 | 
				
			||||||
        # clear old tree if any
 | 
					        self.package_register(local_path)
 | 
				
			||||||
        shutil.rmtree(local_path, ignore_errors=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # create local tree
 | 
					 | 
				
			||||||
        local_path.mkdir(mode=0o755, parents=True, exist_ok=True)
 | 
					 | 
				
			||||||
        self.generator.write_pkgbuild(local_path)
 | 
					 | 
				
			||||||
        Sources.init(local_path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # register package
 | 
					 | 
				
			||||||
        ctx = context.get()
 | 
					 | 
				
			||||||
        database = ctx.get(SQLite)
 | 
					 | 
				
			||||||
        _, repository_id = self.configuration.check_loaded()
 | 
					 | 
				
			||||||
        package = Package.from_build(local_path, repository_id.architecture, None)
 | 
					 | 
				
			||||||
        database.package_update(package, BuildStatus())
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -17,8 +17,11 @@
 | 
				
			|||||||
# You should have received a copy of the GNU General Public License
 | 
					# You should have received a copy of the GNU General Public License
 | 
				
			||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
					# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
from dataclasses import dataclass, field
 | 
					from dataclasses import dataclass, field, fields
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					from typing import Any, Self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ahriman.core.util import dataclass_view, filter_json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@dataclass(frozen=True)
 | 
					@dataclass(frozen=True)
 | 
				
			||||||
@ -33,3 +36,27 @@ class Dependencies:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    package_base: str
 | 
					    package_base: str
 | 
				
			||||||
    paths: dict[Path, list[str]] = field(default_factory=dict)
 | 
					    paths: dict[Path, list[str]] = field(default_factory=dict)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def from_json(cls, dump: dict[str, Any]) -> Self:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        construct dependencies from the json dump
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            dump(dict[str, Any]): json dump body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            Self: dependencies object
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # filter to only known fields
 | 
				
			||||||
 | 
					        known_fields = [pair.name for pair in fields(cls)]
 | 
				
			||||||
 | 
					        return cls(**filter_json(dump, known_fields))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def view(self) -> dict[str, Any]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        generate json dependencies view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            dict[str, Any]: json-friendly dictionary
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return dataclass_view(self)
 | 
				
			||||||
 | 
				
			|||||||
@ -19,11 +19,11 @@
 | 
				
			|||||||
#
 | 
					#
 | 
				
			||||||
import shlex
 | 
					import shlex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from dataclasses import dataclass
 | 
					from dataclasses import dataclass, fields
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from typing import Any, Generator, Self
 | 
					from typing import Any, Generator, Self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ahriman.core.util import dataclass_view
 | 
					from ahriman.core.util import dataclass_view, filter_json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@dataclass(frozen=True)
 | 
					@dataclass(frozen=True)
 | 
				
			||||||
@ -84,6 +84,21 @@ class PkgbuildPatch:
 | 
				
			|||||||
        raw_value = next(iter(value_parts), "")  # extract raw value
 | 
					        raw_value = next(iter(value_parts), "")  # extract raw value
 | 
				
			||||||
        return cls(key, cls.parse(raw_value))
 | 
					        return cls(key, cls.parse(raw_value))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def from_json(cls, dump: dict[str, Any]) -> Self:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        construct patch descriptor from the json dump
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Args:
 | 
				
			||||||
 | 
					            dump(dict[str, Any]): json dump body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            Self: patch object
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        # filter to only known fields
 | 
				
			||||||
 | 
					        known_fields = [pair.name for pair in fields(cls)]
 | 
				
			||||||
 | 
					        return cls(**filter_json(dump, known_fields))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def parse(source: str) -> str | list[str]:
 | 
					    def parse(source: str) -> str | list[str]:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
				
			|||||||
@ -22,6 +22,7 @@ from ahriman.web.schemas.auth_schema import AuthSchema
 | 
				
			|||||||
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema
 | 
					from ahriman.web.schemas.build_options_schema import BuildOptionsSchema
 | 
				
			||||||
from ahriman.web.schemas.changes_schema import ChangesSchema
 | 
					from ahriman.web.schemas.changes_schema import ChangesSchema
 | 
				
			||||||
from ahriman.web.schemas.counters_schema import CountersSchema
 | 
					from ahriman.web.schemas.counters_schema import CountersSchema
 | 
				
			||||||
 | 
					from ahriman.web.schemas.dependencies_schema import DependenciesSchema
 | 
				
			||||||
from ahriman.web.schemas.error_schema import ErrorSchema
 | 
					from ahriman.web.schemas.error_schema import ErrorSchema
 | 
				
			||||||
from ahriman.web.schemas.file_schema import FileSchema
 | 
					from ahriman.web.schemas.file_schema import FileSchema
 | 
				
			||||||
from ahriman.web.schemas.info_schema import InfoSchema
 | 
					from ahriman.web.schemas.info_schema import InfoSchema
 | 
				
			||||||
@ -36,6 +37,7 @@ from ahriman.web.schemas.package_patch_schema import PackagePatchSchema
 | 
				
			|||||||
from ahriman.web.schemas.package_properties_schema import PackagePropertiesSchema
 | 
					from ahriman.web.schemas.package_properties_schema import PackagePropertiesSchema
 | 
				
			||||||
from ahriman.web.schemas.package_schema import PackageSchema
 | 
					from ahriman.web.schemas.package_schema import PackageSchema
 | 
				
			||||||
from ahriman.web.schemas.package_status_schema import PackageStatusSchema, PackageStatusSimplifiedSchema
 | 
					from ahriman.web.schemas.package_status_schema import PackageStatusSchema, PackageStatusSimplifiedSchema
 | 
				
			||||||
 | 
					from ahriman.web.schemas.package_version_schema import PackageVersionSchema
 | 
				
			||||||
from ahriman.web.schemas.pagination_schema import PaginationSchema
 | 
					from ahriman.web.schemas.pagination_schema import PaginationSchema
 | 
				
			||||||
from ahriman.web.schemas.patch_name_schema import PatchNameSchema
 | 
					from ahriman.web.schemas.patch_name_schema import PatchNameSchema
 | 
				
			||||||
from ahriman.web.schemas.patch_schema import PatchSchema
 | 
					from ahriman.web.schemas.patch_schema import PatchSchema
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										35
									
								
								src/ahriman/web/schemas/dependencies_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/ahriman/web/schemas/dependencies_schema.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2024 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 marshmallow import Schema, fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DependenciesSchema(Schema):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    request/response package dependencies schema
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    package_base = fields.String(metadata={
 | 
				
			||||||
 | 
					        "description": "Package base name",
 | 
				
			||||||
 | 
					        "example": "ahriman",
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    paths = fields.Dict(
 | 
				
			||||||
 | 
					        keys=fields.String(), values=fields.List(fields.String()), required=True, metadata={
 | 
				
			||||||
 | 
					            "description": "Map of filesystem paths to packages which contain this path",
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
							
								
								
									
										34
									
								
								src/ahriman/web/schemas/package_version_schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/ahriman/web/schemas/package_version_schema.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2024 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 marshmallow import fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ahriman import __version__
 | 
				
			||||||
 | 
					from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PackageVersionSchema(RepositoryIdSchema):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    request package name schema
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    version = fields.String(required=True, metadata={
 | 
				
			||||||
 | 
					        "description": "Package version",
 | 
				
			||||||
 | 
					        "example": __version__,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
@ -113,7 +113,6 @@ class ChangesView(StatusViewGuard, BaseView):
 | 
				
			|||||||
            raise HTTPBadRequest(reason=str(ex))
 | 
					            raise HTTPBadRequest(reason=str(ex))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        changes = Changes(last_commit_sha, change)
 | 
					        changes = Changes(last_commit_sha, change)
 | 
				
			||||||
        repository_id = self.repository_id()
 | 
					        self.service().client.package_changes_set(package_base, changes)
 | 
				
			||||||
        self.service(repository_id).database.changes_insert(package_base, changes, repository_id)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        raise HTTPNoContent
 | 
					        raise HTTPNoContent
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										120
									
								
								src/ahriman/web/views/v1/packages/dependency.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/ahriman/web/views/v1/packages/dependency.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,120 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (c) 2021-2024 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/>.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					import aiohttp_apispec  # type: ignore[import-untyped]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ahriman.core.exceptions import UnknownPackageError
 | 
				
			||||||
 | 
					from ahriman.models.dependencies import Dependencies
 | 
				
			||||||
 | 
					from ahriman.models.user_access import UserAccess
 | 
				
			||||||
 | 
					from ahriman.web.schemas import AuthSchema, DependenciesSchema, ErrorSchema, PackageNameSchema, RepositoryIdSchema
 | 
				
			||||||
 | 
					from ahriman.web.views.base import BaseView
 | 
				
			||||||
 | 
					from ahriman.web.views.status_view_guard import StatusViewGuard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DependencyView(StatusViewGuard, BaseView):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    package dependencies web view
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Attributes:
 | 
				
			||||||
 | 
					        GET_PERMISSION(UserAccess): (class attribute) get permissions of self
 | 
				
			||||||
 | 
					        POST_PERMISSION(UserAccess): (class attribute) post permissions of self
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GET_PERMISSION = UserAccess.Reporter
 | 
				
			||||||
 | 
					    POST_PERMISSION = UserAccess.Full
 | 
				
			||||||
 | 
					    ROUTES = ["/api/v1/packages/{package}/dependencies"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @aiohttp_apispec.docs(
 | 
				
			||||||
 | 
					        tags=["Build"],
 | 
				
			||||||
 | 
					        summary="Get package dependencies",
 | 
				
			||||||
 | 
					        description="Retrieve package implicit dependencies",
 | 
				
			||||||
 | 
					        responses={
 | 
				
			||||||
 | 
					            200: {"description": "Success response", "schema": DependenciesSchema},
 | 
				
			||||||
 | 
					            401: {"description": "Authorization required", "schema": ErrorSchema},
 | 
				
			||||||
 | 
					            403: {"description": "Access is forbidden", "schema": ErrorSchema},
 | 
				
			||||||
 | 
					            404: {"description": "Package base and/or repository are unknown", "schema": ErrorSchema},
 | 
				
			||||||
 | 
					            500: {"description": "Internal server error", "schema": ErrorSchema},
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        security=[{"token": [GET_PERMISSION]}],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    @aiohttp_apispec.cookies_schema(AuthSchema)
 | 
				
			||||||
 | 
					    @aiohttp_apispec.match_info_schema(PackageNameSchema)
 | 
				
			||||||
 | 
					    @aiohttp_apispec.querystring_schema(RepositoryIdSchema)
 | 
				
			||||||
 | 
					    async def get(self) -> Response:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        get package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Returns:
 | 
				
			||||||
 | 
					            Response: 200 with package implicit dependencies on success
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            HTTPNotFound: if package base is unknown
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        package_base = self.request.match_info["package"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            dependencies = self.service().package_dependencies_get(package_base)
 | 
				
			||||||
 | 
					        except UnknownPackageError:
 | 
				
			||||||
 | 
					            raise HTTPNotFound(reason=f"Package {package_base} is unknown")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return json_response(dependencies.view())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @aiohttp_apispec.docs(
 | 
				
			||||||
 | 
					        tags=["Build"],
 | 
				
			||||||
 | 
					        summary="Update package dependencies",
 | 
				
			||||||
 | 
					        description="Set package implicit dependencies",
 | 
				
			||||||
 | 
					        responses={
 | 
				
			||||||
 | 
					            204: {"description": "Success response"},
 | 
				
			||||||
 | 
					            400: {"description": "Bad data is supplied", "schema": ErrorSchema},
 | 
				
			||||||
 | 
					            401: {"description": "Authorization required", "schema": ErrorSchema},
 | 
				
			||||||
 | 
					            403: {"description": "Access is forbidden", "schema": ErrorSchema},
 | 
				
			||||||
 | 
					            404: {"description": "Repository is unknown", "schema": ErrorSchema},
 | 
				
			||||||
 | 
					            500: {"description": "Internal server error", "schema": ErrorSchema},
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        security=[{"token": [POST_PERMISSION]}],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    @aiohttp_apispec.cookies_schema(AuthSchema)
 | 
				
			||||||
 | 
					    @aiohttp_apispec.match_info_schema(PackageNameSchema)
 | 
				
			||||||
 | 
					    @aiohttp_apispec.querystring_schema(RepositoryIdSchema)
 | 
				
			||||||
 | 
					    @aiohttp_apispec.json_schema(DependenciesSchema)
 | 
				
			||||||
 | 
					    async def post(self) -> None:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        insert new package dependencies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Raises:
 | 
				
			||||||
 | 
					            HTTPBadRequest: if bad data is supplied
 | 
				
			||||||
 | 
					            HTTPNoContent: in case of success response
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        package_base = self.request.match_info["package"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            data = await self.request.json()
 | 
				
			||||||
 | 
					            data["package_base"] = package_base  # read from path instead of object
 | 
				
			||||||
 | 
					            dependencies = Dependencies.from_json(data)
 | 
				
			||||||
 | 
					        except Exception as ex:
 | 
				
			||||||
 | 
					            raise HTTPBadRequest(reason=str(ex))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            self.service().package_dependencies_set(package_base, dependencies)
 | 
				
			||||||
 | 
					        except UnknownPackageError:
 | 
				
			||||||
 | 
					            raise HTTPNotFound(reason=f"Package {package_base} is unknown")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        raise HTTPNoContent
 | 
				
			||||||
@ -25,8 +25,8 @@ from ahriman.core.exceptions import UnknownPackageError
 | 
				
			|||||||
from ahriman.core.util import pretty_datetime
 | 
					from ahriman.core.util import pretty_datetime
 | 
				
			||||||
from ahriman.models.log_record_id import LogRecordId
 | 
					from ahriman.models.log_record_id import LogRecordId
 | 
				
			||||||
from ahriman.models.user_access import UserAccess
 | 
					from ahriman.models.user_access import UserAccess
 | 
				
			||||||
from ahriman.web.schemas import AuthSchema, ErrorSchema, LogsSchema, PackageNameSchema, RepositoryIdSchema, \
 | 
					from ahriman.web.schemas import AuthSchema, ErrorSchema, LogsSchema, PackageNameSchema, PackageVersionSchema, \
 | 
				
			||||||
    VersionedLogSchema
 | 
					    RepositoryIdSchema, VersionedLogSchema
 | 
				
			||||||
from ahriman.web.views.base import BaseView
 | 
					from ahriman.web.views.base import BaseView
 | 
				
			||||||
from ahriman.web.views.status_view_guard import StatusViewGuard
 | 
					from ahriman.web.views.status_view_guard import StatusViewGuard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -60,7 +60,7 @@ class LogsView(StatusViewGuard, BaseView):
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    @aiohttp_apispec.cookies_schema(AuthSchema)
 | 
					    @aiohttp_apispec.cookies_schema(AuthSchema)
 | 
				
			||||||
    @aiohttp_apispec.match_info_schema(PackageNameSchema)
 | 
					    @aiohttp_apispec.match_info_schema(PackageNameSchema)
 | 
				
			||||||
    @aiohttp_apispec.querystring_schema(RepositoryIdSchema)
 | 
					    @aiohttp_apispec.querystring_schema(PackageVersionSchema)
 | 
				
			||||||
    async def delete(self) -> None:
 | 
					    async def delete(self) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        delete package logs
 | 
					        delete package logs
 | 
				
			||||||
@ -69,7 +69,8 @@ class LogsView(StatusViewGuard, BaseView):
 | 
				
			|||||||
            HTTPNoContent: on success response
 | 
					            HTTPNoContent: on success response
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        package_base = self.request.match_info["package"]
 | 
					        package_base = self.request.match_info["package"]
 | 
				
			||||||
        self.service().logs_remove(package_base, None)
 | 
					        version = self.request.query.get("version")
 | 
				
			||||||
 | 
					        self.service().logs_remove(package_base, version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        raise HTTPNoContent
 | 
					        raise HTTPNoContent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,7 @@ from ahriman.core.database import SQLite
 | 
				
			|||||||
from ahriman.core.distributed import WorkersCache
 | 
					from ahriman.core.distributed import WorkersCache
 | 
				
			||||||
from ahriman.core.exceptions import InitializeError
 | 
					from ahriman.core.exceptions import InitializeError
 | 
				
			||||||
from ahriman.core.spawn import Spawn
 | 
					from ahriman.core.spawn import Spawn
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.core.status.watcher import Watcher
 | 
					from ahriman.core.status.watcher import Watcher
 | 
				
			||||||
from ahriman.models.repository_id import RepositoryId
 | 
					from ahriman.models.repository_id import RepositoryId
 | 
				
			||||||
from ahriman.web.apispec import setup_apispec
 | 
					from ahriman.web.apispec import setup_apispec
 | 
				
			||||||
@ -167,7 +168,8 @@ def setup_server(configuration: Configuration, spawner: Spawn, repositories: lis
 | 
				
			|||||||
    watchers: dict[RepositoryId, Watcher] = {}
 | 
					    watchers: dict[RepositoryId, Watcher] = {}
 | 
				
			||||||
    for repository_id in repositories:
 | 
					    for repository_id in repositories:
 | 
				
			||||||
        application.logger.info("load repository %s", repository_id)
 | 
					        application.logger.info("load repository %s", repository_id)
 | 
				
			||||||
        watchers[repository_id] = Watcher(repository_id, database)
 | 
					        client = Client.load(repository_id, configuration, database, report=False)  # explicitly load local client
 | 
				
			||||||
 | 
					        watchers[repository_id] = Watcher(client)
 | 
				
			||||||
    application[WatcherKey] = watchers
 | 
					    application[WatcherKey] = watchers
 | 
				
			||||||
    # workers cache
 | 
					    # workers cache
 | 
				
			||||||
    application[WorkersKey] = WorkersCache(configuration)
 | 
					    application[WorkersKey] = WorkersCache(configuration)
 | 
				
			||||||
 | 
				
			|||||||
@ -93,7 +93,6 @@ def test_with_dependencies(application: Application, package_ahriman: Package, p
 | 
				
			|||||||
                                      side_effect=lambda *args: packages[args[0].name])
 | 
					                                      side_effect=lambda *args: packages[args[0].name])
 | 
				
			||||||
    packages_mock = mocker.patch("ahriman.application.application.Application._known_packages",
 | 
					    packages_mock = mocker.patch("ahriman.application.application.Application._known_packages",
 | 
				
			||||||
                                 return_value={"devtools", "python-build", "python-pytest"})
 | 
					                                 return_value={"devtools", "python-build", "python-pytest"})
 | 
				
			||||||
    update_remote_mock = mocker.patch("ahriman.core.database.SQLite.package_base_update")
 | 
					 | 
				
			||||||
    status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
 | 
					    status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    result = application.with_dependencies([package_ahriman], process_dependencies=True)
 | 
					    result = application.with_dependencies([package_ahriman], process_dependencies=True)
 | 
				
			||||||
@ -107,11 +106,6 @@ def test_with_dependencies(application: Application, package_ahriman: Package, p
 | 
				
			|||||||
    ], any_order=True)
 | 
					    ], any_order=True)
 | 
				
			||||||
    packages_mock.assert_called_once_with()
 | 
					    packages_mock.assert_called_once_with()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    update_remote_mock.assert_has_calls([
 | 
					 | 
				
			||||||
        MockCall(package_python_schedule),
 | 
					 | 
				
			||||||
        MockCall(packages["python"]),
 | 
					 | 
				
			||||||
        MockCall(packages["python-installer"]),
 | 
					 | 
				
			||||||
    ], any_order=True)
 | 
					 | 
				
			||||||
    status_client_mock.assert_has_calls([
 | 
					    status_client_mock.assert_has_calls([
 | 
				
			||||||
        MockCall(package_python_schedule),
 | 
					        MockCall(package_python_schedule),
 | 
				
			||||||
        MockCall(packages["python"]),
 | 
					        MockCall(packages["python"]),
 | 
				
			||||||
 | 
				
			|||||||
@ -41,11 +41,11 @@ def test_add_aur(application_packages: ApplicationPackages, package_ahriman: Pac
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
 | 
					    mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
 | 
				
			||||||
    build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
 | 
					    build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
 | 
				
			||||||
    update_remote_mock = mocker.patch("ahriman.core.database.SQLite.package_base_update")
 | 
					    status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    application_packages._add_aur(package_ahriman.base, "packager")
 | 
					    application_packages._add_aur(package_ahriman.base, "packager")
 | 
				
			||||||
    build_queue_mock.assert_called_once_with(package_ahriman)
 | 
					    build_queue_mock.assert_called_once_with(package_ahriman)
 | 
				
			||||||
    update_remote_mock.assert_called_once_with(package_ahriman)
 | 
					    status_client_mock.assert_called_once_with(package_ahriman)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_add_directory(application_packages: ApplicationPackages, package_ahriman: Package,
 | 
					def test_add_directory(application_packages: ApplicationPackages, package_ahriman: Package,
 | 
				
			||||||
@ -153,11 +153,11 @@ def test_add_repository(application_packages: ApplicationPackages, package_ahrim
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    mocker.patch("ahriman.models.package.Package.from_official", return_value=package_ahriman)
 | 
					    mocker.patch("ahriman.models.package.Package.from_official", return_value=package_ahriman)
 | 
				
			||||||
    build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
 | 
					    build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")
 | 
				
			||||||
    update_remote_mock = mocker.patch("ahriman.core.database.SQLite.package_base_update")
 | 
					    status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    application_packages._add_repository(package_ahriman.base, "packager")
 | 
					    application_packages._add_repository(package_ahriman.base, "packager")
 | 
				
			||||||
    build_queue_mock.assert_called_once_with(package_ahriman)
 | 
					    build_queue_mock.assert_called_once_with(package_ahriman)
 | 
				
			||||||
    update_remote_mock.assert_called_once_with(package_ahriman)
 | 
					    status_client_mock.assert_called_once_with(package_ahriman)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_add_add_archive(application_packages: ApplicationPackages, package_ahriman: Package,
 | 
					def test_add_add_archive(application_packages: ApplicationPackages, package_ahriman: Package,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,15 @@
 | 
				
			|||||||
from ahriman.application.application.application_properties import ApplicationProperties
 | 
					from ahriman.application.application.application_properties import ApplicationProperties
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_create_tree(application_properties: ApplicationProperties) -> None:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    must have repository attribute
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    assert application_properties.repository
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def test_architecture(application_properties: ApplicationProperties) -> None:
 | 
					def test_architecture(application_properties: ApplicationProperties) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must return repository architecture
 | 
					    must return repository architecture
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    assert application_properties.architecture == application_properties.repository_id.architecture
 | 
					    assert application_properties.architecture == application_properties.repository_id.architecture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_reporter(application_properties: ApplicationProperties) -> None:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    must have reporter attribute
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    assert application_properties.reporter
 | 
				
			||||||
 | 
				
			|||||||
@ -17,14 +17,12 @@ def test_changes(application_repository: ApplicationRepository, package_ahriman:
 | 
				
			|||||||
    must generate changes for the packages
 | 
					    must generate changes for the packages
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    changes = Changes("hash", "change")
 | 
					    changes = Changes("hash", "change")
 | 
				
			||||||
    hashes_mock = mocker.patch("ahriman.core.database.SQLite.hashes_get", return_value={
 | 
					    hashes_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get", return_value=changes)
 | 
				
			||||||
        package_ahriman.base: changes.last_commit_sha,
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    changes_mock = mocker.patch("ahriman.core.repository.Repository.package_changes", return_value=changes)
 | 
					    changes_mock = mocker.patch("ahriman.core.repository.Repository.package_changes", return_value=changes)
 | 
				
			||||||
    report_mock = mocker.patch("ahriman.core.status.client.Client.package_changes_set")
 | 
					    report_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_set")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    application_repository.changes([package_ahriman])
 | 
					    application_repository.changes([package_ahriman])
 | 
				
			||||||
    hashes_mock.assert_called_once_with()
 | 
					    hashes_mock.assert_called_once_with(package_ahriman.base)
 | 
				
			||||||
    changes_mock.assert_called_once_with(package_ahriman, changes.last_commit_sha)
 | 
					    changes_mock.assert_called_once_with(package_ahriman, changes.last_commit_sha)
 | 
				
			||||||
    report_mock.assert_called_once_with(package_ahriman.base, changes)
 | 
					    report_mock.assert_called_once_with(package_ahriman.base, changes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -34,9 +32,8 @@ def test_changes_skip(application_repository: ApplicationRepository, package_ahr
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    must skip change generation if no last commit sha has been found
 | 
					    must skip change generation if no last commit sha has been found
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    mocker.patch("ahriman.core.database.SQLite.hashes_get", return_value={})
 | 
					 | 
				
			||||||
    changes_mock = mocker.patch("ahriman.core.repository.Repository.package_changes")
 | 
					    changes_mock = mocker.patch("ahriman.core.repository.Repository.package_changes")
 | 
				
			||||||
    report_mock = mocker.patch("ahriman.core.status.client.Client.package_changes_set")
 | 
					    report_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_set")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    application_repository.changes([package_ahriman])
 | 
					    application_repository.changes([package_ahriman])
 | 
				
			||||||
    changes_mock.assert_not_called()
 | 
					    changes_mock.assert_not_called()
 | 
				
			||||||
 | 
				
			|||||||
@ -62,11 +62,11 @@ def test_run_with_patches(args: argparse.Namespace, configuration: Configuration
 | 
				
			|||||||
    args.variable = ["KEY=VALUE"]
 | 
					    args.variable = ["KEY=VALUE"]
 | 
				
			||||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
					    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
				
			||||||
    mocker.patch("ahriman.application.application.Application.add")
 | 
					    mocker.patch("ahriman.application.application.Application.add")
 | 
				
			||||||
    application_mock = mocker.patch("ahriman.core.database.SQLite.patches_insert")
 | 
					    application_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_add")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _, repository_id = configuration.check_loaded()
 | 
					    _, repository_id = configuration.check_loaded()
 | 
				
			||||||
    Add.run(args, repository_id, configuration, report=False)
 | 
					    Add.run(args, repository_id, configuration, report=False)
 | 
				
			||||||
    application_mock.assert_called_once_with(args.package[0], [PkgbuildPatch("KEY", "VALUE")])
 | 
					    application_mock.assert_called_once_with(args.package[0], PkgbuildPatch("KEY", "VALUE"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_run_with_updates(args: argparse.Namespace, configuration: Configuration, repository: Repository,
 | 
					def test_run_with_updates(args: argparse.Namespace, configuration: Configuration, repository: Repository,
 | 
				
			||||||
 | 
				
			|||||||
@ -160,13 +160,13 @@ def test_patch_set_list(application: Application, mocker: MockerFixture) -> None
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    must list available patches for the command
 | 
					    must list available patches for the command
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    get_mock = mocker.patch("ahriman.core.database.SQLite.patches_list",
 | 
					    get_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get",
 | 
				
			||||||
                            return_value={"ahriman": PkgbuildPatch(None, "patch")})
 | 
					                            return_value=[PkgbuildPatch(None, "patch")])
 | 
				
			||||||
    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
					    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
				
			||||||
    check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
 | 
					    check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Patch.patch_set_list(application, "ahriman", ["version"], False)
 | 
					    Patch.patch_set_list(application, "ahriman", ["version"], False)
 | 
				
			||||||
    get_mock.assert_called_once_with("ahriman", ["version"])
 | 
					    get_mock.assert_called_once_with("ahriman", "version")
 | 
				
			||||||
    print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ")
 | 
					    print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ")
 | 
				
			||||||
    check_mock.assert_called_once_with(False, False)
 | 
					    check_mock.assert_called_once_with(False, False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -185,7 +185,7 @@ def test_extract_packages_by_status(application: Application, mocker: MockerFixt
 | 
				
			|||||||
        ("package2", BuildStatus(BuildStatusEnum.Failed)),
 | 
					        ("package2", BuildStatus(BuildStatusEnum.Failed)),
 | 
				
			||||||
    ])
 | 
					    ])
 | 
				
			||||||
    assert Rebuild.extract_packages(application, BuildStatusEnum.Failed, from_database=True) == ["package2"]
 | 
					    assert Rebuild.extract_packages(application, BuildStatusEnum.Failed, from_database=True) == ["package2"]
 | 
				
			||||||
    packages_mock.assert_called_once_with()
 | 
					    packages_mock.assert_called_once_with(application.repository_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_extract_packages_from_database(application: Application, mocker: MockerFixture) -> None:
 | 
					def test_extract_packages_from_database(application: Application, mocker: MockerFixture) -> None:
 | 
				
			||||||
@ -194,4 +194,4 @@ def test_extract_packages_from_database(application: Application, mocker: Mocker
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    packages_mock = mocker.patch("ahriman.core.database.SQLite.packages_get")
 | 
					    packages_mock = mocker.patch("ahriman.core.database.SQLite.packages_get")
 | 
				
			||||||
    Rebuild.extract_packages(application, None, from_database=True)
 | 
					    Rebuild.extract_packages(application, None, from_database=True)
 | 
				
			||||||
    packages_mock.assert_called_once_with()
 | 
					    packages_mock.assert_called_once_with(application.repository_id)
 | 
				
			||||||
 | 
				
			|||||||
@ -50,13 +50,11 @@ def test_run_packages(args: argparse.Namespace, configuration: Configuration, re
 | 
				
			|||||||
    args.package = [package_ahriman.base, "package"]
 | 
					    args.package = [package_ahriman.base, "package"]
 | 
				
			||||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
					    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
				
			||||||
    mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
 | 
					    mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
 | 
				
			||||||
    add_mock = mocker.patch("ahriman.core.status.client.Client.package_add")
 | 
					    update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_add")
 | 
				
			||||||
    update_mock = mocker.patch("ahriman.core.status.client.Client.package_update")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _, repository_id = configuration.check_loaded()
 | 
					    _, repository_id = configuration.check_loaded()
 | 
				
			||||||
    StatusUpdate.run(args, repository_id, configuration, report=False)
 | 
					    StatusUpdate.run(args, repository_id, configuration, report=False)
 | 
				
			||||||
    add_mock.assert_called_once_with(package_ahriman, args.status)
 | 
					    update_mock.assert_called_once_with(package_ahriman, args.status)
 | 
				
			||||||
    update_mock.assert_called_once_with("package", args.status)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
 | 
					def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
 | 
				
			||||||
@ -68,7 +66,7 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo
 | 
				
			|||||||
    args.package = [package_ahriman.base]
 | 
					    args.package = [package_ahriman.base]
 | 
				
			||||||
    args.action = Action.Remove
 | 
					    args.action = Action.Remove
 | 
				
			||||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
					    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
				
			||||||
    update_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
 | 
					    update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_remove")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _, repository_id = configuration.check_loaded()
 | 
					    _, repository_id = configuration.check_loaded()
 | 
				
			||||||
    StatusUpdate.run(args, repository_id, configuration, report=False)
 | 
					    StatusUpdate.run(args, repository_id, configuration, report=False)
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,7 @@ from ahriman.core.configuration import Configuration
 | 
				
			|||||||
from ahriman.core.database import SQLite
 | 
					from ahriman.core.database import SQLite
 | 
				
			||||||
from ahriman.core.repository import Repository
 | 
					from ahriman.core.repository import Repository
 | 
				
			||||||
from ahriman.core.spawn import Spawn
 | 
					from ahriman.core.spawn import Spawn
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.core.status.watcher import Watcher
 | 
					from ahriman.core.status.watcher import Watcher
 | 
				
			||||||
from ahriman.models.aur_package import AURPackage
 | 
					from ahriman.models.aur_package import AURPackage
 | 
				
			||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
					from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
				
			||||||
@ -276,6 +277,21 @@ def database(configuration: Configuration) -> SQLite:
 | 
				
			|||||||
    database.path.unlink()
 | 
					    database.path.unlink()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture
 | 
				
			||||||
 | 
					def local_client(database: SQLite, configuration: Configuration) -> Client:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    local status client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        database(SQLite): database fixture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Returns:
 | 
				
			||||||
 | 
					        Client: local status client test instance
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    _, repository_id = configuration.check_loaded()
 | 
				
			||||||
 | 
					    return Client.load(repository_id, configuration, database, report=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture
 | 
					@pytest.fixture
 | 
				
			||||||
def package_ahriman(package_description_ahriman: PackageDescription, remote_source: RemoteSource) -> Package:
 | 
					def package_ahriman(package_description_ahriman: PackageDescription, remote_source: RemoteSource) -> Package:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
 | 
				
			|||||||
from unittest.mock import call as MockCall
 | 
					from unittest.mock import call as MockCall
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ahriman.core.build_tools.task import Task
 | 
					from ahriman.core.build_tools.task import Task
 | 
				
			||||||
from ahriman.core.database import SQLite
 | 
					from ahriman.models.pkgbuild_patch import PkgbuildPatch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_build(task_ahriman: Task, mocker: MockerFixture) -> None:
 | 
					def test_build(task_ahriman: Task, mocker: MockerFixture) -> None:
 | 
				
			||||||
@ -91,18 +91,19 @@ def test_build_no_debug(task_ahriman: Task, mocker: MockerFixture) -> None:
 | 
				
			|||||||
    ])
 | 
					    ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_init(task_ahriman: Task, database: SQLite, mocker: MockerFixture) -> None:
 | 
					def test_init(task_ahriman: Task, mocker: MockerFixture) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must copy tree instead of fetch
 | 
					    must copy tree instead of fetch
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					    patches = [PkgbuildPatch("hash", "patch")]
 | 
				
			||||||
    mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
 | 
					    mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
 | 
				
			||||||
    load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
 | 
					    load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert task_ahriman.init(Path("ahriman"), database, None) == "sha"
 | 
					    assert task_ahriman.init(Path("ahriman"), patches, None) == "sha"
 | 
				
			||||||
    load_mock.assert_called_once_with(Path("ahriman"), task_ahriman.package, [], task_ahriman.paths)
 | 
					    load_mock.assert_called_once_with(Path("ahriman"), task_ahriman.package, patches, task_ahriman.paths)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_init_bump_pkgrel(task_ahriman: Task, database: SQLite, mocker: MockerFixture) -> None:
 | 
					def test_init_bump_pkgrel(task_ahriman: Task, mocker: MockerFixture) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must bump pkgrel if it is same as provided
 | 
					    must bump pkgrel if it is same as provided
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
@ -111,11 +112,11 @@ def test_init_bump_pkgrel(task_ahriman: Task, database: SQLite, mocker: MockerFi
 | 
				
			|||||||
    write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
 | 
					    write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    local = Path("ahriman")
 | 
					    local = Path("ahriman")
 | 
				
			||||||
    assert task_ahriman.init(local, database, task_ahriman.package.version) == "sha"
 | 
					    assert task_ahriman.init(local, [], task_ahriman.package.version) == "sha"
 | 
				
			||||||
    write_mock.assert_called_once_with(local / "PKGBUILD")
 | 
					    write_mock.assert_called_once_with(local / "PKGBUILD")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_init_bump_pkgrel_skip(task_ahriman: Task, database: SQLite, mocker: MockerFixture) -> None:
 | 
					def test_init_bump_pkgrel_skip(task_ahriman: Task, mocker: MockerFixture) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must keep pkgrel if version is different from provided
 | 
					    must keep pkgrel if version is different from provided
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
@ -123,5 +124,5 @@ def test_init_bump_pkgrel_skip(task_ahriman: Task, database: SQLite, mocker: Moc
 | 
				
			|||||||
    mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
 | 
					    mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
 | 
				
			||||||
    write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
 | 
					    write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert task_ahriman.init(Path("ahriman"), database, f"2:{task_ahriman.package.version}") == "sha"
 | 
					    assert task_ahriman.init(Path("ahriman"), [], f"2:{task_ahriman.package.version}") == "sha"
 | 
				
			||||||
    write_mock.assert_not_called()
 | 
					    write_mock.assert_not_called()
 | 
				
			||||||
 | 
				
			|||||||
@ -53,13 +53,3 @@ def test_changes_insert_remove_full(database: SQLite, package_ahriman: Package,
 | 
				
			|||||||
    assert database.changes_get(package_python_schedule.base).changes is None
 | 
					    assert database.changes_get(package_python_schedule.base).changes is None
 | 
				
			||||||
    assert database.changes_get(
 | 
					    assert database.changes_get(
 | 
				
			||||||
        package_ahriman.base, RepositoryId("i686", database._repository_id.name)).changes == "change2"
 | 
					        package_ahriman.base, RepositoryId("i686", database._repository_id.name)).changes == "change2"
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def test_hashes_get(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    must return non-empty hashes for packages
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    database.changes_insert(package_ahriman.base, Changes("sha1", "change1"))
 | 
					 | 
				
			||||||
    database.changes_insert(package_python_schedule.base, Changes())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    assert database.hashes_get() == {package_ahriman.base: "sha1"}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ from sqlite3 import Connection
 | 
				
			|||||||
from unittest.mock import call as MockCall
 | 
					from unittest.mock import call as MockCall
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ahriman.core.database import SQLite
 | 
					from ahriman.core.database import SQLite
 | 
				
			||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
					from ahriman.models.build_status import BuildStatus
 | 
				
			||||||
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
 | 
				
			||||||
from ahriman.models.remote_source import RemoteSource
 | 
					from ahriman.models.remote_source import RemoteSource
 | 
				
			||||||
@ -66,14 +66,6 @@ def test_package_update_insert_packages_no_arch(database: SQLite, connection: Co
 | 
				
			|||||||
    connection.executemany(pytest.helpers.anyvar(str, strict=True), [])
 | 
					    connection.executemany(pytest.helpers.anyvar(str, strict=True), [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_package_update_insert_status(database: SQLite, connection: Connection, package_ahriman: Package) -> None:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    must insert single package status
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    database._package_update_insert_status(connection, package_ahriman.base, BuildStatus(), database._repository_id)
 | 
					 | 
				
			||||||
    connection.execute(pytest.helpers.anyvar(str, strict=True), pytest.helpers.anyvar(int))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def test_packages_get_select_package_bases(database: SQLite, connection: Connection) -> None:
 | 
					def test_packages_get_select_package_bases(database: SQLite, connection: Connection) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must select all bases
 | 
					    must select all bases
 | 
				
			||||||
@ -131,16 +123,12 @@ def test_package_update(database: SQLite, package_ahriman: Package, mocker: Mock
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    must update package status
 | 
					    must update package status
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    status = BuildStatus()
 | 
					 | 
				
			||||||
    insert_base_mock = mocker.patch("ahriman.core.database.SQLite._package_update_insert_base")
 | 
					    insert_base_mock = mocker.patch("ahriman.core.database.SQLite._package_update_insert_base")
 | 
				
			||||||
    insert_status_mock = mocker.patch("ahriman.core.database.SQLite._package_update_insert_status")
 | 
					 | 
				
			||||||
    insert_packages_mock = mocker.patch("ahriman.core.database.SQLite._package_update_insert_packages")
 | 
					    insert_packages_mock = mocker.patch("ahriman.core.database.SQLite._package_update_insert_packages")
 | 
				
			||||||
    remove_packages_mock = mocker.patch("ahriman.core.database.SQLite._package_remove_packages")
 | 
					    remove_packages_mock = mocker.patch("ahriman.core.database.SQLite._package_remove_packages")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    database.package_update(package_ahriman, status)
 | 
					    database.package_update(package_ahriman)
 | 
				
			||||||
    insert_base_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman, database._repository_id)
 | 
					    insert_base_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman, database._repository_id)
 | 
				
			||||||
    insert_status_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.base, status,
 | 
					 | 
				
			||||||
                                               database._repository_id)
 | 
					 | 
				
			||||||
    insert_packages_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman,
 | 
					    insert_packages_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman,
 | 
				
			||||||
                                                 database._repository_id)
 | 
					                                                 database._repository_id)
 | 
				
			||||||
    remove_packages_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.base,
 | 
					    remove_packages_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.base,
 | 
				
			||||||
@ -168,7 +156,8 @@ def test_package_update_get(database: SQLite, package_ahriman: Package) -> None:
 | 
				
			|||||||
    must insert and retrieve package
 | 
					    must insert and retrieve package
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    status = BuildStatus()
 | 
					    status = BuildStatus()
 | 
				
			||||||
    database.package_update(package_ahriman, status)
 | 
					    database.package_update(package_ahriman)
 | 
				
			||||||
 | 
					    database.status_update(package_ahriman.base, status)
 | 
				
			||||||
    assert next((db_package, db_status)
 | 
					    assert next((db_package, db_status)
 | 
				
			||||||
                for db_package, db_status in database.packages_get()
 | 
					                for db_package, db_status in database.packages_get()
 | 
				
			||||||
                if db_package.base == package_ahriman.base) == (package_ahriman, status)
 | 
					                if db_package.base == package_ahriman.base) == (package_ahriman, status)
 | 
				
			||||||
@ -179,7 +168,7 @@ def test_package_update_remove_get(database: SQLite, package_ahriman: Package) -
 | 
				
			|||||||
    must insert, remove and retrieve package
 | 
					    must insert, remove and retrieve package
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    status = BuildStatus()
 | 
					    status = BuildStatus()
 | 
				
			||||||
    database.package_update(package_ahriman, status)
 | 
					    database.package_update(package_ahriman)
 | 
				
			||||||
    database.package_remove(package_ahriman.base)
 | 
					    database.package_remove(package_ahriman.base)
 | 
				
			||||||
    assert not database.packages_get()
 | 
					    assert not database.packages_get()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -188,11 +177,12 @@ def test_package_update_update(database: SQLite, package_ahriman: Package) -> No
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    must perform update for existing package
 | 
					    must perform update for existing package
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    database.package_update(package_ahriman, BuildStatus())
 | 
					    database.package_update(package_ahriman)
 | 
				
			||||||
    database.package_update(package_ahriman, BuildStatus(BuildStatusEnum.Failed))
 | 
					    package_ahriman.version = "1.0.0"
 | 
				
			||||||
    assert next(db_status.status
 | 
					    database.package_update(package_ahriman)
 | 
				
			||||||
                for db_package, db_status in database.packages_get()
 | 
					    assert next(db_package.version
 | 
				
			||||||
                if db_package.base == package_ahriman.base) == BuildStatusEnum.Failed
 | 
					                for db_package, _ in database.packages_get()
 | 
				
			||||||
 | 
					                if db_package.base == package_ahriman.base) == package_ahriman.version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_remote_update_get(database: SQLite, package_ahriman: Package) -> None:
 | 
					def test_remote_update_get(database: SQLite, package_ahriman: Package) -> None:
 | 
				
			||||||
@ -213,3 +203,14 @@ def test_remote_update_update(database: SQLite, package_ahriman: Package) -> Non
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    database.package_base_update(package_ahriman)
 | 
					    database.package_base_update(package_ahriman)
 | 
				
			||||||
    assert database.remotes_get()[package_ahriman.base] == remote_source
 | 
					    assert database.remotes_get()[package_ahriman.base] == remote_source
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_status_update(database: SQLite, package_ahriman: Package) -> None:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    must insert single package status
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    status = BuildStatus()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    database.package_update(package_ahriman, database._repository_id)
 | 
				
			||||||
 | 
					    database.status_update(package_ahriman.base, status, database._repository_id)
 | 
				
			||||||
 | 
					    assert database.packages_get(database._repository_id) == [(package_ahriman, status)]
 | 
				
			||||||
 | 
				
			|||||||
@ -44,6 +44,7 @@ def test_package_clear(database: SQLite, mocker: MockerFixture) -> None:
 | 
				
			|||||||
    logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_remove")
 | 
					    logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_remove")
 | 
				
			||||||
    changes_mock = mocker.patch("ahriman.core.database.SQLite.changes_remove")
 | 
					    changes_mock = mocker.patch("ahriman.core.database.SQLite.changes_remove")
 | 
				
			||||||
    dependencies_mock = mocker.patch("ahriman.core.database.SQLite.dependencies_remove")
 | 
					    dependencies_mock = mocker.patch("ahriman.core.database.SQLite.dependencies_remove")
 | 
				
			||||||
 | 
					    tree_clear_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_clear")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    database.package_clear("package")
 | 
					    database.package_clear("package")
 | 
				
			||||||
    build_queue_mock.assert_called_once_with("package")
 | 
					    build_queue_mock.assert_called_once_with("package")
 | 
				
			||||||
@ -51,3 +52,4 @@ def test_package_clear(database: SQLite, mocker: MockerFixture) -> None:
 | 
				
			|||||||
    logs_mock.assert_called_once_with("package", None)
 | 
					    logs_mock.assert_called_once_with("package", None)
 | 
				
			||||||
    changes_mock.assert_called_once_with("package")
 | 
					    changes_mock.assert_called_once_with("package")
 | 
				
			||||||
    dependencies_mock.assert_called_once_with("package")
 | 
					    dependencies_mock.assert_called_once_with("package")
 | 
				
			||||||
 | 
					    tree_clear_mock.assert_called_once_with("package")
 | 
				
			||||||
 | 
				
			|||||||
@ -5,15 +5,15 @@ from pytest_mock import MockerFixture
 | 
				
			|||||||
from unittest.mock import call as MockCall
 | 
					from unittest.mock import call as MockCall
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ahriman.core.configuration import Configuration
 | 
					from ahriman.core.configuration import Configuration
 | 
				
			||||||
from ahriman.core.database import SQLite
 | 
					 | 
				
			||||||
from ahriman.core.exceptions import GitRemoteError
 | 
					from ahriman.core.exceptions import GitRemoteError
 | 
				
			||||||
from ahriman.core.gitremote.remote_push import RemotePush
 | 
					from ahriman.core.gitremote.remote_push import RemotePush
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.models.package import Package
 | 
					from ahriman.models.package import Package
 | 
				
			||||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
 | 
					from ahriman.models.pkgbuild_patch import PkgbuildPatch
 | 
				
			||||||
from ahriman.models.result import Result
 | 
					from ahriman.models.result import Result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_package_update(database: SQLite, configuration: Configuration, package_ahriman: Package,
 | 
					def test_package_update(local_client: Client, configuration: Configuration, package_ahriman: Package,
 | 
				
			||||||
                        mocker: MockerFixture) -> None:
 | 
					                        mocker: MockerFixture) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must update single package
 | 
					    must update single package
 | 
				
			||||||
@ -27,9 +27,10 @@ def test_package_update(database: SQLite, configuration: Configuration, package_
 | 
				
			|||||||
    rmtree_mock = mocker.patch("shutil.rmtree")
 | 
					    rmtree_mock = mocker.patch("shutil.rmtree")
 | 
				
			||||||
    unlink_mock = mocker.patch("pathlib.Path.unlink")
 | 
					    unlink_mock = mocker.patch("pathlib.Path.unlink")
 | 
				
			||||||
    fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
 | 
					    fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
 | 
				
			||||||
    patches_mock = mocker.patch("ahriman.core.database.SQLite.patches_get", return_value=[patch1, patch2])
 | 
					    patches_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get",
 | 
				
			||||||
 | 
					                                return_value=[patch1, patch2])
 | 
				
			||||||
    patches_write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
 | 
					    patches_write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
 | 
				
			||||||
    runner = RemotePush(database, configuration, "gitremote")
 | 
					    runner = RemotePush(local_client, configuration, "gitremote")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assert runner.package_update(package_ahriman, local) == package_ahriman.base
 | 
					    assert runner.package_update(package_ahriman, local) == package_ahriman.base
 | 
				
			||||||
    glob_mock.assert_called_once_with(".git*")
 | 
					    glob_mock.assert_called_once_with(".git*")
 | 
				
			||||||
@ -39,28 +40,28 @@ def test_package_update(database: SQLite, configuration: Configuration, package_
 | 
				
			|||||||
    ])
 | 
					    ])
 | 
				
			||||||
    unlink_mock.assert_called_once_with()
 | 
					    unlink_mock.assert_called_once_with()
 | 
				
			||||||
    fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.remote)
 | 
					    fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.remote)
 | 
				
			||||||
    patches_mock.assert_called_once_with(package_ahriman.base)
 | 
					    patches_mock.assert_called_once_with(package_ahriman.base, None)
 | 
				
			||||||
    patches_write_mock.assert_has_calls([
 | 
					    patches_write_mock.assert_has_calls([
 | 
				
			||||||
        MockCall(local / package_ahriman.base / f"ahriman-{package_ahriman.base}.patch"),
 | 
					        MockCall(local / package_ahriman.base / f"ahriman-{package_ahriman.base}.patch"),
 | 
				
			||||||
        MockCall(local / package_ahriman.base / f"ahriman-{patch2.key}.patch"),
 | 
					        MockCall(local / package_ahriman.base / f"ahriman-{patch2.key}.patch"),
 | 
				
			||||||
    ])
 | 
					    ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_packages_update(database: SQLite, configuration: Configuration, result: Result, package_ahriman: Package,
 | 
					def test_packages_update(local_client: Client, configuration: Configuration, result: Result, package_ahriman: Package,
 | 
				
			||||||
                         mocker: MockerFixture) -> None:
 | 
					                         mocker: MockerFixture) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must generate packages update
 | 
					    must generate packages update
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    update_mock = mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.package_update",
 | 
					    update_mock = mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.package_update",
 | 
				
			||||||
                               return_value=[package_ahriman.base])
 | 
					                               return_value=[package_ahriman.base])
 | 
				
			||||||
    runner = RemotePush(database, configuration, "gitremote")
 | 
					    runner = RemotePush(local_client, configuration, "gitremote")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    local = Path("local")
 | 
					    local = Path("local")
 | 
				
			||||||
    assert list(runner.packages_update(result, local))
 | 
					    assert list(runner.packages_update(result, local))
 | 
				
			||||||
    update_mock.assert_called_once_with(package_ahriman, local)
 | 
					    update_mock.assert_called_once_with(package_ahriman, local)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_run(database: SQLite, configuration: Configuration, result: Result, package_ahriman: Package,
 | 
					def test_run(local_client: Client, configuration: Configuration, result: Result, package_ahriman: Package,
 | 
				
			||||||
             mocker: MockerFixture) -> None:
 | 
					             mocker: MockerFixture) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must push changes on result
 | 
					    must push changes on result
 | 
				
			||||||
@ -68,7 +69,7 @@ def test_run(database: SQLite, configuration: Configuration, result: Result, pac
 | 
				
			|||||||
    mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.packages_update", return_value=[package_ahriman.base])
 | 
					    mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.packages_update", return_value=[package_ahriman.base])
 | 
				
			||||||
    fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
 | 
					    fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
 | 
				
			||||||
    push_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.push")
 | 
					    push_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.push")
 | 
				
			||||||
    runner = RemotePush(database, configuration, "gitremote")
 | 
					    runner = RemotePush(local_client, configuration, "gitremote")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    runner.run(result)
 | 
					    runner.run(result)
 | 
				
			||||||
    fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), runner.remote_source)
 | 
					    fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), runner.remote_source)
 | 
				
			||||||
@ -77,12 +78,12 @@ def test_run(database: SQLite, configuration: Configuration, result: Result, pac
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_run_failed(database: SQLite, configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
 | 
					def test_run_failed(local_client: Client, configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must reraise exception on error occurred
 | 
					    must reraise exception on error occurred
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", side_effect=Exception())
 | 
					    mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", side_effect=Exception())
 | 
				
			||||||
    runner = RemotePush(database, configuration, "gitremote")
 | 
					    runner = RemotePush(local_client, configuration, "gitremote")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    with pytest.raises(GitRemoteError):
 | 
					    with pytest.raises(GitRemoteError):
 | 
				
			||||||
        runner.run(result)
 | 
					        runner.run(result)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,8 @@
 | 
				
			|||||||
from pytest_mock import MockerFixture
 | 
					from pytest_mock import MockerFixture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ahriman.core.configuration import Configuration
 | 
					from ahriman.core.configuration import Configuration
 | 
				
			||||||
from ahriman.core.database import SQLite
 | 
					 | 
				
			||||||
from ahriman.core.gitremote import RemotePushTrigger
 | 
					from ahriman.core.gitremote import RemotePushTrigger
 | 
				
			||||||
 | 
					from ahriman.core.status.client import Client
 | 
				
			||||||
from ahriman.models.package import Package
 | 
					from ahriman.models.package import Package
 | 
				
			||||||
from ahriman.models.result import Result
 | 
					from ahriman.models.result import Result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -19,15 +19,15 @@ def test_configuration_sections(configuration: Configuration) -> None:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_on_result(configuration: Configuration, result: Result, package_ahriman: Package,
 | 
					def test_on_result(configuration: Configuration, result: Result, package_ahriman: Package,
 | 
				
			||||||
                   database: SQLite, mocker: MockerFixture) -> None:
 | 
					                   local_client: Client, mocker: MockerFixture) -> None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    must push changes on result
 | 
					    must push changes on result
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    database_mock = mocker.patch("ahriman.core._Context.get", return_value=database)
 | 
					    database_mock = mocker.patch("ahriman.core._Context.get", return_value=local_client)
 | 
				
			||||||
    run_mock = mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.run")
 | 
					    run_mock = mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.run")
 | 
				
			||||||
    _, repository_id = configuration.check_loaded()
 | 
					    _, repository_id = configuration.check_loaded()
 | 
				
			||||||
    trigger = RemotePushTrigger(repository_id, configuration)
 | 
					    trigger = RemotePushTrigger(repository_id, configuration)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    trigger.on_result(result, [package_ahriman])
 | 
					    trigger.on_result(result, [package_ahriman])
 | 
				
			||||||
    database_mock.assert_called_once_with(SQLite)
 | 
					    database_mock.assert_called_once_with(Client)
 | 
				
			||||||
    run_mock.assert_called_once_with(result)
 | 
					    run_mock.assert_called_once_with(result)
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user