Compare commits

..

2 Commits

15 changed files with 98 additions and 123 deletions

View File

@ -17,6 +17,7 @@
# 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 pathlib import Path
from sqlite3 import Connection from sqlite3 import Connection
from ahriman.core.database.operations.operations import Operations from ahriman.core.database.operations.operations import Operations
@ -45,7 +46,13 @@ class DependenciesOperations(Operations):
def run(connection: Connection) -> list[Dependencies]: def run(connection: Connection) -> list[Dependencies]:
return [ return [
Dependencies(row["package_base"], row["dependencies"]) Dependencies(
row["package_base"],
{
Path(path): packages
for path, packages in row["dependencies"].items()
}
)
for row in connection.execute( for row in connection.execute(
""" """
select package_base, dependencies from package_dependencies select package_base, dependencies from package_dependencies

View File

@ -108,7 +108,7 @@ class UpdateHandler(PackageInfo, Cleaner):
filesystem = extract_files(required_packages) filesystem = extract_files(required_packages)
for path, packages in dependencies.paths.items(): for path, packages in dependencies.paths.items():
found = filesystem.get(Path(path), set()) found = filesystem.get(path, set())
if found.intersection(packages): if found.intersection(packages):
continue continue

View File

@ -76,17 +76,45 @@ class Watcher(LazyLogging):
for package, status in self.client.package_get(None) for package, status in self.client.package_get(None)
} }
def package_add(self, package: Package, status: BuildStatusEnum) -> None: def logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]:
""" """
update package extract logs for the package base
Args: Args:
package(Package): package description package_base(str): package base
status(BuildStatusEnum): new build status 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 self._lock: _ = self.package_get(package_base)
self._known[package.base] = (package, BuildStatus(status)) return self.client.package_logs_get(package_base, limit, offset)
self.client.package_add(package, status)
def logs_remove(self, package_base: str, version: str | None) -> None:
"""
remove package related logs
Args:
package_base(str): package base
version(str): package version
"""
self.client.package_logs_remove(package_base, version)
def logs_update(self, log_record_id: LogRecordId, created: float, message: str) -> None:
"""
make new log record into database
Args:
log_record_id(LogRecordId): log record id
created(float): log created timestamp
message(str): log message
"""
if self._last_log_record_id != log_record_id:
# there is new log record, so we remove old ones
self.logs_remove(log_record_id.package_base, log_record_id.version)
self._last_log_record_id = log_record_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:
""" """
@ -101,17 +129,6 @@ class Watcher(LazyLogging):
_ = self.package_get(package_base) _ = self.package_get(package_base)
return self.client.package_changes_get(package_base) return self.client.package_changes_get(package_base)
def package_changes_set(self, package_base: str, changes: Changes) -> None:
"""
update package changes
Args:
package_base(str): package base
changes(Changes): package changes
"""
_ = self.package_get(package_base)
self.client.package_changes_set(package_base, changes)
def package_dependencies_get(self, package_base: str) -> Dependencies: def package_dependencies_get(self, package_base: str) -> Dependencies:
""" """
retrieve package dependencies retrieve package dependencies
@ -158,47 +175,34 @@ class Watcher(LazyLogging):
except KeyError: except KeyError:
raise UnknownPackageError(package_base) from None raise UnknownPackageError(package_base) from None
def package_logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]: def package_remove(self, package_base: str) -> None:
""" """
extract logs for the package base remove package base from known list if any
Args: Args:
package_base(str): package base 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
""" """
_ = self.package_get(package_base) with self._lock:
return self.client.package_logs_get(package_base, limit, offset) self._known.pop(package_base, None)
self.client.package_remove(package_base)
self.logs_remove(package_base, None)
def package_logs_remove(self, package_base: str, version: str | None) -> None: def package_update(self, package_base: str, status: BuildStatusEnum, package: Package | None) -> None:
""" """
remove package related logs update package status and description
Args: Args:
package_base(str): package base package_base(str): package base to update
version(str): package version status(BuildStatusEnum): new build status
package(Package | None): optional package description. In case if not set current properties will be used
""" """
self.client.package_logs_remove(package_base, version) if package is None:
package, _ = self.package_get(package_base)
with self._lock:
self._known[package_base] = (package, BuildStatus(status))
self.client.package_set(package_base, status)
def package_logs_update(self, log_record_id: LogRecordId, created: float, message: str) -> None: def patches_get(self, package_base: str, variable: str | None) -> list[PkgbuildPatch]:
"""
make new log record into database
Args:
log_record_id(LogRecordId): log record id
created(float): log created timestamp
message(str): log message
"""
if self._last_log_record_id != log_record_id:
# there is new log record, so we remove old ones
self.package_logs_remove(log_record_id.package_base, log_record_id.version)
self._last_log_record_id = log_record_id
self.client.package_logs_add(log_record_id, created, message)
def package_patches_get(self, package_base: str, variable: str | None) -> list[PkgbuildPatch]:
""" """
get patches for the package get patches for the package
@ -213,7 +217,7 @@ class Watcher(LazyLogging):
# so here we skip checking if package exists or not # so here we skip checking if package exists or not
return self.client.package_patches_get(package_base, variable) return self.client.package_patches_get(package_base, variable)
def package_patches_remove(self, package_base: str, variable: str) -> None: def patches_remove(self, package_base: str, variable: str) -> None:
""" """
remove package patch remove package patch
@ -223,7 +227,7 @@ class Watcher(LazyLogging):
""" """
self.client.package_patches_remove(package_base, variable) self.client.package_patches_remove(package_base, variable)
def package_patches_update(self, package_base: str, patch: PkgbuildPatch) -> None: def patches_update(self, package_base: str, patch: PkgbuildPatch) -> None:
""" """
update package patch update package patch
@ -233,31 +237,6 @@ class Watcher(LazyLogging):
""" """
self.client.package_patches_add(package_base, patch) self.client.package_patches_add(package_base, patch)
def package_remove(self, package_base: str) -> None:
"""
remove package base from known list if any
Args:
package_base(str): package base
"""
with self._lock:
self._known.pop(package_base, None)
self.client.package_remove(package_base)
self.package_logs_remove(package_base, None)
def package_update(self, package_base: str, status: BuildStatusEnum) -> None:
"""
update package status
Args:
package_base(str): package base to update
status(BuildStatusEnum): new build status
"""
package, _ = self.package_get(package_base)
with self._lock:
self._known[package_base] = (package, BuildStatus(status))
self.client.package_set(package_base, status)
def status_update(self, status: BuildStatusEnum) -> None: def status_update(self, status: BuildStatusEnum) -> None:
""" """
update service status update service status

