Compare commits

..

2 Commits

15 changed files with 124 additions and 99 deletions

View File

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

View File

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

View File

@ -76,45 +76,17 @@ class Watcher(LazyLogging):
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 package_add(self, package: Package, status: BuildStatusEnum) -> None:
"""
extract logs for the package base
update package
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
package(Package): package description
status(BuildStatusEnum): new build status
"""
_ = self.package_get(package_base)
return self.client.package_logs_get(package_base, limit, offset)
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)
with self._lock:
self._known[package.base] = (package, BuildStatus(status))
self.client.package_add(package, status)
def package_changes_get(self, package_base: str) -> Changes:
"""
@ -129,6 +101,17 @@ class Watcher(LazyLogging):
_ = self.package_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:
"""
retrieve package dependencies
@ -175,34 +158,47 @@ class Watcher(LazyLogging):
except KeyError:
raise UnknownPackageError(package_base) from None
def package_remove(self, package_base: str) -> None:
def package_logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]:
"""
remove package base from known list if any
extract logs for the package base
Args:
package_base(str): package base
"""
with self._lock:
self._known.pop(package_base, None)
self.client.package_remove(package_base)
self.logs_remove(package_base, None)
limit(int, optional): limit records to the specified count, -1 means unlimited (Default value = -1)
offset(int, optional): records offset (Default value = 0)
def package_update(self, package_base: str, status: BuildStatusEnum, package: Package | None) -> None:
Returns:
list[tuple[float, str]]: package logs
"""
update package status and description
_ = self.package_get(package_base)
return self.client.package_logs_get(package_base, limit, offset)
def package_logs_remove(self, package_base: str, version: str | None) -> None:
"""
remove package related logs
Args:
package_base(str): package base to update
status(BuildStatusEnum): new build status
package(Package | None): optional package description. In case if not set current properties will be used
package_base(str): package base
version(str): package 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)
self.client.package_logs_remove(package_base, version)
def patches_get(self, package_base: str, variable: str | None) -> list[PkgbuildPatch]:
def package_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.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
@ -217,7 +213,7 @@ class Watcher(LazyLogging):
# so here we skip checking if package exists or not
return self.client.package_patches_get(package_base, variable)
def patches_remove(self, package_base: str, variable: str) -> None:
def package_patches_remove(self, package_base: str, variable: str) -> None:
"""
remove package patch
@ -227,7 +223,7 @@ class Watcher(LazyLogging):
"""
self.client.package_patches_remove(package_base, variable)
def patches_update(self, package_base: str, patch: PkgbuildPatch) -> None:
def package_patches_update(self, package_base: str, patch: PkgbuildPatch) -> None:
"""
update package patch
@ -237,6 +233,31 @@ class Watcher(LazyLogging):
"""
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:
"""
update service status

View File

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

View File

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

View File

@ -113,6 +113,6 @@ class ChangesView(StatusViewGuard, BaseView):
raise HTTPBadRequest(reason=str(ex))
changes = Changes(last_commit_sha, change)
self.service().client.package_changes_set(package_base, changes)
self.service().package_changes_set(package_base, changes)
raise HTTPNoContent

View File

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

View File

@ -152,7 +152,10 @@ class PackageView(StatusViewGuard, BaseView):
raise HTTPBadRequest(reason=str(ex))
try:
self.service().package_update(package_base, status, package)
if package is None:
self.service().package_update(package_base, status)
else:
self.service().package_add(package, status)
except UnknownPackageError:
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"]
variable = self.request.match_info["patch"]
self.service().patches_remove(package_base, variable)
self.service().package_patches_remove(package_base, variable)
raise HTTPNoContent
@ -95,7 +95,7 @@ class PatchView(StatusViewGuard, BaseView):
package_base = self.request.match_info["package"]
variable = self.request.match_info["patch"]
patches = self.service().patches_get(package_base, variable)
patches = self.service().package_patches_get(package_base, variable)
selected = next((patch for patch in patches if patch.key == variable), None)
if selected is None:

View File

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

View File

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

View File

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

View File

@ -0,0 +1,10 @@
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,6 +51,14 @@ def test_from_env() -> None:
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:
"""
must parse string correctly
@ -124,13 +132,6 @@ def test_serialize_list() -> None:
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:
"""
must write serialized value to the file