mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-05-04 20:23:49 +00:00
implement local reporter mode
This commit is contained in:
parent
cd4516d6e8
commit
cd1b2b171c
@ -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 | None) -> list[Dependencies]:
|
||||||
|
"""
|
||||||
|
get package dependencies
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str | None): 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 | None) -> list[Dependencies]:
|
||||||
|
"""
|
||||||
|
get package dependencies
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str | None): 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, optional): package base (Default value = "")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: full url for web service for dependencies
|
||||||
|
"""
|
||||||
|
return f"{self.address}/api/v1/dependencies/{urlencode(package_base)}"
|
||||||
|
|
||||||
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 | None) -> list[Dependencies]:
|
||||||
|
"""
|
||||||
|
get package dependencies
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str | None): 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 or ""),
|
||||||
|
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
|
||||||
|
66
src/ahriman/web/views/v1/packages/dependencies.py
Normal file
66
src/ahriman/web/views/v1/packages/dependencies.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#
|
||||||
|
# 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 Response, json_response
|
||||||
|
|
||||||
|
from ahriman.models.user_access import UserAccess
|
||||||
|
from ahriman.web.schemas import AuthSchema, DependenciesSchema, ErrorSchema, RepositoryIdSchema
|
||||||
|
from ahriman.web.views.base import BaseView
|
||||||
|
from ahriman.web.views.status_view_guard import StatusViewGuard
|
||||||
|
|
||||||
|
|
||||||
|
class DependenciesView(StatusViewGuard, BaseView):
|
||||||
|
"""
|
||||||
|
packages dependencies web view
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||||
|
"""
|
||||||
|
|
||||||
|
GET_PERMISSION = UserAccess.Reporter
|
||||||
|
POST_PERMISSION = UserAccess.Full
|
||||||
|
ROUTES = ["/api/v1/dependencies"]
|
||||||
|
|
||||||
|
@aiohttp_apispec.docs(
|
||||||
|
tags=["Build"],
|
||||||
|
summary="Get dependencies for all packages",
|
||||||
|
description="Retrieve implicit dependencies for all known packages",
|
||||||
|
responses={
|
||||||
|
200: {"description": "Success response", "schema": DependenciesSchema(many=True)},
|
||||||
|
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.querystring_schema(RepositoryIdSchema)
|
||||||
|
async def get(self) -> Response:
|
||||||
|
"""
|
||||||
|
get dependencies for all packages
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: 200 with package implicit dependencies on success
|
||||||
|
"""
|
||||||
|
dependencies = self.service().client.package_dependencies_get(None)
|
||||||
|
|
||||||
|
return json_response([dependency.view() for dependency in dependencies])
|
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/dependencies/{package}"]
|
||||||
|
|
||||||
|
@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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user