View File

@ -18,6 +18,7 @@
# 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, fields from dataclasses import dataclass, field, fields
from pathlib import Path
from typing import Any, Self from typing import Any, Self
from ahriman.core.util import dataclass_view, filter_json from ahriman.core.util import dataclass_view, filter_json
@ -30,11 +31,11 @@ class Dependencies:
Attributes: Attributes:
package_base(str): package base package_base(str): package base
paths(dict[str, list[str]]): map of the paths used by this package to set of packages in which they were found paths(dict[Path, list[str]]): map of the paths used by this package to set of packages in which they were found
""" """
package_base: str package_base: str
paths: dict[str, list[str]] = field(default_factory=dict) paths: dict[Path, list[str]] = field(default_factory=dict)
@classmethod @classmethod
def from_json(cls, dump: dict[str, Any]) -> Self: def from_json(cls, dump: dict[str, Any]) -> Self:

View File

@ -99,7 +99,7 @@ class PackageArchive:
""" """
dependencies, roots = self.depends_on_paths() dependencies, roots = self.depends_on_paths()
result: dict[str, list[str]] = {} result: dict[Path, list[str]] = {}
for package, (directories, files) in self.installed_packages().items(): for package, (directories, files) in self.installed_packages().items():
if package in self.package.packages: if package in self.package.packages:
continue # skip package itself continue # skip package itself
@ -108,7 +108,7 @@ class PackageArchive:
required_by.extend(library for library in files if library.name in dependencies) required_by.extend(library for library in files if library.name in dependencies)
for path in required_by: for path in required_by:
result.setdefault(str(path), []).append(package) result.setdefault(path, []).append(package)
return Dependencies(self.package.base, result) return Dependencies(self.package.base, result)

