Compare commits

..

3 Commits

10 changed files with 72 additions and 16 deletions

View File

@@ -31,16 +31,16 @@ export default function PackageCountBarChart({ stats }: PackageCountBarChartProp
data={{
labels: ["packages"],
datasets: [
{
label: "archives",
data: [stats.packages ?? 0],
backgroundColor: blue[500],
},
{
label: "bases",
data: [stats.bases ?? 0],
backgroundColor: indigo[300],
},
{
label: "archives",
data: [stats.packages ?? 0],
backgroundColor: blue[500],
},
],
}}
options={{
@@ -48,7 +48,7 @@ export default function PackageCountBarChart({ stats }: PackageCountBarChartProp
responsive: true,
scales: {
x: { stacked: true },
y: { stacked: true },
y: { stacked: false },
},
}}
/>;

View File

@@ -86,12 +86,12 @@ export default function DashboardDialog({ open, onClose }: DashboardDialogProps)
<Grid container spacing={2} sx={{ mt: 2 }}>
<Grid size={{ xs: 12, md: 6 }}>
<Box sx={{ maxHeight: 300 }}>
<Box sx={{ height: 300 }}>
<PackageCountBarChart stats={status.stats} />
</Box>
</Grid>
<Grid size={{ xs: 12, md: 6 }}>
<Box sx={{ maxHeight: 300, display: "flex", justifyContent: "center", alignItems: "center" }}>
<Box sx={{ height: 300, display: "flex", justifyContent: "center", alignItems: "center" }}>
<StatusPieChart counters={status.packages} />
</Box>
</Grid>

View File

@@ -138,8 +138,14 @@ class PacmanDatabase(SyncHttpClient):
Args:
force(bool): force database synchronization (same as ``pacman -Syy``)
Raises:
PacmanError: on operation error (invalid scheme or incomplete configuration)
"""
server = next(iter(self.database.servers))
try:
server = next(iter(self.database.servers))
except StopIteration:
raise PacmanError("No configured servers available for database") from None
filename = f"{self.database.name}.files.tar.gz"
url = f"{server}/{filename}"

View File

@@ -116,6 +116,19 @@ class GitRemoteError(RuntimeError):
RuntimeError.__init__(self, "Git remote failed")
class GPGError(RuntimeError):
"""
PGP/GPG related exception
"""
def __init__(self, details: str) -> None:
"""
Args:
details(str): details of the exception
"""
RuntimeError.__init__(self, f"GPG operation failed: {details}")
class InitializeError(RuntimeError):
"""
base service initialization exception

View File

@@ -86,6 +86,11 @@ class ArchiveRotationTrigger(Trigger):
package(Package): package which has been updated to check for older versions
pacman(Pacman): alpm wrapper instance
"""
# explicit guard to skip process in case if rotation is disabled
# this guard is supposed to speedup process
if self.keep_built_packages == 0:
return
packages: dict[tuple[str, str], Package] = {}
# we can't use here load_archives, because it ignores versions
for full_path in filter(package_like, self.paths.archive_for(package.base).iterdir()):
@@ -94,7 +99,7 @@ class ArchiveRotationTrigger(Trigger):
comparator: Callable[[Package, Package], int] = lambda left, right: left.vercmp(right.version)
to_remove = sorted(packages.values(), key=cmp_to_key(comparator))
# 0 will implicitly be translated into [:0], meaning we keep all packages
for single in to_remove[:-self.keep_built_packages]:
self.logger.info("removing version %s of package %s", single.version, single.base)
for archive in single.packages.values():

View File

@@ -20,7 +20,7 @@
from pathlib import Path
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import BuildError
from ahriman.core.exceptions import BuildError, GPGError
from ahriman.core.http import SyncHttpClient
from ahriman.core.utils import check_output
from ahriman.models.sign_settings import SignSettings
@@ -147,12 +147,19 @@ class GPG(SyncHttpClient):
Returns:
str: full PGP key fingerprint
Raises:
GPGError: if key is in wrong format
"""
metadata = check_output("gpg", "--with-colons", "--fingerprint", key, logger=self.logger)
# fingerprint line will be like
# fpr:::::::::43A663569A07EE1E4ECC55CC7E3A4240CE3C45C2:
fingerprint = next(filter(lambda line: line[:3] == "fpr", metadata.splitlines()))
return fingerprint.split(":")[-2]
metadata = check_output("gpg", "--with-colons", "--fingerprint", key, logger=self.logger)
try:
fingerprint = next(filter(lambda line: line[:3] == "fpr", metadata.splitlines()))
return fingerprint.split(":")[-2]
except (IndexError, StopIteration):
raise GPGError(f"key {key} has invalid metadata") from None
def key_import(self, server: str, key: str) -> None:
"""

View File

@@ -88,8 +88,12 @@ class User:
"""
if not self.password:
return None
algo = next(segment for segment in self.password.split("$") if segment)
return f"${algo}$"
try:
algo = next(segment for segment in self.password.split("$") if segment)
return f"${algo}$"
except StopIteration:
return None
@staticmethod
def generate_password(length: int) -> str:

View File

@@ -183,6 +183,15 @@ def test_sync_files_local(pacman_database: PacmanDatabase, mocker: MockerFixture
copy_mock.assert_called_once_with(Path("/var/core.files.tar.gz"), pytest.helpers.anyvar(int))
def test_sync_files_no_servers(pacman_database: PacmanDatabase) -> None:
"""
must raise PacmanError if no servers are configured
"""
pacman_database.database.servers = []
with pytest.raises(PacmanError):
pacman_database.sync_files(force=False)
def test_sync_files_unknown_source(pacman_database: PacmanDatabase) -> None:
"""
must raise an exception in case if server scheme is unsupported

View File

@@ -5,6 +5,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import GPGError
from ahriman.core.sign.gpg import GPG
from ahriman.models.sign_settings import SignSettings
@@ -113,6 +114,15 @@ fpr:::::::::43A663569A07EE1E4ECC55CC7E3A4240CE3C45C2:""")
check_output_mock.assert_called_once_with("gpg", "--with-colons", "--fingerprint", key, logger=gpg.logger)
def test_key_fingerprint_invalid(gpg: GPG, mocker: MockerFixture) -> None:
"""
must raise GPGError if no fingerprint found in output
"""
mocker.patch("ahriman.core.sign.gpg.check_output", return_value="no fingerprint here")
with pytest.raises(GPGError):
gpg.key_fingerprint("0xCE3C45C2")
def test_key_import(gpg: GPG, mocker: MockerFixture) -> None:
"""
must import PGP key from the server

View File

@@ -12,6 +12,8 @@ def test_algo() -> None:
"""
assert User(username="user", password=None, access=UserAccess.Read).algo is None
assert User(username="user", password="", access=UserAccess.Read).algo is None
assert User(username="user", password="$$$", access=UserAccess.Read).algo is None
assert User(
username="user",
password="$6$rounds=656000$mWBiecMPrHAL1VgX$oU4Y5HH8HzlvMaxwkNEJjK13ozElyU1wAHBoO/WW5dAaE4YEfnB0X3FxbynKMl4FBdC3Ovap0jINz4LPkNADg0",