View File

@ -113,6 +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)
self.service().package_changes_set(package_base, changes) self.service().client.package_changes_set(package_base, changes)
raise HTTPNoContent raise HTTPNoContent

View File

@ -70,7 +70,7 @@ class LogsView(StatusViewGuard, BaseView):
""" """
package_base = self.request.match_info["package"] package_base = self.request.match_info["package"]
version = self.request.query.get("version") version = self.request.query.get("version")
self.service().package_logs_remove(package_base, version) self.service().logs_remove(package_base, version)
raise HTTPNoContent raise HTTPNoContent
@ -104,7 +104,7 @@ class LogsView(StatusViewGuard, BaseView):
try: try:
_, status = self.service().package_get(package_base) _, status = self.service().package_get(package_base)
logs = self.service().package_logs_get(package_base) logs = self.service().logs_get(package_base)
except UnknownPackageError: except UnknownPackageError:
raise HTTPNotFound(reason=f"Package {package_base} is unknown") raise HTTPNotFound(reason=f"Package {package_base} is unknown")
@ -150,6 +150,6 @@ class LogsView(StatusViewGuard, BaseView):
except Exception as ex: except Exception as ex:
raise HTTPBadRequest(reason=str(ex)) raise HTTPBadRequest(reason=str(ex))
self.service().package_logs_update(LogRecordId(package_base, version), created, record) self.service().logs_update(LogRecordId(package_base, version), created, record)
raise HTTPNoContent raise HTTPNoContent

View File

@ -152,10 +152,7 @@ class PackageView(StatusViewGuard, BaseView):
raise HTTPBadRequest(reason=str(ex)) raise HTTPBadRequest(reason=str(ex))
try: try:
if package is None: self.service().package_update(package_base, status, package)
self.service().package_update(package_base, status)
else:
self.service().package_add(package, status)
except UnknownPackageError: except UnknownPackageError:
raise HTTPBadRequest(reason=f"Package {package_base} is unknown, but no package body set") raise HTTPBadRequest(reason=f"Package {package_base} is unknown, but no package body set")

View File

@ -63,7 +63,7 @@ class PatchView(StatusViewGuard, BaseView):
""" """
package_base = self.request.match_info["package"] package_base = self.request.match_info["package"]
variable = self.request.match_info["patch"] variable = self.request.match_info["patch"]
self.service().package_patches_remove(package_base, variable) self.service().patches_remove(package_base, variable)
raise HTTPNoContent raise HTTPNoContent
@ -95,7 +95,7 @@ class PatchView(StatusViewGuard, BaseView):
package_base = self.request.match_info["package"] package_base = self.request.match_info["package"]
variable = self.request.match_info["patch"] variable = self.request.match_info["patch"]
patches = self.service().package_patches_get(package_base, variable) patches = self.service().patches_get(package_base, variable)
selected = next((patch for patch in patches if patch.key == variable), None) selected = next((patch for patch in patches if patch.key == variable), None)
if selected is None: if selected is None:

View File

@ -63,7 +63,7 @@ class PatchesView(StatusViewGuard, BaseView):
Response: 200 with package patches on success Response: 200 with package patches on success
""" """
package_base = self.request.match_info["package"] package_base = self.request.match_info["package"]
patches = self.service().package_patches_get(package_base, None) patches = self.service().patches_get(package_base, None)
response = [patch.view() for patch in patches] response = [patch.view() for patch in patches]
return json_response(response) return json_response(response)
@ -101,6 +101,6 @@ class PatchesView(StatusViewGuard, BaseView):
except Exception as ex: except Exception as ex:
raise HTTPBadRequest(reason=str(ex)) raise HTTPBadRequest(reason=str(ex))
self.service().package_patches_update(package_base, PkgbuildPatch(key, value)) self.service().patches_update(package_base, PkgbuildPatch(key, value))
raise HTTPNoContent raise HTTPNoContent

View File

@ -69,7 +69,7 @@ class LogsView(StatusViewGuard, BaseView):
package_base = self.request.match_info["package"] package_base = self.request.match_info["package"]
limit, offset = self.page() limit, offset = self.page()
try: try:
logs = self.service().package_logs_get(package_base, limit, offset) logs = self.service().logs_get(package_base, limit, offset)
except UnknownPackageError: except UnknownPackageError:
raise HTTPNotFound(reason=f"Package {package_base} is unknown") raise HTTPNotFound(reason=f"Package {package_base} is unknown")

View File

@ -1,3 +1,5 @@
from pathlib import Path
from ahriman.core.database import SQLite from ahriman.core.database import SQLite
from ahriman.models.dependencies import Dependencies from ahriman.models.dependencies import Dependencies
from ahriman.models.package import Package from ahriman.models.package import Package
@ -8,11 +10,11 @@ def test_dependencies_insert_get(database: SQLite, package_ahriman: Package) ->
""" """
must insert and get dependencies must insert and get dependencies
""" """
dependencies = Dependencies(package_ahriman.base, {"usr/lib/python3.11/site-packages": ["python"]}) dependencies = Dependencies(package_ahriman.base, {Path("usr/lib/python3.11/site-packages"): ["python"]})
database.dependencies_insert(dependencies) database.dependencies_insert(dependencies)
assert database.dependencies_get(package_ahriman.base) == [dependencies] assert database.dependencies_get(package_ahriman.base) == [dependencies]
dependencies2 = Dependencies(package_ahriman.base, {"usr/lib/python3.11/site-packages": ["python3"]}) dependencies2 = Dependencies(package_ahriman.base, {Path("usr/lib/python3.11/site-packages"): ["python3"]})
database.dependencies_insert(dependencies2, RepositoryId("i686", database._repository_id.name)) database.dependencies_insert(dependencies2, RepositoryId("i686", database._repository_id.name))
assert database.dependencies_get() == [dependencies] assert database.dependencies_get() == [dependencies]
assert database.dependencies_get(package_ahriman.base) == [dependencies] assert database.dependencies_get(package_ahriman.base) == [dependencies]
@ -25,11 +27,11 @@ def test_dependencies_insert_remove(database: SQLite, package_ahriman: Package,
""" """
must remove dependencies for the package must remove dependencies for the package
""" """
dependencies1 = Dependencies(package_ahriman.base, {"usr": ["python"]}) dependencies1 = Dependencies(package_ahriman.base, {Path("usr"): ["python"]})
database.dependencies_insert(dependencies1) database.dependencies_insert(dependencies1)
dependencies2 = Dependencies(package_python_schedule.base, {"usr": ["filesystem"]}) dependencies2 = Dependencies(package_python_schedule.base, {Path("usr"): ["filesystem"]})
database.dependencies_insert(dependencies2) database.dependencies_insert(dependencies2)
dependencies3 = Dependencies(package_ahriman.base, {"usr": ["python3"]}) dependencies3 = Dependencies(package_ahriman.base, {Path("usr"): ["python3"]})
database.dependencies_insert(dependencies3, RepositoryId("i686", database._repository_id.name)) database.dependencies_insert(dependencies3, RepositoryId("i686", database._repository_id.name))
assert database.dependencies_get() == [dependencies1, dependencies2] assert database.dependencies_get() == [dependencies1, dependencies2]
@ -49,9 +51,9 @@ def test_dependencies_insert_remove_full(database: SQLite, package_ahriman: Pack
""" """
must remove all dependencies for the repository must remove all dependencies for the repository
""" """
database.dependencies_insert(Dependencies(package_ahriman.base, {"usr": ["python"]})) database.dependencies_insert(Dependencies(package_ahriman.base, {Path("usr"): ["python"]}))
database.dependencies_insert(Dependencies(package_python_schedule.base, {"usr": ["filesystem"]})) database.dependencies_insert(Dependencies(package_python_schedule.base, {Path("usr"): ["filesystem"]}))
database.dependencies_insert(Dependencies(package_ahriman.base, {"usr": ["python3"]}), database.dependencies_insert(Dependencies(package_ahriman.base, {Path("usr"): ["python3"]}),
RepositoryId("i686", database._repository_id.name)) RepositoryId("i686", database._repository_id.name))
database.dependencies_remove(None) database.dependencies_remove(None)

View File

@ -155,10 +155,10 @@ def test_updates_dependencies(update_handler: UpdateHandler, package_ahriman: Pa
return_value=[package_ahriman, package_python_schedule]) return_value=[package_ahriman, package_python_schedule])
dependencies = { dependencies = {
package_ahriman.base: [ package_ahriman.base: [
Dependencies(package_ahriman.base, {"usr/lib/python3.11/site-packages": ["python"]}) Dependencies(package_ahriman.base, {Path("usr/lib/python3.11/site-packages"): ["python"]})
], ],
package_python_schedule.base: [ package_python_schedule.base: [
Dependencies(package_python_schedule.base, {"usr/lib/python3.12/site-packages": ["python"]}) Dependencies(package_python_schedule.base, {Path("usr/lib/python3.12/site-packages"): ["python"]})
], ],
} }
mocker.patch("ahriman.core.status.local_client.LocalClient.package_dependencies_get", mocker.patch("ahriman.core.status.local_client.LocalClient.package_dependencies_get",
@ -190,7 +190,7 @@ def test_updates_dependencies_partial(update_handler: UpdateHandler, package_ahr
""" """
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman]) mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
dependencies = [ dependencies = [
Dependencies(package_ahriman.base, {"usr": ["filesystem", "python"]}), Dependencies(package_ahriman.base, {Path("usr"): ["filesystem", "python"]}),
] ]
mocker.patch("ahriman.core.database.SQLite.dependencies_get", return_value=dependencies) mocker.patch("ahriman.core.database.SQLite.dependencies_get", return_value=dependencies)
mocker.patch("ahriman.core.alpm.pacman.Pacman.files", return_value={ mocker.patch("ahriman.core.alpm.pacman.Pacman.files", return_value={

View File

@ -1,10 +0,0 @@
from ahriman.models.dependencies import Dependencies
from ahriman.models.package import Package
def test_from_json_view(package_ahriman: Package) -> None:
"""
must construct and serialize dependencies to json
"""
dependencies = Dependencies(package_ahriman.base, {"/usr/bin/python3": ["python"]})
assert Dependencies.from_json(dependencies.view()) == dependencies

View File

@ -51,14 +51,6 @@ def test_from_env() -> None:
assert PkgbuildPatch.from_env("KEY") == PkgbuildPatch("KEY", "") assert PkgbuildPatch.from_env("KEY") == PkgbuildPatch("KEY", "")
def test_from_json_view() -> None:
"""
must correctly serialize to json
"""
patch = PkgbuildPatch("key", "value")
assert PkgbuildPatch.from_json(patch.view()) == patch
def test_parse() -> None: def test_parse() -> None:
""" """
must parse string correctly must parse string correctly
@ -132,6 +124,13 @@ def test_serialize_list() -> None:
assert PkgbuildPatch("key", ["val'ue", "val\"ue2"]).serialize() == """key=('val'"'"'ue' 'val"ue2')""" assert PkgbuildPatch("key", ["val'ue", "val\"ue2"]).serialize() == """key=('val'"'"'ue' 'val"ue2')"""
def test_view() -> None:
"""
must correctly serialize to json
"""
assert PkgbuildPatch("key", "value").view() == {"key": "key", "value": "value"}
def test_write(mocker: MockerFixture) -> None: def test_write(mocker: MockerFixture) -> None:
""" """
must write serialized value to the file must write serialized value to the file