diff --git a/.github/workflows/setup.sh b/.github/workflows/setup.sh
index a6acc660..67822439 100755
--- a/.github/workflows/setup.sh
+++ b/.github/workflows/setup.sh
@@ -10,7 +10,7 @@ echo -e '[arcanisrepo]\nServer = https://repo.arcanis.me/$arch\nSigLevel = Never
# refresh the image
pacman -Syu --noconfirm
# main dependencies
-pacman -Sy --noconfirm devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-srcinfo python-systemd sudo
+pacman -Sy --noconfirm devtools git pyalpm python-cerberus python-inflection python-passlib python-pyelftools python-requests python-srcinfo python-systemd sudo
# make dependencies
pacman -Sy --noconfirm --asdeps base-devel python-build python-flit python-installer python-tox python-wheel
# optional dependencies
diff --git a/Dockerfile b/Dockerfile
index baffd945..4607f1dc 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -32,7 +32,7 @@ RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build && \
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
-RUN pacman -Sy --noconfirm --asdeps devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-srcinfo && \
+RUN pacman -Sy --noconfirm --asdeps devtools git pyalpm python-cerberus python-inflection python-passlib python-pyelftools python-requests python-srcinfo && \
pacman -Sy --noconfirm --asdeps base-devel python-build python-flit python-installer python-wheel && \
pacman -Sy --noconfirm --asdeps breezy git mercurial python-aiohttp python-boto3 python-cryptography python-jinja python-systemd rsync subversion && \
runuser -u build -- install-aur-package python-aioauth-client python-webargs python-aiohttp-apispec-git python-aiohttp-cors \
diff --git a/docs/ahriman.core.alpm.rst b/docs/ahriman.core.alpm.rst
index e22baa36..1b218d86 100644
--- a/docs/ahriman.core.alpm.rst
+++ b/docs/ahriman.core.alpm.rst
@@ -20,6 +20,14 @@ ahriman.core.alpm.pacman module
:no-undoc-members:
:show-inheritance:
+ahriman.core.alpm.pacman\_database module
+-----------------------------------------
+
+.. automodule:: ahriman.core.alpm.pacman_database
+ :members:
+ :no-undoc-members:
+ :show-inheritance:
+
ahriman.core.alpm.repo module
-----------------------------
diff --git a/docs/ahriman.core.database.migrations.rst b/docs/ahriman.core.database.migrations.rst
index 30cb7d28..16fdf617 100644
--- a/docs/ahriman.core.database.migrations.rst
+++ b/docs/ahriman.core.database.migrations.rst
@@ -108,6 +108,14 @@ ahriman.core.database.migrations.m012\_last\_commit\_sha module
:no-undoc-members:
:show-inheritance:
+ahriman.core.database.migrations.m013\_dependencies module
+----------------------------------------------------------
+
+.. automodule:: ahriman.core.database.migrations.m013_dependencies
+ :members:
+ :no-undoc-members:
+ :show-inheritance:
+
Module contents
---------------
diff --git a/docs/ahriman.core.database.operations.rst b/docs/ahriman.core.database.operations.rst
index c2d38a94..326979c1 100644
--- a/docs/ahriman.core.database.operations.rst
+++ b/docs/ahriman.core.database.operations.rst
@@ -28,6 +28,14 @@ ahriman.core.database.operations.changes\_operations module
:no-undoc-members:
:show-inheritance:
+ahriman.core.database.operations.dependencies\_operations module
+----------------------------------------------------------------
+
+.. automodule:: ahriman.core.database.operations.dependencies_operations
+ :members:
+ :no-undoc-members:
+ :show-inheritance:
+
ahriman.core.database.operations.logs\_operations module
--------------------------------------------------------
diff --git a/docs/ahriman.models.rst b/docs/ahriman.models.rst
index 461c044e..06626aa9 100644
--- a/docs/ahriman.models.rst
+++ b/docs/ahriman.models.rst
@@ -60,6 +60,14 @@ ahriman.models.counters module
:no-undoc-members:
:show-inheritance:
+ahriman.models.dependencies module
+----------------------------------
+
+.. automodule:: ahriman.models.dependencies
+ :members:
+ :no-undoc-members:
+ :show-inheritance:
+
ahriman.models.internal\_status module
--------------------------------------
@@ -108,6 +116,14 @@ ahriman.models.package module
:no-undoc-members:
:show-inheritance:
+ahriman.models.package\_archive module
+--------------------------------------
+
+.. automodule:: ahriman.models.package_archive
+ :members:
+ :no-undoc-members:
+ :show-inheritance:
+
ahriman.models.package\_description module
------------------------------------------
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 5f5d1d16..c3011e4e 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -53,6 +53,7 @@ libalpm and AUR related configuration. Group name can refer to architecture, e.g
* ``mirror`` - package database mirror used by pacman for synchronization, string, required. This option supports standard pacman substitutions with ``$arch`` and ``$repo``. Note that the mentioned mirror should contain all repositories which are set by ``alpm.repositories`` option.
* ``repositories`` - list of pacman repositories, used for package search, space separated list of strings, required.
* ``root`` - root for alpm library, string, required. In the most cases it must point to the system root.
+* ``sync_files_database`` - download files database from mirror, boolean, required.
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands). If set to ``no``, databases must be synchronized manually.
``auth`` group
diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD
index 8564fbcb..311dd6d7 100644
--- a/package/archlinux/PKGBUILD
+++ b/package/archlinux/PKGBUILD
@@ -7,7 +7,7 @@ pkgdesc="ArcH linux ReposItory MANager"
arch=('any')
url="https://github.com/arcan1s/ahriman"
license=('GPL3')
-depends=('devtools>=1:1.0.0' 'git' 'pyalpm' 'python-cerberus' 'python-inflection' 'python-passlib' 'python-requests' 'python-srcinfo')
+depends=('devtools>=1:1.0.0' 'git' 'pyalpm' 'python-cerberus' 'python-inflection' 'python-passlib' 'python-pyelftools' 'python-requests' 'python-srcinfo')
makedepends=('python-build' 'python-flit' 'python-installer' 'python-wheel')
optdepends=('breezy: -bzr packages support'
'darcs: -darcs packages support'
diff --git a/package/share/ahriman/settings/ahriman.ini b/package/share/ahriman/settings/ahriman.ini
index 494ddc33..5b17b0d7 100644
--- a/package/share/ahriman/settings/ahriman.ini
+++ b/package/share/ahriman/settings/ahriman.ini
@@ -17,6 +17,8 @@ mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
repositories = core extra multilib
; Pacman's root directory. In the most cases it must point to the system root.
root = /
+; Sync files databases too, which is required by deep dependencies check
+sync_files_database = yes
; Use local packages cache. If this option is enabled, the service will be able to synchronize databases (available
; as additional option for some subcommands). If set to no, databases must be synchronized manually.
use_ahriman_cache = yes
diff --git a/package/share/bash-completion/completions/_ahriman b/package/share/bash-completion/completions/_ahriman
index e18c4d4b..24f86fdf 100644
--- a/package/share/bash-completion/completions/_ahriman
+++ b/package/share/bash-completion/completions/_ahriman
@@ -27,12 +27,12 @@ _shtab_ahriman_patch_list_option_strings=('-h' '--help' '-e' '--exit-code' '-v'
_shtab_ahriman_patch_remove_option_strings=('-h' '--help' '-v' '--variable')
_shtab_ahriman_patch_set_add_option_strings=('-h' '--help' '-t' '--track')
_shtab_ahriman_repo_backup_option_strings=('-h' '--help')
-_shtab_ahriman_repo_check_option_strings=('-h' '--help' '--changes' '--no-changes' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
-_shtab_ahriman_check_option_strings=('-h' '--help' '--changes' '--no-changes' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
+_shtab_ahriman_repo_check_option_strings=('-h' '--help' '--changes' '--no-changes' '--check-files' '--no-check-files' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
+_shtab_ahriman_check_option_strings=('-h' '--help' '--changes' '--no-changes' '--check-files' '--no-check-files' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_create_keyring_option_strings=('-h' '--help')
_shtab_ahriman_repo_create_mirrorlist_option_strings=('-h' '--help')
-_shtab_ahriman_repo_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '--dry-run' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '--partitions' '--no-partitions' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
-_shtab_ahriman_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '--dry-run' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '--partitions' '--no-partitions' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
+_shtab_ahriman_repo_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '--partitions' '--no-partitions' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
+_shtab_ahriman_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '--partitions' '--no-partitions' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
_shtab_ahriman_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
_shtab_ahriman_repo_remove_unknown_option_strings=('-h' '--help' '--dry-run')
@@ -47,8 +47,8 @@ _shtab_ahriman_repo_sync_option_strings=('-h' '--help')
_shtab_ahriman_sync_option_strings=('-h' '--help')
_shtab_ahriman_repo_tree_option_strings=('-h' '--help' '-p' '--partitions')
_shtab_ahriman_repo_triggers_option_strings=('-h' '--help')
-_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
-_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
+_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
+_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_service_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_repo_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
@@ -243,6 +243,8 @@ _shtab_ahriman_repo_check__h_nargs=0
_shtab_ahriman_repo_check___help_nargs=0
_shtab_ahriman_repo_check___changes_nargs=0
_shtab_ahriman_repo_check___no_changes_nargs=0
+_shtab_ahriman_repo_check___check_files_nargs=0
+_shtab_ahriman_repo_check___no_check_files_nargs=0
_shtab_ahriman_repo_check__e_nargs=0
_shtab_ahriman_repo_check___exit_code_nargs=0
_shtab_ahriman_repo_check___vcs_nargs=0
@@ -254,6 +256,8 @@ _shtab_ahriman_check__h_nargs=0
_shtab_ahriman_check___help_nargs=0
_shtab_ahriman_check___changes_nargs=0
_shtab_ahriman_check___no_changes_nargs=0
+_shtab_ahriman_check___check_files_nargs=0
+_shtab_ahriman_check___no_check_files_nargs=0
_shtab_ahriman_check__e_nargs=0
_shtab_ahriman_check___exit_code_nargs=0
_shtab_ahriman_check___vcs_nargs=0
@@ -270,6 +274,8 @@ _shtab_ahriman_repo_daemon___aur_nargs=0
_shtab_ahriman_repo_daemon___no_aur_nargs=0
_shtab_ahriman_repo_daemon___changes_nargs=0
_shtab_ahriman_repo_daemon___no_changes_nargs=0
+_shtab_ahriman_repo_daemon___check_files_nargs=0
+_shtab_ahriman_repo_daemon___no_check_files_nargs=0
_shtab_ahriman_repo_daemon___dependencies_nargs=0
_shtab_ahriman_repo_daemon___no_dependencies_nargs=0
_shtab_ahriman_repo_daemon___dry_run_nargs=0
@@ -291,6 +297,8 @@ _shtab_ahriman_daemon___aur_nargs=0
_shtab_ahriman_daemon___no_aur_nargs=0
_shtab_ahriman_daemon___changes_nargs=0
_shtab_ahriman_daemon___no_changes_nargs=0
+_shtab_ahriman_daemon___check_files_nargs=0
+_shtab_ahriman_daemon___no_check_files_nargs=0
_shtab_ahriman_daemon___dependencies_nargs=0
_shtab_ahriman_daemon___no_dependencies_nargs=0
_shtab_ahriman_daemon___dry_run_nargs=0
@@ -358,6 +366,8 @@ _shtab_ahriman_repo_update___aur_nargs=0
_shtab_ahriman_repo_update___no_aur_nargs=0
_shtab_ahriman_repo_update___changes_nargs=0
_shtab_ahriman_repo_update___no_changes_nargs=0
+_shtab_ahriman_repo_update___check_files_nargs=0
+_shtab_ahriman_repo_update___no_check_files_nargs=0
_shtab_ahriman_repo_update___dependencies_nargs=0
_shtab_ahriman_repo_update___no_dependencies_nargs=0
_shtab_ahriman_repo_update___dry_run_nargs=0
@@ -380,6 +390,8 @@ _shtab_ahriman_update___aur_nargs=0
_shtab_ahriman_update___no_aur_nargs=0
_shtab_ahriman_update___changes_nargs=0
_shtab_ahriman_update___no_changes_nargs=0
+_shtab_ahriman_update___check_files_nargs=0
+_shtab_ahriman_update___no_check_files_nargs=0
_shtab_ahriman_update___dependencies_nargs=0
_shtab_ahriman_update___no_dependencies_nargs=0
_shtab_ahriman_update___dry_run_nargs=0
diff --git a/package/share/man/man1/ahriman.1 b/package/share/man/man1/ahriman.1
index 4fbb102d..3f0559e0 100644
--- a/package/share/man/man1/ahriman.1
+++ b/package/share/man/man1/ahriman.1
@@ -447,7 +447,9 @@ backup repository settings and database
path of the output archive
.SH COMMAND \fI\,'ahriman repo\-check'\/\fR
-usage: ahriman repo\-check [\-h] [\-\-changes | \-\-no\-changes] [\-e] [\-\-vcs | \-\-no\-vcs] [\-y] [package ...]
+usage: ahriman repo\-check [\-h] [\-\-changes | \-\-no\-changes] [\-\-check\-files | \-\-no\-check\-files] [\-e] [\-\-vcs | \-\-no\-vcs]
+ [\-y]
+ [package ...]
check for packages updates. Same as repo\-update \-\-dry\-run \-\-no\-manual
@@ -460,6 +462,10 @@ filter check by package base
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
calculate changes from the latest known commit if available. Only applicable in dry run mode
+.TP
+\fB\-\-check\-files\fR, \fB\-\-no\-check\-files\fR
+enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories)
+
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
@@ -484,9 +490,9 @@ create package which contains list of available mirrors as set by configuration.
.SH COMMAND \fI\,'ahriman repo\-daemon'\/\fR
usage: ahriman repo\-daemon [\-h] [\-i INTERVAL] [\-\-aur | \-\-no\-aur] [\-\-changes | \-\-no\-changes]
- [\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run] [\-\-increment | \-\-no\-increment]
- [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-\-partitions | \-\-no\-partitions]
- [\-u USERNAME] [\-\-vcs | \-\-no\-vcs] [\-y]
+ [\-\-check\-files | \-\-no\-check\-files] [\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run]
+ [\-\-increment | \-\-no\-increment] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual]
+ [\-\-partitions | \-\-no\-partitions] [\-u USERNAME] [\-\-vcs | \-\-no\-vcs] [\-y]
start process which periodically will run update process
@@ -503,6 +509,10 @@ enable or disable checking for AUR updates
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
calculate changes from the latest known commit if available. Only applicable in dry run mode
+.TP
+\fB\-\-check\-files\fR, \fB\-\-no\-check\-files\fR
+enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories)
+
.TP
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
process missing package dependencies
@@ -649,9 +659,9 @@ run triggers on empty build result as configured by settings
instead of running all triggers as set by configuration, just process specified ones in order of mention
.SH COMMAND \fI\,'ahriman repo\-update'\/\fR
-usage: ahriman repo\-update [\-h] [\-\-aur | \-\-no\-aur] [\-\-changes | \-\-no\-changes] [\-\-dependencies | \-\-no\-dependencies]
- [\-\-dry\-run] [\-e] [\-\-increment | \-\-no\-increment] [\-\-local | \-\-no\-local]
- [\-\-manual | \-\-no\-manual] [\-u USERNAME] [\-\-vcs | \-\-no\-vcs] [\-y]
+usage: ahriman repo\-update [\-h] [\-\-aur | \-\-no\-aur] [\-\-changes | \-\-no\-changes] [\-\-check\-files | \-\-no\-check\-files]
+ [\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run] [\-e] [\-\-increment | \-\-no\-increment]
+ [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-u USERNAME] [\-\-vcs | \-\-no\-vcs] [\-y]
[package ...]
check for packages updates and run build process if requested
@@ -669,6 +679,10 @@ enable or disable checking for AUR updates
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
calculate changes from the latest known commit if available. Only applicable in dry run mode
+.TP
+\fB\-\-check\-files\fR, \fB\-\-no\-check\-files\fR
+enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories)
+
.TP
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
process missing package dependencies
diff --git a/package/share/zsh/site-functions/_ahriman b/package/share/zsh/site-functions/_ahriman
index eb2efb80..512bce76 100644
--- a/package/share/zsh/site-functions/_ahriman
+++ b/package/share/zsh/site-functions/_ahriman
@@ -120,6 +120,7 @@ _shtab_ahriman_aur_search_options=(
_shtab_ahriman_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
+ {--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
@@ -153,6 +154,7 @@ _shtab_ahriman_daemon_options=(
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
+ {--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{--increment,--no-increment}"[increment package release (pkgrel) on duplicate (default\: True)]:increment:"
@@ -322,6 +324,7 @@ _shtab_ahriman_repo_backup_options=(
_shtab_ahriman_repo_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
+ {--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
@@ -363,6 +366,7 @@ _shtab_ahriman_repo_daemon_options=(
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
+ {--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{--increment,--no-increment}"[increment package release (pkgrel) on duplicate (default\: True)]:increment:"
@@ -460,6 +464,7 @@ _shtab_ahriman_repo_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
+ {--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@@ -601,6 +606,7 @@ _shtab_ahriman_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
+ {--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
"--dry-run[just perform check for updates, same as check command (default\: False)]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
diff --git a/pyproject.toml b/pyproject.toml
index 47a96c7d..7cba70fd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -20,6 +20,7 @@ dependencies = [
"cerberus",
"inflection",
"passlib",
+ "pyelftools",
"requests",
"srcinfo",
]
diff --git a/src/ahriman/application/ahriman.py b/src/ahriman/application/ahriman.py
index 5673da68..1f4b5477 100644
--- a/src/ahriman/application/ahriman.py
+++ b/src/ahriman/application/ahriman.py
@@ -537,6 +537,9 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
"Only applicable in dry run mode",
action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
+ "(e.g. dynamically linked libraries or modules directories)",
+ action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("--vcs", help="fetch actual version of VCS packages",
action=argparse.BooleanOptionalAction, default=True)
@@ -605,6 +608,9 @@ def _set_repo_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
"Only applicable in dry run mode",
action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
+ "(e.g. dynamically linked libraries or modules directories)",
+ action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--dependencies", help="process missing package dependencies",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
@@ -826,6 +832,9 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
"Only applicable in dry run mode",
action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
+ "(e.g. dynamically linked libraries or modules directories)",
+ action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--dependencies", help="process missing package dependencies",
action=argparse.BooleanOptionalAction, default=True)
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
diff --git a/src/ahriman/application/application/application.py b/src/ahriman/application/application/application.py
index 90e2b535..4ec45a61 100644
--- a/src/ahriman/application/application/application.py
+++ b/src/ahriman/application/application/application.py
@@ -62,10 +62,13 @@ class Application(ApplicationPackages, ApplicationRepository):
"""
known_packages: set[str] = set()
# local set
+ # this action is not really needed in case if ``alpm.use_ahriman_cache`` set to yes, because pacman
+ # will eventually contain all the local packages
for base in self.repository.packages():
for package, properties in base.packages.items():
known_packages.add(package)
known_packages.update(properties.provides)
+ # known pacman databases
known_packages.update(self.repository.pacman.packages())
return known_packages
diff --git a/src/ahriman/application/application/application_repository.py b/src/ahriman/application/application/application_repository.py
index 49b30991..f4547c75 100644
--- a/src/ahriman/application/application/application_repository.py
+++ b/src/ahriman/application/application/application_repository.py
@@ -91,10 +91,7 @@ class ApplicationRepository(ApplicationProperties):
packages(Iterable[str]): only sign specified packages
"""
# copy to prebuilt directory
- for package in self.repository.packages():
- # no one requested this package
- if packages and package.base not in packages:
- continue
+ for package in self.repository.packages(packages):
for archive in package.packages.values():
if archive.filepath is None:
self.logger.warning("filepath is empty for %s", package.base)
@@ -179,7 +176,7 @@ class ApplicationRepository(ApplicationProperties):
return result
def updates(self, filter_packages: Iterable[str], *,
- aur: bool, local: bool, manual: bool, vcs: bool) -> list[Package]:
+ aur: bool, local: bool, manual: bool, vcs: bool, check_files: bool) -> list[Package]:
"""
get list of packages to run update process
@@ -189,6 +186,7 @@ class ApplicationRepository(ApplicationProperties):
local(bool): enable or disable checking of local packages for updates
manual(bool): include or exclude manual updates
vcs(bool): enable or disable checking of VCS packages
+ check_files(bool): check for broken dependencies
Returns:
list[Package]: list of out-of-dated packages
@@ -201,5 +199,7 @@ class ApplicationRepository(ApplicationProperties):
updates.update({package.base: package for package in self.repository.updates_local(vcs=vcs)})
if manual:
updates.update({package.base: package for package in self.repository.updates_manual()})
+ if check_files:
+ updates.update({package.base: package for package in self.repository.updates_dependencies(filter_packages)})
return [package for _, package in sorted(updates.items())]
diff --git a/src/ahriman/application/handlers/add.py b/src/ahriman/application/handlers/add.py
index 0c687028..ac185e1c 100644
--- a/src/ahriman/application/handlers/add.py
+++ b/src/ahriman/application/handlers/add.py
@@ -55,7 +55,7 @@ class Add(Handler):
if not args.now:
return
- packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False)
+ packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False, check_files=False)
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
packagers = Packagers(args.username, {package.base: package.packager for package in packages})
diff --git a/src/ahriman/application/handlers/backup.py b/src/ahriman/application/handlers/backup.py
index b0e4a1d9..17e03d3a 100644
--- a/src/ahriman/application/handlers/backup.py
+++ b/src/ahriman/application/handlers/backup.py
@@ -18,10 +18,10 @@
# along with this program. If not, see .
#
import argparse
-import pwd
+import tarfile
from pathlib import Path
-from tarfile import TarFile
+from pwd import getpwuid
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
@@ -49,7 +49,7 @@ class Backup(Handler):
report(bool): force enable or disable reporting
"""
backup_paths = Backup.get_paths(configuration)
- with TarFile(args.path, mode="w") as archive: # well we don't actually use compression
+ with tarfile.open(args.path, mode="w") as archive: # well we don't actually use compression
for backup_path in backup_paths:
archive.add(backup_path)
@@ -77,7 +77,7 @@ class Backup(Handler):
# gnupg home with imported keys
uid, _ = repository_paths.root_owner
- system_user = pwd.getpwuid(uid)
+ system_user = getpwuid(uid)
gnupg_home = Path(system_user.pw_dir) / ".gnupg"
if gnupg_home.is_dir():
paths.add(gnupg_home)
diff --git a/src/ahriman/application/handlers/restore.py b/src/ahriman/application/handlers/restore.py
index 6f07cd71..969d67fc 100644
--- a/src/ahriman/application/handlers/restore.py
+++ b/src/ahriman/application/handlers/restore.py
@@ -18,8 +18,7 @@
# along with this program. If not, see .
#
import argparse
-
-from tarfile import TarFile
+import tarfile
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
@@ -45,5 +44,5 @@ class Restore(Handler):
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
"""
- with TarFile(args.path) as archive:
- archive.extractall(path=args.output)
+ with tarfile.open(args.path) as archive:
+ archive.extractall(path=args.output) # nosec
diff --git a/src/ahriman/application/handlers/update.py b/src/ahriman/application/handlers/update.py
index 359394b1..490aeeee 100644
--- a/src/ahriman/application/handlers/update.py
+++ b/src/ahriman/application/handlers/update.py
@@ -48,7 +48,8 @@ class Update(Handler):
application = Application(repository_id, configuration, report=report, refresh_pacman_database=args.refresh)
application.on_start()
- packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs)
+ packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs,
+ check_files=args.check_files)
if args.dry_run: # some check specific actions
if args.changes: # generate changes if requested
application.changes(packages)
diff --git a/src/ahriman/core/alpm/pacman.py b/src/ahriman/core/alpm/pacman.py
index e6cedb59..66b52269 100644
--- a/src/ahriman/core/alpm/pacman.py
+++ b/src/ahriman/core/alpm/pacman.py
@@ -18,24 +18,31 @@
# along with this program. If not, see .
#
import shutil
+import tarfile
-from collections.abc import Callable, Generator
+from collections.abc import Generator, Iterable
from functools import cached_property
from pathlib import Path
-from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore[import-not-found]
+from pyalpm import DB, Handle, Package, SIG_DATABASE_OPTIONAL, SIG_PACKAGE_OPTIONAL # type: ignore[import-not-found]
from string import Template
+from ahriman.core.alpm.pacman_database import PacmanDatabase
from ahriman.core.configuration import Configuration
from ahriman.core.log import LazyLogging
from ahriman.core.util import trim_package
from ahriman.models.pacman_synchronization import PacmanSynchronization
from ahriman.models.repository_id import RepositoryId
-from ahriman.models.repository_paths import RepositoryPaths
class Pacman(LazyLogging):
"""
alpm wrapper
+
+ Attributes:
+ configuration(Configuration): configuration instance
+ refresh_database(PacmanSynchronization): synchronize local cache to remote
+ repository_id(RepositoryId): repository unique identifier
+ repository_path(RepositoryPaths): repository paths instance
"""
def __init__(self, repository_id: RepositoryId, configuration: Configuration, *,
@@ -48,8 +55,11 @@ class Pacman(LazyLogging):
configuration(Configuration): configuration instance
refresh_database(PacmanSynchronization): synchronize local cache to remote
"""
- self.__create_handle_fn: Callable[[], Handle] = lambda: self.__create_handle(
- repository_id, configuration, refresh_database=refresh_database)
+ self.configuration = configuration
+ self.repository_id = repository_id
+ self.repository_paths = configuration.repository_paths
+
+ self.refresh_database = refresh_database
@cached_property
def handle(self) -> Handle:
@@ -59,40 +69,39 @@ class Pacman(LazyLogging):
Returns:
Handle: generated pyalpm handle instance
"""
- return self.__create_handle_fn()
+ return self.__create_handle(refresh_database=self.refresh_database)
- def __create_handle(self, repository_id: RepositoryId, configuration: Configuration, *,
- refresh_database: PacmanSynchronization) -> Handle:
+ def __create_handle(self, *, refresh_database: PacmanSynchronization) -> Handle:
"""
create lazy handle function
Args:
- repository_id(RepositoryId): repository unique identifier
- configuration(Configuration): configuration instance
refresh_database(PacmanSynchronization): synchronize local cache to remote
Returns:
Handle: fully initialized pacman handle
"""
- root = configuration.getpath("alpm", "root")
- pacman_root = configuration.getpath("alpm", "database")
- use_ahriman_cache = configuration.getboolean("alpm", "use_ahriman_cache")
- mirror = configuration.get("alpm", "mirror")
- paths = configuration.repository_paths
- database_path = paths.pacman if use_ahriman_cache else pacman_root
+ pacman_root = self.configuration.getpath("alpm", "database")
+ use_ahriman_cache = self.configuration.getboolean("alpm", "use_ahriman_cache")
+ database_path = self.repository_paths.pacman if use_ahriman_cache else pacman_root
+ root = self.configuration.getpath("alpm", "root")
handle = Handle(str(root), str(database_path))
- for repository in configuration.getlist("alpm", "repositories"):
- database = self.database_init(handle, repository, mirror, repository_id.architecture)
- self.database_copy(handle, database, pacman_root, paths, use_ahriman_cache=use_ahriman_cache)
+
+ for repository in self.configuration.getlist("alpm", "repositories"):
+ database = self.database_init(handle, repository, self.repository_id.architecture)
+ self.database_copy(handle, database, pacman_root, use_ahriman_cache=use_ahriman_cache)
+
+ # install repository database too
+ local_database = self.database_init(handle, self.repository_id.name, self.repository_id.architecture)
+ self.database_copy(handle, local_database, pacman_root, use_ahriman_cache=use_ahriman_cache)
if use_ahriman_cache and refresh_database:
self.database_sync(handle, force=refresh_database == PacmanSynchronization.Force)
return handle
- def database_copy(self, handle: Handle, database: DB, pacman_root: Path, paths: RepositoryPaths, *,
- use_ahriman_cache: bool) -> None:
+ def database_copy(self, handle: Handle, database: DB, pacman_root: Path, *, use_ahriman_cache: bool) -> None:
"""
copy database from the operating system root to the ahriman local home
@@ -100,7 +109,6 @@ class Pacman(LazyLogging):
handle(Handle): pacman handle which will be used for database copying
database(DB): pacman database instance to be copied
pacman_root(Path): operating system pacman root
- paths(RepositoryPaths): repository paths instance
use_ahriman_cache(bool): use local ahriman cache instead of system one
"""
def repository_database(root: Path) -> Path:
@@ -122,30 +130,36 @@ class Pacman(LazyLogging):
return # database for some reason deos not exist
self.logger.info("copy pacman database from operating system root to ahriman's home")
shutil.copy(src, dst)
- paths.chown(dst)
+ self.repository_paths.chown(dst)
- def database_init(self, handle: Handle, repository: str, mirror: str, architecture: str) -> DB:
+ def database_init(self, handle: Handle, repository: str, architecture: str) -> DB:
"""
create database instance from pacman handler and set its properties
Args:
handle(Handle): pacman handle which will be used for database initializing
repository(str): pacman repository name (e.g. core)
- mirror(str): arch linux mirror url
architecture(str): repository architecture
Returns:
DB: loaded pacman database instance
"""
self.logger.info("loading pacman database %s", repository)
- database: DB = handle.register_syncdb(repository, SIG_PACKAGE)
+ database: DB = handle.register_syncdb(repository, SIG_DATABASE_OPTIONAL | SIG_PACKAGE_OPTIONAL)
- # replace variables in mirror address
- variables = {
- "arch": architecture,
- "repo": repository,
- }
- database.servers = [Template(mirror).safe_substitute(variables)]
+ if repository != self.repository_id.name:
+ mirror = self.configuration.get("alpm", "mirror")
+ # replace variables in mirror address
+ variables = {
+ "arch": architecture,
+ "repo": repository,
+ }
+ server = Template(mirror).safe_substitute(variables)
+ else:
+ # special case, same database, use local storage instead
+ server = f"file://{self.repository_paths.repository}"
+
+ database.servers = [server]
return database
@@ -160,13 +174,44 @@ class Pacman(LazyLogging):
self.logger.info("refresh ahriman's home pacman database (force refresh %s)", force)
transaction = handle.init_transaction()
for database in handle.get_syncdbs():
- try:
- database.update(force)
- except PyalpmError:
- self.logger.exception("exception during update %s", database.name)
+ PacmanDatabase(database, self.configuration).sync(force=force)
transaction.release()
- def package_get(self, package_name: str) -> Generator[Package, None, None]:
+ def files(self, packages: Iterable[str] | None = None) -> dict[str, set[Path]]:
+ """
+ extract list of known packages from the databases
+
+ Args:
+ packages(Iterable[str] | None, optional): filter by package names (Default value = None)
+
+ Returns:
+ dict[str, set[Path]]: map of package name to its list of files
+ """
+ packages = packages or []
+
+ def extract(tar: tarfile.TarFile) -> Generator[tuple[str, set[Path]], None, None]:
+ for descriptor in filter(lambda info: info.path.endswith("/files"), tar.getmembers()):
+ package, *_ = str(Path(descriptor.path).parent).rsplit("-", 2)
+ if packages and package not in packages:
+ continue # skip unused packages
+ content = tar.extractfile(descriptor)
+ if content is None:
+ continue
+ files = {Path(filename.decode("utf8").rstrip()) for filename in content.readlines()}
+
+ yield package, files
+
+ result: dict[str, set[Path]] = {}
+ for database in self.handle.get_syncdbs():
+ database_file = self.repository_paths.pacman / "sync" / f"{database.name}.files.tar.gz"
+ if not database_file.is_file():
+ continue # no database file found
+ with tarfile.open(database_file, "r:gz") as archive:
+ result.update(extract(archive))
+
+ return result
+
+ def package(self, package_name: str) -> Generator[Package, None, None]:
"""
retrieve list of the packages from the repository by name
diff --git a/src/ahriman/core/alpm/pacman_database.py b/src/ahriman/core/alpm/pacman_database.py
new file mode 100644
index 00000000..3c99f07c
--- /dev/null
+++ b/src/ahriman/core/alpm/pacman_database.py
@@ -0,0 +1,170 @@
+#
+# 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 .
+#
+import os
+import shutil
+
+from email.utils import parsedate_to_datetime
+from pathlib import Path
+from pyalpm import DB # type: ignore[import-not-found]
+from urllib.parse import urlparse
+
+from ahriman.core.configuration import Configuration
+from ahriman.core.exceptions import PacmanError
+from ahriman.core.http import SyncHttpClient
+
+
+class PacmanDatabase(SyncHttpClient):
+ """
+ implementation for database sync, because pyalpm is not always enough
+
+ Attributes:
+ LAST_MODIFIED_HEADER(str): last modified header name
+ database(DB): pyalpm database object
+ repository_paths(RepositoryPaths): repository paths instance
+ sync_files_database(bool): sync files database
+ """
+
+ LAST_MODIFIED_HEADER = "Last-Modified"
+
+ def __init__(self, database: DB, configuration: Configuration) -> None:
+ """
+ default constructor
+
+ Args:
+ database(DB): pyalpm database object
+ configuration(Configuration): configuration instance
+ """
+ SyncHttpClient.__init__(self)
+ self.timeout = None # reset timeout
+
+ self.database = database
+ self.repository_paths = configuration.repository_paths
+
+ self.sync_files_database = configuration.getboolean("alpm", "sync_files_database")
+
+ def copy(self, remote_path: Path, local_path: Path) -> None:
+ """
+ copy local database file
+
+ Args:
+ remote_path(Path): path to source (remote) file
+ local_path(Path): path to locally stored file
+ """
+ shutil.copy(remote_path, local_path)
+
+ def download(self, url: str, local_path: Path) -> None:
+ """
+ download remote file and store it to local path with the correct last modified headers
+
+ Args:
+ url(str): remote url to request file
+ local_path(Path): path to locally stored file
+
+ Raises:
+ PacmanError: in case if no last-modified header was found
+ """
+ response = self.make_request("GET", url, stream=True)
+ if self.LAST_MODIFIED_HEADER not in response.headers:
+ raise PacmanError("No last-modified header found")
+
+ with local_path.open("wb") as local_file:
+ for chunk in response.iter_content(chunk_size=1024):
+ local_file.write(chunk)
+
+ # set correct (a,m)time for the file
+ remote_changed = parsedate_to_datetime(response.headers[self.LAST_MODIFIED_HEADER]).timestamp()
+ os.utime(local_path, (remote_changed, remote_changed))
+
+ def is_outdated(self, url: str, local_path: Path) -> bool:
+ """
+ check if local file is outdated
+
+ Args:
+ url(str): remote url to request last modified header
+ local_path(Path): path to locally stored file
+
+ Returns:
+ bool: True in case if remote file is newer than local file
+
+ Raises:
+ PacmanError: in case if no last-modified header was found
+ """
+ if not local_path.is_file():
+ return True # no local file found, requires to update
+
+ response = self.make_request("HEAD", url)
+ if self.LAST_MODIFIED_HEADER not in response.headers:
+ raise PacmanError("No last-modified header found")
+
+ remote_changed = parsedate_to_datetime(response.headers["Last-Modified"]).timestamp()
+ local_changed = local_path.stat().st_mtime
+
+ return remote_changed > local_changed
+
+ def sync(self, *, force: bool) -> None:
+ """
+ sync packages and files databases
+
+ Args:
+ force(bool): force database synchronization (same as ``pacman -Syy``)
+ """
+ try:
+ self.sync_packages(force=force)
+ if self.sync_files_database:
+ self.sync_files(force=force)
+ except Exception:
+ self.logger.exception("exception during update %s", self.database.name)
+
+ def sync_files(self, *, force: bool) -> None:
+ """
+ sync files by using http request
+
+ Args:
+ force(bool): force database synchronization (same as ``pacman -Syy``)
+ """
+ server = next(iter(self.database.servers))
+ filename = f"{self.database.name}.files.tar.gz"
+ url = f"{server}/{filename}"
+
+ remote_uri = urlparse(url)
+ local_path = Path(self.repository_paths.pacman / "sync" / filename)
+
+ match remote_uri.scheme:
+ case "http" | "https":
+ if not force and not self.is_outdated(url, local_path):
+ return
+
+ self.download(url, local_path)
+
+ case "file":
+ # just copy file as it is relatively cheap operation, no need to check timestamps
+ self.copy(Path(remote_uri.path), local_path)
+
+ case other:
+ raise PacmanError(f"Unknown or unsupported URL scheme {other}")
+
+ def sync_packages(self, *, force: bool) -> None:
+ """
+ sync packages by using built-in pyalpm methods
+
+ Args:
+ force(bool): force database synchronization (same as ``pacman -Syy``)
+ """
+ self.database.update(force)
diff --git a/src/ahriman/core/alpm/remote/official_syncdb.py b/src/ahriman/core/alpm/remote/official_syncdb.py
index b1aaf150..e18facb3 100644
--- a/src/ahriman/core/alpm/remote/official_syncdb.py
+++ b/src/ahriman/core/alpm/remote/official_syncdb.py
@@ -56,6 +56,6 @@ class OfficialSyncdb(Official):
raise UnknownPackageError(package_name)
try:
- return next(AURPackage.from_pacman(package) for package in pacman.package_get(package_name))
+ return next(AURPackage.from_pacman(package) for package in pacman.package(package_name))
except StopIteration:
raise UnknownPackageError(package_name) from None
diff --git a/src/ahriman/core/configuration/schema.py b/src/ahriman/core/configuration/schema.py
index 0ce10901..f890ce73 100644
--- a/src/ahriman/core/configuration/schema.py
+++ b/src/ahriman/core/configuration/schema.py
@@ -89,6 +89,11 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"path_exists": True,
"path_type": "dir",
},
+ "sync_files_database": {
+ "type": "boolean",
+ "coerce": "boolean",
+ "required": True,
+ },
"use_ahriman_cache": {
"type": "boolean",
"coerce": "boolean",
diff --git a/src/ahriman/core/database/migrations/m013_dependencies.py b/src/ahriman/core/database/migrations/m013_dependencies.py
new file mode 100644
index 00000000..d6270c9c
--- /dev/null
+++ b/src/ahriman/core/database/migrations/m013_dependencies.py
@@ -0,0 +1,32 @@
+#
+# 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 .
+#
+__all__ = ["steps"]
+
+
+steps = [
+ """
+ create table package_dependencies (
+ package_base text not null,
+ repository text not null,
+ dependencies json not null,
+ unique (package_base, repository)
+ )
+ """,
+]
diff --git a/src/ahriman/core/database/operations/__init__.py b/src/ahriman/core/database/operations/__init__.py
index 579465fd..d89ef710 100644
--- a/src/ahriman/core/database/operations/__init__.py
+++ b/src/ahriman/core/database/operations/__init__.py
@@ -20,6 +20,7 @@
from ahriman.core.database.operations.auth_operations import AuthOperations
from ahriman.core.database.operations.build_operations import BuildOperations
from ahriman.core.database.operations.changes_operations import ChangesOperations
+from ahriman.core.database.operations.dependencies_operations import DependenciesOperations
from ahriman.core.database.operations.logs_operations import LogsOperations
from ahriman.core.database.operations.package_operations import PackageOperations
from ahriman.core.database.operations.patch_operations import PatchOperations
diff --git a/src/ahriman/core/database/operations/changes_operations.py b/src/ahriman/core/database/operations/changes_operations.py
index d5e4dac8..53fe6495 100644
--- a/src/ahriman/core/database/operations/changes_operations.py
+++ b/src/ahriman/core/database/operations/changes_operations.py
@@ -64,7 +64,7 @@ class ChangesOperations(Operations):
def changes_insert(self, package_base: str, changes: Changes, repository_id: RepositoryId | None = None) -> None:
"""
- insert packages to build queue
+ insert package changes
Args:
package_base(str): package base to insert
diff --git a/src/ahriman/core/database/operations/dependencies_operations.py b/src/ahriman/core/database/operations/dependencies_operations.py
new file mode 100644
index 00000000..e5ee99ac
--- /dev/null
+++ b/src/ahriman/core/database/operations/dependencies_operations.py
@@ -0,0 +1,124 @@
+#
+# 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 .
+#
+from pathlib import Path
+from sqlite3 import Connection
+
+from ahriman.core.database.operations.operations import Operations
+from ahriman.models.dependencies import Dependencies
+from ahriman.models.repository_id import RepositoryId
+
+
+class DependenciesOperations(Operations):
+ """
+ operations for dependencies table
+ """
+
+ def dependencies_get(self, package_base: str | None = None,
+ repository_id: RepositoryId | None = None) -> list[Dependencies]:
+ """
+ get dependencies for the specific package base if available
+
+ Args:
+ package_base(str | None): package base to search
+ repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
+
+ Returns:
+ Dependencies: changes for the package base if available
+ """
+ repository_id = repository_id or self._repository_id
+
+ def run(connection: Connection) -> list[Dependencies]:
+ return [
+ Dependencies(
+ row["package_base"],
+ {
+ Path(path): packages
+ for path, packages in row["dependencies"].items()
+ }
+ )
+ for row in connection.execute(
+ """
+ select package_base, dependencies from package_dependencies
+ where (:package_base is null or package_base = :package_base)
+ and repository = :repository
+ """,
+ {
+ "package_base": package_base,
+ "repository": repository_id.id,
+ }
+ )
+ ]
+
+ return self.with_connection(run)
+
+ def dependencies_insert(self, dependencies: Dependencies, repository_id: RepositoryId | None = None) -> None:
+ """
+ insert package dependencies
+
+ Args:
+ dependencies(Dependencies): package dependencies
+ 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_dependencies
+ (package_base, repository, dependencies)
+ values
+ (:package_base, :repository, :dependencies)
+ on conflict (package_base, repository) do update set
+ dependencies = :dependencies
+ """,
+ {
+ "package_base": dependencies.package_base,
+ "repository": repository_id.id,
+ "dependencies": {
+ str(path): packages
+ for path, packages in dependencies.paths.items()
+ }
+ })
+
+ return self.with_connection(run, commit=True)
+
+ def dependencies_remove(self, package_base: str | None, repository_id: RepositoryId | None = None) -> None:
+ """
+ remove packages dependencies
+
+ Args:
+ package_base(str | None): optional filter by package base
+ 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(
+ """
+ delete from package_dependencies
+ where (:package_base is null or package_base = :package_base)
+ and repository = :repository
+ """,
+ {
+ "package_base": package_base,
+ "repository": repository_id.id,
+ })
+
+ return self.with_connection(run, commit=True)
diff --git a/src/ahriman/core/database/sqlite.py b/src/ahriman/core/database/sqlite.py
index e503aad2..06bcd2bb 100644
--- a/src/ahriman/core/database/sqlite.py
+++ b/src/ahriman/core/database/sqlite.py
@@ -25,12 +25,19 @@ from typing import Self
from ahriman.core.configuration import Configuration
from ahriman.core.database.migrations import Migrations
-from ahriman.core.database.operations import AuthOperations, BuildOperations, ChangesOperations, LogsOperations, \
- PackageOperations, PatchOperations
+from ahriman.core.database.operations import AuthOperations, BuildOperations, ChangesOperations, \
+ DependenciesOperations, LogsOperations, PackageOperations, PatchOperations
# pylint: disable=too-many-ancestors
-class SQLite(AuthOperations, BuildOperations, ChangesOperations, LogsOperations, PackageOperations, PatchOperations):
+class SQLite(
+ AuthOperations,
+ BuildOperations,
+ ChangesOperations,
+ DependenciesOperations,
+ LogsOperations,
+ PackageOperations,
+ PatchOperations):
"""
wrapper for sqlite3 database
@@ -94,3 +101,21 @@ class SQLite(AuthOperations, BuildOperations, ChangesOperations, LogsOperations,
if configuration.getboolean("settings", "apply_migrations", fallback=True):
self.with_connection(lambda connection: Migrations.migrate(connection, configuration))
paths.chown(self.path)
+
+ def package_clear(self, package_base: str) -> None:
+ """
+ completely remove package from all tables
+
+ Args:
+ package_base(str): package base to remove
+
+ Examples:
+ This method completely removes the package from all tables and must be used, e.g. on package removal::
+
+ >>> database.package_clear("ahriman")
+ """
+ self.build_queue_clear(package_base)
+ self.patches_remove(package_base, [])
+ self.logs_remove(package_base, None)
+ self.changes_remove(package_base)
+ self.dependencies_remove(package_base)
diff --git a/src/ahriman/core/exceptions.py b/src/ahriman/core/exceptions.py
index 98a69d4b..9fbe845b 100644
--- a/src/ahriman/core/exceptions.py
+++ b/src/ahriman/core/exceptions.py
@@ -219,6 +219,21 @@ class PackageInfoError(RuntimeError):
RuntimeError.__init__(self, f"There are errors during reading package information: `{details}`")
+class PacmanError(RuntimeError):
+ """
+ exception in case of pacman operation errors
+ """
+
+ def __init__(self, details: Any) -> None:
+ """
+ default constructor
+
+ Args:
+ details(Any): error details
+ """
+ RuntimeError.__init__(self, f"Could not perform operation with pacman: `{details}`")
+
+
class PathError(ValueError):
"""
exception which will be raised on path which is not belong to root directory
diff --git a/src/ahriman/core/http/sync_http_client.py b/src/ahriman/core/http/sync_http_client.py
index a4551063..824a5897 100644
--- a/src/ahriman/core/http/sync_http_client.py
+++ b/src/ahriman/core/http/sync_http_client.py
@@ -38,7 +38,7 @@ class SyncHttpClient(LazyLogging):
Attributes:
auth(tuple[str, str] | None): HTTP basic auth object if set
suppress_errors(bool): suppress logging of request errors
- timeout(int): HTTP request timeout in seconds
+ timeout(int | None): HTTP request timeout in seconds
"""
def __init__(self, configuration: Configuration | None = None, section: str | None = None, *,
@@ -60,7 +60,7 @@ class SyncHttpClient(LazyLogging):
password = configuration.get(section, "password", fallback=None)
self.auth = (username, password) if username and password else None
- self.timeout = configuration.getint(section, "timeout", fallback=30)
+ self.timeout: int | None = configuration.getint(section, "timeout", fallback=30)
self.suppress_errors = suppress_errors
@cached_property
@@ -90,25 +90,27 @@ class SyncHttpClient(LazyLogging):
result: str = exception.response.text if exception.response is not None else ""
return result
- def make_request(self, method: Literal["DELETE", "GET", "POST", "PUT"], url: str, *,
+ def make_request(self, method: Literal["DELETE", "GET", "HEAD", "POST", "PUT"], url: str, *,
headers: dict[str, str] | None = None,
params: list[tuple[str, str]] | None = None,
data: Any | None = None,
json: dict[str, Any] | None = None,
files: dict[str, MultipartType] | None = None,
+ stream: bool | None = None,
session: requests.Session | None = None,
suppress_errors: bool | None = None) -> requests.Response:
"""
perform request with specified parameters
Args:
- method(Literal["DELETE", "GET", "POST", "PUT"]): HTTP method to call
+ method(Literal["DELETE", "GET", "HEAD", "POST", "PUT"]): HTTP method to call
url(str): remote url to call
headers(dict[str, str] | None, optional): request headers (Default value = None)
params(list[tuple[str, str]] | None, optional): request query parameters (Default value = None)
data(Any | None, optional): request raw data parameters (Default value = None)
json(dict[str, Any] | None, optional): request json parameters (Default value = None)
files(dict[str, MultipartType] | None, optional): multipart upload (Default value = None)
+ stream(bool | None, optional): handle response as stream (Default value = None)
session(requests.Session | None, optional): session object if any (Default value = None)
suppress_errors(bool | None, optional): suppress logging errors (e.g. if no web server available). If none
set, the instance-wide value will be used (Default value = None)
@@ -124,7 +126,7 @@ class SyncHttpClient(LazyLogging):
try:
response = session.request(method, url, params=params, data=data, headers=headers, files=files, json=json,
- auth=self.auth, timeout=self.timeout)
+ stream=stream, auth=self.auth, timeout=self.timeout)
response.raise_for_status()
return response
except requests.HTTPError as ex:
diff --git a/src/ahriman/core/repository/executor.py b/src/ahriman/core/repository/executor.py
index 0eab6a5e..43033ab1 100644
--- a/src/ahriman/core/repository/executor.py
+++ b/src/ahriman/core/repository/executor.py
@@ -29,6 +29,7 @@ from ahriman.core.repository.package_info import PackageInfo
from ahriman.core.util import safe_filename
from ahriman.models.changes import Changes
from ahriman.models.package import Package
+from ahriman.models.package_archive import PackageArchive
from ahriman.models.package_description import PackageDescription
from ahriman.models.packagers import Packagers
from ahriman.models.result import Result
@@ -77,6 +78,10 @@ class Executor(PackageInfo, Cleaner):
last_commit_sha = build_single(single, Path(dir_name), packager.packager_id)
# clear changes and update commit hash
self.reporter.package_changes_set(single.base, Changes(last_commit_sha))
+ # update dependencies list
+ dependencies = PackageArchive(self.paths.build_directory, single).depends_on()
+ self.database.dependencies_insert(dependencies)
+ # update result set
result.add_updated(single)
except Exception:
self.reporter.set_failed(single.base)
@@ -98,10 +103,7 @@ class Executor(PackageInfo, Cleaner):
def remove_base(package_base: str) -> None:
try:
self.paths.tree_clear(package_base) # remove all internal files
- self.database.build_queue_clear(package_base)
- self.database.patches_remove(package_base, [])
- self.database.logs_remove(package_base, None)
- self.database.changes_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:
self.logger.exception("could not remove base %s", package_base)
diff --git a/src/ahriman/core/repository/package_info.py b/src/ahriman/core/repository/package_info.py
index 2fa84639..56a6fef1 100644
--- a/src/ahriman/core/repository/package_info.py
+++ b/src/ahriman/core/repository/package_info.py
@@ -86,14 +86,21 @@ class PackageInfo(RepositoryProperties):
return Changes(last_commit_sha, changes)
- def packages(self) -> list[Package]:
+ def packages(self, filter_packages: Iterable[str] | None = None) -> list[Package]:
"""
generate list of repository packages
+ Args:
+ filter_packages(Iterable[str] | None, optional): filter packages list by specified only
+
Returns:
list[Package]: list of packages properties
"""
- return self.load_archives(filter(package_like, self.paths.repository.iterdir()))
+ packages = self.load_archives(filter(package_like, self.paths.repository.iterdir()))
+ if filter_packages:
+ packages = [package for package in packages if package.base in filter_packages]
+
+ return packages
def packages_built(self) -> list[Path]:
"""
diff --git a/src/ahriman/core/repository/update_handler.py b/src/ahriman/core/repository/update_handler.py
index 04882841..ca79f8b4 100644
--- a/src/ahriman/core/repository/update_handler.py
+++ b/src/ahriman/core/repository/update_handler.py
@@ -18,6 +18,7 @@
# along with this program. If not, see .
#
from collections.abc import Iterable
+from pathlib import Path
from ahriman.core.build_tools.sources import Sources
from ahriman.core.exceptions import UnknownPackageError
@@ -55,17 +56,13 @@ class UpdateHandler(PackageInfo, Cleaner):
continue
raise UnknownPackageError(package.base)
- local_versions = {package.base: package.version for package in self.packages()}
-
result: list[Package] = []
- for local in self.packages():
- with self.in_package_context(local.base, local_versions.get(local.base)):
+ for local in self.packages(filter_packages):
+ with self.in_package_context(local.base, local.version):
if not local.remote.is_remote:
continue # avoid checking local packages
if local.base in self.ignore_list:
continue
- if filter_packages and local.base not in filter_packages:
- continue
try:
remote = load_remote(local)
@@ -82,6 +79,47 @@ class UpdateHandler(PackageInfo, Cleaner):
return result
+ def updates_dependencies(self, filter_packages: Iterable[str]) -> list[Package]:
+ """
+ check packages which ae required to be rebuilt based on dynamic dependencies (e.g. linking, modules paths, etc.)
+
+ Args:
+ filter_packages(Iterable[str]): do not check every package just specified in the list
+
+ Returns:
+ list[Package]: list of packages for which there is breaking linking
+ """
+ def extract_files(lookup_packages: Iterable[str]) -> dict[Path, set[str]]:
+ database_files = self.pacman.files(lookup_packages)
+ files: dict[Path, set[str]] = {}
+ for package_name, package_files in database_files.items(): # invert map
+ for package_file in package_files:
+ files.setdefault(package_file, set()).add(package_name)
+
+ return files
+
+ dependencies = {dependency.package_base: dependency for dependency in self.database.dependencies_get()}
+
+ result: list[Package] = []
+ for package in self.packages(filter_packages):
+ if package.base not in dependencies:
+ continue # skip check if no package dependencies found
+
+ required = dependencies[package.base].paths
+ required_packages = {dep for dep_packages in required.values() for dep in dep_packages}
+ filesystem = extract_files(required_packages)
+
+ for path, packages in required.items():
+ found = filesystem.get(path, set())
+ if found.intersection(packages):
+ continue
+
+ # there are no packages found in filesystem with the same paths
+ result.append(package)
+ break
+
+ return result
+
def updates_local(self, *, vcs: bool) -> list[Package]:
"""
check local packages for updates
diff --git a/src/ahriman/models/dependencies.py b/src/ahriman/models/dependencies.py
new file mode 100644
index 00000000..fed89841
--- /dev/null
+++ b/src/ahriman/models/dependencies.py
@@ -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 .
+#
+from dataclasses import dataclass, field
+from pathlib import Path
+
+
+@dataclass(frozen=True)
+class Dependencies:
+ """
+ package paths 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
+ """
+
+ package_base: str
+ paths: dict[Path, list[str]] = field(default_factory=dict)
diff --git a/src/ahriman/models/package_archive.py b/src/ahriman/models/package_archive.py
new file mode 100644
index 00000000..dd82cb47
--- /dev/null
+++ b/src/ahriman/models/package_archive.py
@@ -0,0 +1,165 @@
+#
+# 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 .
+#
+from dataclasses import dataclass
+from elftools.elf.dynamic import DynamicSection
+from elftools.elf.elffile import ELFFile
+from pathlib import Path
+from typing import IO
+
+from ahriman.core.util import walk
+from ahriman.models.dependencies import Dependencies
+from ahriman.models.package import Package
+
+
+@dataclass
+class PackageArchive:
+ """
+ helper for package archives
+
+ Attributes:
+ package(Package): package descriptor
+ root(Path): path to root filesystem
+ """
+
+ root: Path
+ package: Package
+
+ @staticmethod
+ def dynamic_needed(binary_path: Path) -> list[str]:
+ """
+ extract dynamic libraries required by the specified file
+
+ Args:
+ binary_path(Path): path to library, file, etc
+
+ Returns:
+ list[str]: libraries which this file linked dynamically. Returns empty set in case if file is not
+ a binary or no dynamic section has been found
+ """
+ with binary_path.open("rb") as binary_file:
+ if not PackageArchive.is_elf(binary_file):
+ return []
+
+ elf_file = ELFFile(binary_file) # type: ignore[no-untyped-call]
+ dynamic_section = next(
+ (section for section in elf_file.iter_sections() # type: ignore[no-untyped-call]
+ if isinstance(section, DynamicSection)),
+ None)
+ if dynamic_section is None:
+ return []
+
+ return [
+ tag.needed
+ for tag in dynamic_section.iter_tags() # type: ignore[no-untyped-call]
+ if tag.entry.d_tag == "DT_NEEDED"
+ ]
+
+ @staticmethod
+ def is_elf(content: IO[bytes]) -> bool:
+ """
+ check if the content is actually elf file
+
+ Args:
+ content(IO[bytes]): content of the file
+
+ Returns:
+ bool: True in case if file has elf header and False otherwise
+ """
+ expected = b"\x7fELF"
+ length = len(expected)
+
+ magic_bytes = content.read(length)
+ content.seek(0) # reset reading position
+
+ return magic_bytes == expected
+
+ def depends_on(self) -> Dependencies:
+ """
+ extract packages and paths which are required for this package
+
+ Returns:
+ Dependencies: map of the package name to set of paths used by this package
+ """
+ dependencies, roots = self.depends_on_paths()
+
+ result: dict[Path, list[str]] = {}
+ for package, (directories, files) in self.installed_packages().items():
+ if package in self.package.packages:
+ continue # skip package itself
+
+ required_by = [directory for directory in directories if directory in roots]
+ required_by.extend(library for library in files if library.name in dependencies)
+
+ for path in required_by:
+ result.setdefault(path, []).append(package)
+
+ return Dependencies(self.package.base, result)
+
+ def depends_on_paths(self) -> tuple[set[str], set[Path]]:
+ """
+ extract dependencies from installation
+
+ Returns:
+ tuple[set[str], set[Path]]: tuple of dynamically linked libraries and directory paths
+ """
+ dependencies = set()
+ roots: set[Path] = set()
+
+ package_dir = self.root / "build" / self.package.base / "pkg"
+ for path in filter(lambda p: p.is_file(), walk(package_dir)):
+ dependencies.update(PackageArchive.dynamic_needed(path))
+ filesystem_path = Path(*path.relative_to(package_dir).parts[1:])
+ roots.update(filesystem_path.parents[:-1]) # last element is always . because paths are relative
+
+ return dependencies, roots
+
+ def installed_packages(self) -> dict[str, tuple[list[Path], list[Path]]]:
+ """
+ extract list of the installed packages and their content
+
+ Returns:
+ dict[str, tuple[list[Path], list[Path]]]; map of package name to list of directories and files contained
+ by this package
+ """
+ result = {}
+
+ pacman_local_files = self.root / "var" / "lib" / "pacman" / "local"
+ for path in filter(lambda fn: fn.name == "files", walk(pacman_local_files)):
+ package, *_ = path.parent.name.rsplit("-", 2)
+
+ directories, files = [], []
+ is_files = False
+ for line in path.read_text(encoding="utf8").splitlines():
+ if not line: # skip empty lines
+ continue
+ if line.startswith("%") and line.endswith("%"): # directive started
+ is_files = line == "%FILES%"
+ if not is_files: # not a files directive
+ continue
+
+ entry = Path(line)
+ if line.endswith("/"): # simple check if it is directory
+ directories.append(entry)
+ else:
+ files.append(entry)
+
+ result[package] = directories, files
+
+ return result
diff --git a/src/ahriman/models/repository_paths.py b/src/ahriman/models/repository_paths.py
index 35d97bff..462f6076 100644
--- a/src/ahriman/models/repository_paths.py
+++ b/src/ahriman/models/repository_paths.py
@@ -24,6 +24,7 @@ from collections.abc import Generator
from dataclasses import dataclass, field
from functools import cached_property
from pathlib import Path
+from pwd import getpwuid
from ahriman.core.exceptions import PathError
from ahriman.core.log import LazyLogging
@@ -83,6 +84,17 @@ class RepositoryPaths(LazyLogging):
return Path(self.repository_id.architecture) # legacy tree suffix
return Path(self.repository_id.name) / self.repository_id.architecture
+ @property
+ def build_directory(self) -> Path:
+ """
+ same as :attr:`chroot`, but exactly build chroot
+
+ Returns:
+ Path: path to directory in which build process is run
+ """
+ uid, _ = self.owner(self.root)
+ return self.chroot / f"{self.repository_id.name}-{self.repository_id.architecture}" / getpwuid(uid).pw_name
+
@property
def cache(self) -> Path:
"""
diff --git a/tests/ahriman/application/application/test_application_repository.py b/tests/ahriman/application/application/test_application_repository.py
index 21138e18..cfb68680 100644
--- a/tests/ahriman/application/application/test_application_repository.py
+++ b/tests/ahriman/application/application/test_application_repository.py
@@ -131,25 +131,6 @@ def test_sign_skip(application_repository: ApplicationRepository, package_ahrima
application_repository.sign([])
-def test_sign_specific(application_repository: ApplicationRepository, package_ahriman: Package,
- package_python_schedule: Package, mocker: MockerFixture) -> None:
- """
- must sign only specified packages
- """
- mocker.patch("ahriman.core.repository.repository.Repository.packages",
- return_value=[package_ahriman, package_python_schedule])
- sign_package_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_package")
- sign_repository_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_repository")
- on_result_mock = mocker.patch(
- "ahriman.application.application.application_repository.ApplicationRepository.on_result")
-
- filename = package_ahriman.packages[package_ahriman.base].filepath
- application_repository.sign([package_ahriman.base])
- sign_package_mock.assert_called_once_with(filename, None)
- sign_repository_mock.assert_called_once_with(application_repository.repository.repo.repo_path)
- on_result_mock.assert_called_once_with(Result())
-
-
def test_unknown_no_aur(application_repository: ApplicationRepository, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
@@ -239,11 +220,13 @@ def test_updates_all(application_repository: ApplicationRepository, package_ahri
return_value=[package_ahriman])
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+ updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
- application_repository.updates([], aur=True, local=True, manual=True, vcs=True)
+ application_repository.updates([], aur=True, local=True, manual=True, vcs=True, check_files=True)
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_called_once_with(vcs=True)
updates_manual_mock.assert_called_once_with()
+ updates_deps_mock.assert_called_once_with([])
def test_updates_disabled(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
@@ -253,11 +236,13 @@ def test_updates_disabled(application_repository: ApplicationRepository, mocker:
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+ updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
- application_repository.updates([], aur=False, local=False, manual=False, vcs=True)
+ application_repository.updates([], aur=False, local=False, manual=False, vcs=True, check_files=False)
updates_aur_mock.assert_not_called()
updates_local_mock.assert_not_called()
updates_manual_mock.assert_not_called()
+ updates_deps_mock.assert_not_called()
def test_updates_no_aur(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
@@ -267,11 +252,13 @@ def test_updates_no_aur(application_repository: ApplicationRepository, mocker: M
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+ updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
- application_repository.updates([], aur=False, local=True, manual=True, vcs=True)
+ application_repository.updates([], aur=False, local=True, manual=True, vcs=True, check_files=True)
updates_aur_mock.assert_not_called()
updates_local_mock.assert_called_once_with(vcs=True)
updates_manual_mock.assert_called_once_with()
+ updates_deps_mock.assert_called_once_with([])
def test_updates_no_local(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
@@ -281,11 +268,13 @@ def test_updates_no_local(application_repository: ApplicationRepository, mocker:
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+ updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
- application_repository.updates([], aur=True, local=False, manual=True, vcs=True)
+ application_repository.updates([], aur=True, local=False, manual=True, vcs=True, check_files=True)
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_not_called()
updates_manual_mock.assert_called_once_with()
+ updates_deps_mock.assert_called_once_with([])
def test_updates_no_manual(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
@@ -295,11 +284,13 @@ def test_updates_no_manual(application_repository: ApplicationRepository, mocker
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+ updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
- application_repository.updates([], aur=True, local=True, manual=False, vcs=True)
+ application_repository.updates([], aur=True, local=True, manual=False, vcs=True, check_files=True)
updates_aur_mock.assert_called_once_with([], vcs=True)
updates_local_mock.assert_called_once_with(vcs=True)
updates_manual_mock.assert_not_called()
+ updates_deps_mock.assert_called_once_with([])
def test_updates_no_vcs(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
@@ -309,11 +300,29 @@ def test_updates_no_vcs(application_repository: ApplicationRepository, mocker: M
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+ updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
- application_repository.updates([], aur=True, local=True, manual=True, vcs=False)
+ application_repository.updates([], aur=True, local=True, manual=True, vcs=False, check_files=True)
updates_aur_mock.assert_called_once_with([], vcs=False)
updates_local_mock.assert_called_once_with(vcs=False)
updates_manual_mock.assert_called_once_with()
+ updates_deps_mock.assert_called_once_with([])
+
+
+def test_updates_no_check_files(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
+ """
+ must get updates without checking broken links
+ """
+ updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
+ updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
+ updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+ updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
+
+ application_repository.updates([], aur=True, local=True, manual=True, vcs=True, check_files=False)
+ updates_aur_mock.assert_called_once_with([], vcs=True)
+ updates_local_mock.assert_called_once_with(vcs=True)
+ updates_manual_mock.assert_called_once_with()
+ updates_deps_mock.assert_not_called()
def test_updates_with_filter(application_repository: ApplicationRepository, mocker: MockerFixture) -> None:
@@ -323,8 +332,10 @@ def test_updates_with_filter(application_repository: ApplicationRepository, mock
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local")
updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+ updates_deps_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_dependencies")
- application_repository.updates(["filter"], aur=True, local=True, manual=True, vcs=True)
+ application_repository.updates(["filter"], aur=True, local=True, manual=True, vcs=True, check_files=True)
updates_aur_mock.assert_called_once_with(["filter"], vcs=True)
updates_local_mock.assert_called_once_with(vcs=True)
updates_manual_mock.assert_called_once_with()
+ updates_deps_mock.assert_called_once_with(["filter"])
diff --git a/tests/ahriman/application/handlers/test_handler_add.py b/tests/ahriman/application/handlers/test_handler_add.py
index 0be3a281..cfb640bc 100644
--- a/tests/ahriman/application/handlers/test_handler_add.py
+++ b/tests/ahriman/application/handlers/test_handler_add.py
@@ -89,7 +89,8 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration
_, repository_id = configuration.check_loaded()
Add.run(args, repository_id, configuration, report=False)
- updates_mock.assert_called_once_with(args.package, aur=False, local=False, manual=True, vcs=False)
+ updates_mock.assert_called_once_with(args.package,
+ aur=False, local=False, manual=True, vcs=False, check_files=False)
application_mock.assert_called_once_with([package_ahriman],
Packagers(args.username, {package_ahriman.base: "packager"}),
bump_pkgrel=args.increment)
diff --git a/tests/ahriman/application/handlers/test_handler_backup.py b/tests/ahriman/application/handlers/test_handler_backup.py
index a1349694..19b90760 100644
--- a/tests/ahriman/application/handlers/test_handler_backup.py
+++ b/tests/ahriman/application/handlers/test_handler_backup.py
@@ -31,7 +31,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
mocker.patch("ahriman.application.handlers.Backup.get_paths", return_value=[Path("path")])
tarfile = MagicMock()
add_mock = tarfile.__enter__.return_value = MagicMock()
- mocker.patch("tarfile.TarFile.__new__", return_value=tarfile)
+ mocker.patch("ahriman.application.handlers.backup.tarfile.open", return_value=tarfile)
_, repository_id = configuration.check_loaded()
Backup.run(args, repository_id, configuration, report=False)
@@ -45,7 +45,7 @@ def test_get_paths(configuration: Configuration, mocker: MockerFixture) -> None:
# gnupg export mock
mocker.patch("pathlib.Path.is_dir", return_value=True)
mocker.patch.object(RepositoryPaths, "root_owner", (42, 42))
- getpwuid_mock = mocker.patch("pwd.getpwuid", return_value=MagicMock())
+ getpwuid_mock = mocker.patch("ahriman.application.handlers.backup.getpwuid", return_value=MagicMock())
# well database does not exist so we override it
database_mock = mocker.patch("ahriman.core.database.SQLite.database_path", return_value=configuration.path)
diff --git a/tests/ahriman/application/handlers/test_handler_restore.py b/tests/ahriman/application/handlers/test_handler_restore.py
index 3b2053cf..ee2697fb 100644
--- a/tests/ahriman/application/handlers/test_handler_restore.py
+++ b/tests/ahriman/application/handlers/test_handler_restore.py
@@ -30,7 +30,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
args = _default_args(args)
tarfile = MagicMock()
extract_mock = tarfile.__enter__.return_value = MagicMock()
- mocker.patch("tarfile.TarFile.__new__", return_value=tarfile)
+ mocker.patch("ahriman.application.handlers.restore.tarfile.open", return_value=tarfile)
_, repository_id = configuration.check_loaded()
Restore.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_update.py b/tests/ahriman/application/handlers/test_handler_update.py
index 8863de95..0809f171 100644
--- a/tests/ahriman/application/handlers/test_handler_update.py
+++ b/tests/ahriman/application/handlers/test_handler_update.py
@@ -25,6 +25,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
"""
args.aur = True
args.changes = True
+ args.check_files = True
args.package = []
args.dependencies = True
args.dry_run = False
@@ -61,7 +62,8 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
application_mock.assert_called_once_with([package_ahriman],
Packagers(args.username, {package_ahriman.base: "packager"}),
bump_pkgrel=args.increment)
- updates_mock.assert_called_once_with(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs)
+ updates_mock.assert_called_once_with(
+ args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files)
changes_mock.assert_not_called()
dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies)
check_mock.assert_called_once_with(False, False)
@@ -122,7 +124,8 @@ def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configu
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
- updates_mock.assert_called_once_with(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs)
+ updates_mock.assert_called_once_with(
+ args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files)
application_mock.assert_not_called()
changes_mock.assert_called_once_with([package_ahriman])
check_mock.assert_called_once_with(False, pytest.helpers.anyvar(int))
diff --git a/tests/ahriman/conftest.py b/tests/ahriman/conftest.py
index f8e77228..878b9c12 100644
--- a/tests/ahriman/conftest.py
+++ b/tests/ahriman/conftest.py
@@ -451,6 +451,7 @@ def passwd() -> MagicMock:
"""
passwd = MagicMock()
passwd.pw_dir = "home"
+ passwd.pw_name = "ahriman"
return passwd
diff --git a/tests/ahriman/core/alpm/conftest.py b/tests/ahriman/core/alpm/conftest.py
new file mode 100644
index 00000000..9447b9a6
--- /dev/null
+++ b/tests/ahriman/core/alpm/conftest.py
@@ -0,0 +1,21 @@
+import pytest
+
+from ahriman.core.alpm.pacman_database import PacmanDatabase
+from ahriman.core.alpm.pacman import Pacman
+from ahriman.core.configuration import Configuration
+
+
+@pytest.fixture
+def pacman_database(configuration: Configuration, pacman: Pacman) -> PacmanDatabase:
+ """
+ database sync fixture
+
+ Args:
+ configuration(Configuration): configuration test instance
+ pacman(Pacman): pacman test instance
+
+ Returns:
+ DatabaseSync: database sync test instance
+ """
+ database = next(iter(pacman.handle.get_syncdbs()))
+ return PacmanDatabase(database, configuration)
diff --git a/tests/ahriman/core/alpm/remote/test_official_syncdb.py b/tests/ahriman/core/alpm/remote/test_official_syncdb.py
index e293c360..c9accfd5 100644
--- a/tests/ahriman/core/alpm/remote/test_official_syncdb.py
+++ b/tests/ahriman/core/alpm/remote/test_official_syncdb.py
@@ -14,7 +14,7 @@ def test_package_info(official_syncdb: OfficialSyncdb, aur_package_akonadi: AURP
must return package info from the database
"""
mocker.patch("ahriman.models.aur_package.AURPackage.from_pacman", return_value=aur_package_akonadi)
- get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.package_get", return_value=[aur_package_akonadi])
+ get_mock = mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[aur_package_akonadi])
package = official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)
get_mock.assert_called_once_with(aur_package_akonadi.name)
@@ -26,7 +26,7 @@ def test_package_info_no_pacman(official_syncdb: OfficialSyncdb, aur_package_ako
"""
must raise UnknownPackageError if no pacman set
"""
- mocker.patch("ahriman.core.alpm.pacman.Pacman.package_get", return_value=[aur_package_akonadi])
+ mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[aur_package_akonadi])
with pytest.raises(UnknownPackageError, match=aur_package_akonadi.name):
official_syncdb.package_info(aur_package_akonadi.name, pacman=None)
@@ -37,6 +37,6 @@ def test_package_info_not_found(official_syncdb: OfficialSyncdb, aur_package_ako
"""
must raise UnknownPackage exception in case if no package was found
"""
- mocker.patch("ahriman.core.alpm.pacman.Pacman.package_get", return_value=[])
+ mocker.patch("ahriman.core.alpm.pacman.Pacman.package", return_value=[])
with pytest.raises(UnknownPackageError, match=aur_package_akonadi.name):
assert official_syncdb.package_info(aur_package_akonadi.name, pacman=pacman)
diff --git a/tests/ahriman/core/alpm/test_pacman.py b/tests/ahriman/core/alpm/test_pacman.py
index 4bcdb828..d0dce96a 100644
--- a/tests/ahriman/core/alpm/test_pacman.py
+++ b/tests/ahriman/core/alpm/test_pacman.py
@@ -1,13 +1,14 @@
import pytest
+import tarfile
from pathlib import Path
-from pyalpm import error as PyalpmError
from pytest_mock import MockerFixture
from tempfile import TemporaryDirectory
-from unittest.mock import MagicMock
+from unittest.mock import MagicMock, call as MockCall
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.configuration import Configuration
+from ahriman.models.package import Package
from ahriman.models.pacman_synchronization import PacmanSynchronization
from ahriman.models.repository_paths import RepositoryPaths
@@ -48,7 +49,7 @@ def test_init_with_local_cache_forced(configuration: Configuration, mocker: Mock
sync_mock.assert_called_once_with(pytest.helpers.anyvar(int), force=True)
-def test_database_copy(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
+def test_database_copy(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must copy database from root
"""
@@ -62,13 +63,13 @@ def test_database_copy(pacman: Pacman, repository_paths: RepositoryPaths, mocker
copy_mock = mocker.patch("shutil.copy")
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
- pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
+ pacman.database_copy(pacman.handle, database, path, use_ahriman_cache=True)
mkdir_mock.assert_called_once_with(mode=0o755, exist_ok=True)
copy_mock.assert_called_once_with(path / "sync" / "core.db", dst_path)
chown_mock.assert_called_once_with(dst_path)
-def test_database_copy_skip(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
+def test_database_copy_skip(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database from root if local cache is disabled
"""
@@ -79,11 +80,11 @@ def test_database_copy_skip(pacman: Pacman, repository_paths: RepositoryPaths, m
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p.is_relative_to(path))
copy_mock = mocker.patch("shutil.copy")
- pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=False)
+ pacman.database_copy(pacman.handle, database, path, use_ahriman_cache=False)
copy_mock.assert_not_called()
-def test_database_copy_no_directory(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
+def test_database_copy_no_directory(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database if local cache already exists
"""
@@ -94,11 +95,11 @@ def test_database_copy_no_directory(pacman: Pacman, repository_paths: Repository
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p.is_relative_to(path))
copy_mock = mocker.patch("shutil.copy")
- pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
+ pacman.database_copy(pacman.handle, database, path, use_ahriman_cache=True)
copy_mock.assert_not_called()
-def test_database_copy_no_root_file(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
+def test_database_copy_no_root_file(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database if no repository file exists in filesystem
"""
@@ -109,11 +110,11 @@ def test_database_copy_no_root_file(pacman: Pacman, repository_paths: Repository
mocker.patch("pathlib.Path.is_file", return_value=False)
copy_mock = mocker.patch("shutil.copy")
- pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
+ pacman.database_copy(pacman.handle, database, path, use_ahriman_cache=True)
copy_mock.assert_not_called()
-def test_database_copy_database_exist(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
+def test_database_copy_database_exist(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must do not copy database if local cache already exists
"""
@@ -123,7 +124,7 @@ def test_database_copy_database_exist(pacman: Pacman, repository_paths: Reposito
mocker.patch("pathlib.Path.is_file", return_value=True)
copy_mock = mocker.patch("shutil.copy")
- pacman.database_copy(pacman.handle, database, Path("root"), repository_paths, use_ahriman_cache=True)
+ pacman.database_copy(pacman.handle, database, Path("root"), use_ahriman_cache=True)
copy_mock.assert_not_called()
@@ -131,71 +132,133 @@ def test_database_init(pacman: Pacman, configuration: Configuration) -> None:
"""
must init database with settings
"""
- mirror = configuration.get("alpm", "mirror")
- database = pacman.database_init(pacman.handle, "testing", mirror, "x86_64")
+ database = pacman.database_init(pacman.handle, "testing", "x86_64")
assert database.servers == ["https://geo.mirror.pkgbuild.com/testing/os/x86_64"]
-def test_database_sync(pacman: Pacman) -> None:
+def test_database_init_local(pacman: Pacman, configuration: Configuration) -> None:
+ """
+ must set file protocol for local databases
+ """
+ _, repository_id = configuration.check_loaded()
+ database = pacman.database_init(MagicMock(), repository_id.name, repository_id.architecture)
+ assert database.servers == [f"file://{configuration.repository_paths.repository}"]
+
+
+def test_database_sync(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must sync databases
"""
handle_mock = MagicMock()
- core_mock = MagicMock()
- extra_mock = MagicMock()
transaction_mock = MagicMock()
- handle_mock.get_syncdbs.return_value = [core_mock, extra_mock]
+ handle_mock.get_syncdbs.return_value = [1, 2]
handle_mock.init_transaction.return_value = transaction_mock
- pacman.handle = handle_mock
- pacman.database_sync(pacman.handle, force=False)
+ sync_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync")
+
+ pacman.database_sync(handle_mock, force=False)
handle_mock.init_transaction.assert_called_once_with()
- core_mock.update.assert_called_once_with(False)
- extra_mock.update.assert_called_once_with(False)
+ sync_mock.assert_has_calls([MockCall(force=False), MockCall(force=False)])
transaction_mock.release.assert_called_once_with()
-def test_database_sync_failed(pacman: Pacman) -> None:
- """
- must sync databases even if there was exception
- """
- handle_mock = MagicMock()
- core_mock = MagicMock()
- core_mock.update.side_effect = PyalpmError()
- extra_mock = MagicMock()
- handle_mock.get_syncdbs.return_value = [core_mock, extra_mock]
- pacman.handle = handle_mock
-
- pacman.database_sync(pacman.handle, force=False)
- extra_mock.update.assert_called_once_with(False)
-
-
-def test_database_sync_forced(pacman: Pacman) -> None:
+def test_database_sync_forced(pacman: Pacman, mocker: MockerFixture) -> None:
"""
must sync databases with force flag
"""
handle_mock = MagicMock()
- core_mock = MagicMock()
- handle_mock.get_syncdbs.return_value = [core_mock]
+ handle_mock.get_syncdbs.return_value = [1]
+
+ sync_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync")
+
+ pacman.database_sync(handle_mock, force=True)
+ sync_mock.assert_called_once_with(force=True)
+
+
+def test_files(pacman: Pacman, package_ahriman: Package, mocker: MockerFixture, resource_path_root: Path) -> None:
+ """
+ must load files from databases
+ """
+ handle_mock = MagicMock()
+ handle_mock.get_syncdbs.return_value = [MagicMock()]
+ pacman.handle = handle_mock
+ tarball = resource_path_root / "core" / "arcanisrepo.files.tar.gz"
+
+ mocker.patch("pathlib.Path.is_file", return_value=True)
+ open_mock = mocker.patch("ahriman.core.alpm.pacman.tarfile.open", return_value=tarfile.open(tarball, "r:gz"))
+
+ files = pacman.files()
+ assert len(files) == 2
+ assert package_ahriman.base in files
+ assert Path("usr/bin/ahriman") in files[package_ahriman.base]
+ open_mock.assert_called_once_with(pytest.helpers.anyvar(int), "r:gz")
+
+
+def test_files_package(pacman: Pacman, package_ahriman: Package, mocker: MockerFixture,
+ resource_path_root: Path) -> None:
+ """
+ must load files only for the specified package
+ """
+ handle_mock = MagicMock()
+ handle_mock.get_syncdbs.return_value = [MagicMock()]
pacman.handle = handle_mock
- pacman.database_sync(pacman.handle, force=True)
- handle_mock.init_transaction.assert_called_once_with()
- core_mock.update.assert_called_once_with(True)
+ tarball = resource_path_root / "core" / "arcanisrepo.files.tar.gz"
+
+ mocker.patch("pathlib.Path.is_file", return_value=True)
+ mocker.patch("ahriman.core.alpm.pacman.tarfile.open", return_value=tarfile.open(tarball, "r:gz"))
+
+ files = pacman.files(package_ahriman.base)
+ assert len(files) == 1
+ assert package_ahriman.base in files
-def test_package_get(pacman: Pacman) -> None:
+def test_files_skip(pacman: Pacman, mocker: MockerFixture) -> None:
+ """
+ must return empty list if no database found
+ """
+ handle_mock = MagicMock()
+ handle_mock.get_syncdbs.return_value = [MagicMock()]
+ pacman.handle = handle_mock
+
+ mocker.patch("pathlib.Path.is_file", return_value=False)
+
+ assert not pacman.files()
+
+
+def test_files_no_content(pacman: Pacman, mocker: MockerFixture) -> None:
+ """
+ must skip package if no content can be loaded
+ """
+ handle_mock = MagicMock()
+ handle_mock.get_syncdbs.return_value = [MagicMock()]
+ pacman.handle = handle_mock
+
+ tar_mock = MagicMock()
+ tar_mock.getmembers.return_value = [MagicMock()]
+ tar_mock.extractfile.return_value = None
+
+ open_mock = MagicMock()
+ open_mock.__enter__.return_value = tar_mock
+
+ mocker.patch("pathlib.Path.is_file", return_value=True)
+ mocker.patch("ahriman.core.alpm.pacman.tarfile.open", return_value=open_mock)
+
+ assert not pacman.files()
+
+
+def test_package(pacman: Pacman) -> None:
"""
must retrieve package
"""
- assert list(pacman.package_get("pacman"))
+ assert list(pacman.package("pacman"))
-def test_package_get_empty(pacman: Pacman) -> None:
+def test_package_empty(pacman: Pacman) -> None:
"""
must return empty packages list without exception
"""
- assert not list(pacman.package_get("some-random-name"))
+ assert not list(pacman.package("some-random-name"))
def test_packages(pacman: Pacman) -> None:
diff --git a/tests/ahriman/core/alpm/test_pacman_database.py b/tests/ahriman/core/alpm/test_pacman_database.py
new file mode 100644
index 00000000..53a03c67
--- /dev/null
+++ b/tests/ahriman/core/alpm/test_pacman_database.py
@@ -0,0 +1,203 @@
+import pytest
+
+from pathlib import Path
+from pytest_mock import MockerFixture
+from unittest.mock import MagicMock, call as MockCall
+
+from ahriman.core.alpm.pacman_database import PacmanDatabase
+from ahriman.core.exceptions import PacmanError
+
+
+def test_copy(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must copy loca database file
+ """
+ copy_mock = mocker.patch("shutil.copy")
+ pacman_database.copy(Path("remote"), Path("local"))
+ copy_mock.assert_called_once_with(Path("remote"), Path("local"))
+
+
+def test_download(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must download database by remote url
+ """
+ response_obj = MagicMock()
+ response_obj.headers = {pacman_database.LAST_MODIFIED_HEADER: "Fri, 09 Feb 2024 00:25:55 GMT"}
+ response_obj.iter_content.return_value = ["chunk".encode("utf8")]
+
+ path = Path("local")
+ url = "url"
+
+ file_mock = MagicMock()
+ request_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.make_request",
+ return_value=response_obj)
+ open_mock = mocker.patch("pathlib.Path.open")
+ open_mock.return_value.__enter__.return_value = file_mock
+ mtime_mock = mocker.patch("os.utime")
+
+ pacman_database.download(url, path)
+ request_mock.assert_called_once_with("GET", url, stream=True)
+ open_mock.assert_called_once_with("wb")
+ file_mock.write.assert_called_once_with("chunk".encode("utf8"))
+ mtime_mock.assert_called_once_with(path, (1707438355.0, 1707438355.0))
+
+
+def test_download_no_header(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must raise exception in case if no last modified head found
+ """
+ response_obj = MagicMock()
+ response_obj.headers = {}
+ mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.make_request", return_value=response_obj)
+
+ with pytest.raises(PacmanError):
+ pacman_database.download("url", Path("local"))
+
+
+def test_is_outdated(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must correctly check if file is outdated
+ """
+ response_obj = MagicMock()
+ response_obj.headers = {pacman_database.LAST_MODIFIED_HEADER: "Fri, 09 Feb 2024 00:25:55 GMT"}
+ stat_mock = MagicMock()
+ stat_mock.st_mtime = 1707438354
+
+ path = Path("local")
+ url = "url"
+
+ mocker.patch("pathlib.Path.is_file", return_value=True)
+ mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.make_request", return_value=response_obj)
+ mocker.patch("pathlib.Path.stat", return_value=stat_mock)
+
+ assert pacman_database.is_outdated(url, path)
+
+ stat_mock.st_mtime += 1
+ assert not pacman_database.is_outdated(url, path)
+
+ stat_mock.st_mtime += 1
+ assert not pacman_database.is_outdated(url, path)
+
+
+def test_is_outdated_not_a_file(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must mark as outdated if no file was found
+ """
+ mocker.patch("pathlib.Path.is_file", return_value=False)
+ assert pacman_database.is_outdated("url", Path("local"))
+
+
+def test_is_outdated_no_header(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must raise exception in case if no last modified head found during timestamp check
+ """
+ response_obj = MagicMock()
+ response_obj.headers = {}
+ mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.make_request", return_value=response_obj)
+ mocker.patch("pathlib.Path.is_file", return_value=True)
+
+ with pytest.raises(PacmanError):
+ pacman_database.is_outdated("url", Path("local"))
+
+
+def test_sync(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must sync database
+ """
+ sync_db_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_packages")
+ sync_files_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_files")
+
+ pacman_database.sync(force=True)
+ pacman_database.sync(force=False)
+ sync_db_mock.assert_has_calls([MockCall(force=True), MockCall(force=False)])
+ sync_files_mock.assert_not_called()
+
+
+def test_sync_with_files(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must sync database and files
+ """
+ sync_db_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_packages")
+ sync_files_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_files")
+ pacman_database.sync_files_database = True
+
+ pacman_database.sync(force=True)
+ pacman_database.sync(force=False)
+ sync_db_mock.assert_has_calls([MockCall(force=True), MockCall(force=False)])
+ sync_files_mock.assert_has_calls([MockCall(force=True), MockCall(force=False)])
+
+
+def test_sync_exception(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must suppress all exceptions on failure
+ """
+ mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.sync_packages", side_effect=Exception())
+ pacman_database.sync(force=True)
+
+
+def test_sync_files(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must sync files database
+ """
+ outdated_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.is_outdated", return_value=True)
+ download_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.download")
+
+ pacman_database.sync_files(force=False)
+ outdated_mock.assert_called_once_with(
+ "https://geo.mirror.pkgbuild.com/core/os/x86_64/core.files.tar.gz", pytest.helpers.anyvar(int))
+ download_mock.assert_called_once_with(
+ "https://geo.mirror.pkgbuild.com/core/os/x86_64/core.files.tar.gz", pytest.helpers.anyvar(int))
+
+
+def test_sync_files_not_outdated(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must skip files sync if up-to-date
+ """
+ mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.is_outdated", return_value=False)
+ download_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.download")
+
+ pacman_database.sync_files(force=False)
+ download_mock.assert_not_called()
+
+
+def test_sync_files_force(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must sync up-to-date files if force flag is set
+ """
+ mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.is_outdated", return_value=False)
+ download_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.download")
+
+ pacman_database.sync_files(force=True)
+ download_mock.assert_called_once_with(
+ "https://geo.mirror.pkgbuild.com/core/os/x86_64/core.files.tar.gz", pytest.helpers.anyvar(int))
+
+
+def test_sync_files_local(pacman_database: PacmanDatabase, mocker: MockerFixture) -> None:
+ """
+ must copy local files instead of downloading them
+ """
+ pacman_database.database.servers = ["file:///var"]
+ copy_mock = mocker.patch("ahriman.core.alpm.pacman_database.PacmanDatabase.copy")
+
+ pacman_database.sync_files(force=False)
+ copy_mock.assert_called_once_with(Path("/var/core.files.tar.gz"), pytest.helpers.anyvar(int))
+
+
+def test_sync_files_unknown_source(pacman_database: PacmanDatabase) -> None:
+ """
+ must raise an exception in case if server scheme is unsupported
+ """
+ pacman_database.database.servers = ["some random string"]
+ with pytest.raises(PacmanError):
+ pacman_database.sync_files(force=False)
+
+
+def test_sync_packages(pacman_database: PacmanDatabase) -> None:
+ """
+ must sync packages by using pyalpm method
+ """
+ pacman_database.database = MagicMock()
+
+ pacman_database.sync_packages(force=True)
+ pacman_database.sync_packages(force=False)
+ pacman_database.database.update.assert_has_calls([MockCall(True), MockCall(False)])
diff --git a/tests/ahriman/core/database/migrations/test_m013_dependencies.py b/tests/ahriman/core/database/migrations/test_m013_dependencies.py
new file mode 100644
index 00000000..0fc25928
--- /dev/null
+++ b/tests/ahriman/core/database/migrations/test_m013_dependencies.py
@@ -0,0 +1,8 @@
+from ahriman.core.database.migrations.m013_dependencies import steps
+
+
+def test_migration_dependencies() -> None:
+ """
+ migration must not be empty
+ """
+ assert steps
diff --git a/tests/ahriman/core/database/operations/test_dependencies_operations.py b/tests/ahriman/core/database/operations/test_dependencies_operations.py
new file mode 100644
index 00000000..5b466f46
--- /dev/null
+++ b/tests/ahriman/core/database/operations/test_dependencies_operations.py
@@ -0,0 +1,61 @@
+from pathlib import Path
+
+from ahriman.core.database import SQLite
+from ahriman.models.dependencies import Dependencies
+from ahriman.models.package import Package
+from ahriman.models.repository_id import RepositoryId
+
+
+def test_dependencies_insert_get(database: SQLite, package_ahriman: Package) -> None:
+ """
+ must insert and get dependencies
+ """
+ dependencies = Dependencies(package_ahriman.base, {Path("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"]})
+ database.dependencies_insert(dependencies2, RepositoryId("i686", database._repository_id.name))
+ assert database.dependencies_get() == [dependencies]
+ assert database.dependencies_get(package_ahriman.base) == [dependencies]
+ assert database.dependencies_get(
+ package_ahriman.base, RepositoryId("i686", database._repository_id.name)) == [dependencies2]
+
+
+def test_dependencies_insert_remove(database: SQLite, package_ahriman: Package,
+ package_python_schedule: Package) -> None:
+ """
+ must remove dependencies for the package
+ """
+ dependencies1 = Dependencies(package_ahriman.base, {Path("usr"): ["python"]})
+ database.dependencies_insert(dependencies1)
+ dependencies2 = Dependencies(package_python_schedule.base, {Path("usr"): ["filesystem"]})
+ database.dependencies_insert(dependencies2)
+ dependencies3 = Dependencies(package_ahriman.base, {Path("usr"): ["python3"]})
+ database.dependencies_insert(dependencies3, RepositoryId("i686", database._repository_id.name))
+
+ assert database.dependencies_get() == [dependencies1, dependencies2]
+
+ database.dependencies_remove(package_ahriman.base)
+ assert database.dependencies_get(package_ahriman.base) == []
+ assert database.dependencies_get(package_python_schedule.base) == [dependencies2]
+
+ # insert null
+ database.dependencies_remove(package_ahriman.base, RepositoryId("i686", database._repository_id.name))
+ assert database.dependencies_get(package_ahriman.base, RepositoryId("i686", database._repository_id.name)) == []
+ assert database.dependencies_get(package_python_schedule.base) == [dependencies2]
+
+
+def test_dependencies_insert_remove_full(database: SQLite, package_ahriman: Package,
+ package_python_schedule: Package) -> None:
+ """
+ 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"]}),
+ RepositoryId("i686", database._repository_id.name))
+
+ database.dependencies_remove(None)
+ assert database.dependencies_get() == []
+ assert database.dependencies_get(package_ahriman.base, RepositoryId("i686", database._repository_id.name))
diff --git a/tests/ahriman/core/database/test_sqlite.py b/tests/ahriman/core/database/test_sqlite.py
index 32215dcd..5afe8354 100644
--- a/tests/ahriman/core/database/test_sqlite.py
+++ b/tests/ahriman/core/database/test_sqlite.py
@@ -33,3 +33,21 @@ def test_init_skip_migration(database: SQLite, configuration: Configuration, moc
database.init(configuration)
migrate_schema_mock.assert_not_called()
+
+
+def test_package_clear(database: SQLite, mocker: MockerFixture) -> None:
+ """
+ must clear package data
+ """
+ build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_clear")
+ patches_mock = mocker.patch("ahriman.core.database.SQLite.patches_remove")
+ logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_remove")
+ changes_mock = mocker.patch("ahriman.core.database.SQLite.changes_remove")
+ dependencies_mock = mocker.patch("ahriman.core.database.SQLite.dependencies_remove")
+
+ database.package_clear("package")
+ build_queue_mock.assert_called_once_with("package")
+ patches_mock.assert_called_once_with("package", [])
+ logs_mock.assert_called_once_with("package", None)
+ changes_mock.assert_called_once_with("package")
+ dependencies_mock.assert_called_once_with("package")
diff --git a/tests/ahriman/core/http/test_sync_http_client.py b/tests/ahriman/core/http/test_sync_http_client.py
index b5565905..74648fb6 100644
--- a/tests/ahriman/core/http/test_sync_http_client.py
+++ b/tests/ahriman/core/http/test_sync_http_client.py
@@ -82,34 +82,40 @@ def test_make_request(mocker: MockerFixture) -> None:
auth = client.auth = ("username", "password")
assert client.make_request("GET", "url9") is not None
+ client.auth = None
+
+ assert client.make_request("GET", "url10", stream=True) is not None
request_mock.assert_has_calls([
MockCall("GET", "url1", params=None, data=None, headers=None, files=None, json=None,
- auth=None, timeout=client.timeout),
+ stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("GET", "url2", params=[("param", "value")], data=None, headers=None, files=None, json=None,
- auth=None, timeout=client.timeout),
+ stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("POST", "url3", params=None, data=None, headers=None, files=None, json=None,
- auth=None, timeout=client.timeout),
+ stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("POST", "url4", params=None, data=None, headers=None, files=None, json={"param": "value"},
- auth=None, timeout=client.timeout),
+ stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("POST", "url5", params=None, data={"param": "value"}, headers=None, files=None, json=None,
- auth=None, timeout=client.timeout),
+ stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("POST", "url6", params=None, data=None, headers=None, files={"file": "tuple"}, json=None,
- auth=None, timeout=client.timeout),
+ stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("DELETE", "url7", params=None, data=None, headers=None, files=None, json=None,
- auth=None, timeout=client.timeout),
+ stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("GET", "url8", params=None, data=None, headers={"user-agent": "ua"}, files=None, json=None,
- auth=None, timeout=client.timeout),
+ stream=None, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
MockCall("GET", "url9", params=None, data=None, headers=None, files=None, json=None,
- auth=auth, timeout=client.timeout),
+ stream=None, auth=auth, timeout=client.timeout),
+ MockCall().raise_for_status(),
+ MockCall("GET", "url10", params=None, data=None, headers=None, files=None, json=None,
+ stream=True, auth=None, timeout=client.timeout),
MockCall().raise_for_status(),
])
@@ -151,4 +157,4 @@ def test_make_request_session() -> None:
client.make_request("GET", "url", session=session_mock)
session_mock.request.assert_called_once_with(
"GET", "url", params=None, data=None, headers=None, files=None, json=None,
- auth=None, timeout=client.timeout)
+ stream=None, auth=None, timeout=client.timeout)
diff --git a/tests/ahriman/core/repository/test_executor.py b/tests/ahriman/core/repository/test_executor.py
index 118274c5..ec992c08 100644
--- a/tests/ahriman/core/repository/test_executor.py
+++ b/tests/ahriman/core/repository/test_executor.py
@@ -2,28 +2,37 @@ import pytest
from pathlib import Path
from pytest_mock import MockerFixture
+from typing import Any
from unittest.mock import call as MockCall
from ahriman.core.repository.executor import Executor
from ahriman.models.changes import Changes
+from ahriman.models.dependencies import Dependencies
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
from ahriman.models.user import User
-def test_process_build(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
+def test_process_build(executor: Executor, package_ahriman: Package, passwd: Any, mocker: MockerFixture) -> None:
"""
must run build process
"""
+ dependencies = Dependencies(package_ahriman.base)
+ mocker.patch("ahriman.models.repository_paths.getpwuid", return_value=passwd)
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)])
init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init", return_value="sha")
move_mock = mocker.patch("shutil.move")
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_building")
commit_sha_mock = mocker.patch("ahriman.core.status.client.Client.package_changes_set")
+ depends_on_mock = mocker.patch("ahriman.models.package_archive.PackageArchive.depends_on",
+ return_value=dependencies)
+ dependencies_mock = mocker.patch("ahriman.core.database.SQLite.dependencies_insert")
executor.process_build([package_ahriman], Packagers("packager"), bump_pkgrel=False)
init_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), None)
+ depends_on_mock.assert_called_once_with()
+ dependencies_mock.assert_called_once_with(dependencies)
# must move files (once)
move_mock.assert_called_once_with(Path(package_ahriman.base), executor.paths.packages / package_ahriman.base)
# must update status
@@ -70,10 +79,7 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman])
tree_clear_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_clear")
repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove")
- build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_clear")
- patches_mock = mocker.patch("ahriman.core.database.SQLite.patches_remove")
- logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_remove")
- commit_sha_mock = mocker.patch("ahriman.core.database.SQLite.changes_remove")
+ database_mock = mocker.patch("ahriman.core.database.SQLite.package_clear")
status_client_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
executor.process_remove([package_ahriman.base])
@@ -82,11 +88,8 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke
package_ahriman.base, package_ahriman.packages[package_ahriman.base].filepath)
# must update status and remove package files
tree_clear_mock.assert_called_once_with(package_ahriman.base)
- build_queue_mock.assert_called_once_with(package_ahriman.base)
- patches_mock.assert_called_once_with(package_ahriman.base, [])
- logs_mock.assert_called_once_with(package_ahriman.base, None)
+ database_mock.assert_called_once_with(package_ahriman.base)
status_client_mock.assert_called_once_with(package_ahriman.base)
- commit_sha_mock.assert_called_once_with(package_ahriman.base)
def test_process_remove_with_debug(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None:
diff --git a/tests/ahriman/core/repository/test_package_info.py b/tests/ahriman/core/repository/test_package_info.py
index 705b657c..a6a1656b 100644
--- a/tests/ahriman/core/repository/test_package_info.py
+++ b/tests/ahriman/core/repository/test_package_info.py
@@ -96,17 +96,30 @@ def test_package_changes_skip(package_info: PackageInfo, package_ahriman: Packag
changes_mock.assert_not_called()
-def test_packages(package_info: PackageInfo, package_ahriman: Package, mocker: MockerFixture) -> None:
+def test_packages(package_info: PackageInfo, package_ahriman: Package, package_python_schedule: Package,
+ mocker: MockerFixture) -> None:
"""
must return repository packages
"""
- mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman])
- load_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives")
- package_info.packages()
+ mocker.patch("pathlib.Path.iterdir")
+ load_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives",
+ return_value=[package_ahriman, package_python_schedule])
+ assert package_info.packages() == [package_ahriman, package_python_schedule]
# it uses filter object, so we cannot verify argument list =/
load_mock.assert_called_once_with(pytest.helpers.anyvar(int))
+def test_packages_filter(package_info: PackageInfo, package_ahriman: Package, package_python_schedule: Package,
+ mocker: MockerFixture) -> None:
+ """
+ must filter result by bases
+ """
+ mocker.patch("pathlib.Path.iterdir")
+ mocker.patch("ahriman.core.repository.package_info.PackageInfo.load_archives",
+ return_value=[package_ahriman, package_python_schedule])
+ assert package_info.packages([package_ahriman.base]) == [package_ahriman]
+
+
def test_packages_built(package_info: PackageInfo, mocker: MockerFixture) -> None:
"""
must return build packages
diff --git a/tests/ahriman/core/repository/test_update_handler.py b/tests/ahriman/core/repository/test_update_handler.py
index 27f3b9af..567fb2e3 100644
--- a/tests/ahriman/core/repository/test_update_handler.py
+++ b/tests/ahriman/core/repository/test_update_handler.py
@@ -6,6 +6,7 @@ from typing import Any
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.repository.update_handler import UpdateHandler
+from ahriman.models.dependencies import Dependencies
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
@@ -16,12 +17,14 @@ def test_updates_aur(update_handler: UpdateHandler, package_ahriman: Package,
"""
must provide updates with status updates
"""
- mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
+ packages_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
+ return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_pending")
package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
assert update_handler.updates_aur([], vcs=True) == [package_ahriman]
+ packages_mock.assert_called_once_with([])
status_client_mock.assert_called_once_with(package_ahriman.base)
package_is_outdated_mock.assert_called_once_with(
package_ahriman, update_handler.paths,
@@ -70,17 +73,17 @@ def test_updates_aur_local(update_handler: UpdateHandler, package_ahriman: Packa
package_load_mock.assert_not_called()
-def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Package, package_python_schedule: Package,
- mocker: MockerFixture) -> None:
+def test_updates_aur_filter(update_handler: UpdateHandler, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must provide updates only for filtered packages
"""
- mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
- return_value=[package_ahriman, package_python_schedule])
+ packages_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
+ return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.is_outdated", return_value=True)
package_load_mock = mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
assert update_handler.updates_aur([package_ahriman.base], vcs=True) == [package_ahriman]
+ packages_mock.assert_called_once_with([package_ahriman.base])
package_load_mock.assert_called_once_with(package_ahriman.base, None)
@@ -131,7 +134,7 @@ def test_updates_aur_load_by_package(update_handler: UpdateHandler, package_pyth
assert update_handler.updates_aur([], vcs=True) == [package_python_schedule]
-def test_updates_load_by_package_aur_failed(update_handler: UpdateHandler, package_ahriman: Package,
+def test_updates_aur_load_by_package_failed(update_handler: UpdateHandler, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must update status via client for failed load
@@ -143,6 +146,56 @@ def test_updates_load_by_package_aur_failed(update_handler: UpdateHandler, packa
update_handler.updates_aur([], vcs=True)
+def test_updates_dependencies(update_handler: UpdateHandler, package_ahriman: Package, package_python_schedule: Package,
+ mocker: MockerFixture) -> None:
+ """
+ must define updates with broken dependencies
+ """
+ packages_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages",
+ return_value=[package_ahriman, package_python_schedule])
+ dependencies = [
+ Dependencies(package_ahriman.base, {Path("usr/lib/python3.11/site-packages"): ["python"]}),
+ Dependencies(package_python_schedule.base, {Path("usr/lib/python3.12/site-packages"): ["python"]}),
+ ]
+ mocker.patch("ahriman.core.database.SQLite.dependencies_get", return_value=dependencies)
+ mocker.patch("ahriman.core.alpm.pacman.Pacman.files",
+ return_value={"python": {Path("usr/lib/python3.12/site-packages")}})
+
+ assert update_handler.updates_dependencies(["filter"]) == [package_ahriman]
+ packages_mock.assert_called_once_with(["filter"])
+
+
+def test_updates_dependencies_skip_unknown(update_handler: UpdateHandler, package_ahriman: Package,
+ mocker: MockerFixture) -> None:
+ """
+ must skip unknown package dependencies
+ """
+ mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
+ mocker.patch("ahriman.core.database.SQLite.dependencies_get", return_value=[])
+ mocker.patch("ahriman.core.alpm.pacman.Pacman.files",
+ return_value={"python": {Path("usr/lib/python3.12/site-packages")}})
+
+ assert update_handler.updates_dependencies(["filter"]) == []
+
+
+def test_updates_dependencies_partial(update_handler: UpdateHandler, package_ahriman: Package,
+ mocker: MockerFixture) -> None:
+ """
+ must skip broken dependencies update if at least one package provides file
+ """
+ mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
+ dependencies = [
+ Dependencies(package_ahriman.base, {Path("usr"): ["filesystem", "python"]}),
+ ]
+ mocker.patch("ahriman.core.database.SQLite.dependencies_get", return_value=dependencies)
+ mocker.patch("ahriman.core.alpm.pacman.Pacman.files", return_value={
+ "filesystem": {Path("usr")},
+ "python": {Path("usr")},
+ })
+
+ assert update_handler.updates_dependencies(["filter"]) == []
+
+
def test_updates_local(update_handler: UpdateHandler, package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must check for updates for locally stored packages
diff --git a/tests/ahriman/core/test_util.py b/tests/ahriman/core/test_util.py
index 3172f475..9686e332 100644
--- a/tests/ahriman/core/test_util.py
+++ b/tests/ahriman/core/test_util.py
@@ -460,6 +460,7 @@ def test_walk(resource_path_root: Path) -> None:
"""
expected = sorted([
resource_path_root / "core" / "ahriman.ini",
+ resource_path_root / "core" / "arcanisrepo.files.tar.gz",
resource_path_root / "core" / "logging.ini",
resource_path_root / "models" / "aur_error",
resource_path_root / "models" / "big_file_checksum",
@@ -467,6 +468,7 @@ def test_walk(resource_path_root: Path) -> None:
resource_path_root / "models" / "official_error",
resource_path_root / "models" / "package_ahriman_aur",
resource_path_root / "models" / "package_akonadi_aur",
+ resource_path_root / "models" / "package_ahriman_files",
resource_path_root / "models" / "package_ahriman_srcinfo",
resource_path_root / "models" / "package_gcc10_srcinfo",
resource_path_root / "models" / "package_jellyfin-ffmpeg5-bin_srcinfo",
diff --git a/tests/ahriman/models/conftest.py b/tests/ahriman/models/conftest.py
index 4b62b4fc..2abcf140 100644
--- a/tests/ahriman/models/conftest.py
+++ b/tests/ahriman/models/conftest.py
@@ -1,7 +1,9 @@
import datetime
import pytest
+from typing import Any
from unittest.mock import MagicMock, PropertyMock
+from pytest_mock import MockerFixture
from ahriman import __version__
from ahriman.core.alpm.remote import AUR
@@ -10,9 +12,11 @@ from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.counters import Counters
from ahriman.models.internal_status import InternalStatus
from ahriman.models.package import Package
+from ahriman.models.package_archive import PackageArchive
from ahriman.models.package_description import PackageDescription
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
+from ahriman.models.repository_paths import RepositoryPaths
@pytest.fixture
@@ -60,6 +64,25 @@ def internal_status(counters: Counters) -> InternalStatus:
repository="aur-clone")
+@pytest.fixture
+def package_archive_ahriman(package_ahriman: Package, repository_paths: RepositoryPaths,
+ passwd: Any, mocker: MockerFixture) -> PackageArchive:
+ """
+ package archive fixture
+
+ Args:
+ package_ahriman(Package): package test instance
+ repository_paths(RepositoryPaths): repository paths test instance
+ passwd(Any): passwd structure test instance
+ mocker(MockerFixture): mocker object
+
+ Returns:
+ PackageArchive: package archive test instance
+ """
+ mocker.patch("ahriman.models.repository_paths.getpwuid", return_value=passwd)
+ return PackageArchive(repository_paths.build_directory, package_ahriman)
+
+
@pytest.fixture
def package_tpacpi_bat_git() -> Package:
"""
diff --git a/tests/ahriman/models/test_dependencies.py b/tests/ahriman/models/test_dependencies.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/ahriman/models/test_package_archive.py b/tests/ahriman/models/test_package_archive.py
new file mode 100644
index 00000000..35c27654
--- /dev/null
+++ b/tests/ahriman/models/test_package_archive.py
@@ -0,0 +1,114 @@
+from io import BytesIO
+from pathlib import Path
+from pytest_mock import MockerFixture
+
+from ahriman.models.package_archive import PackageArchive
+
+
+def test_dynamic_needed(mocker: MockerFixture) -> None:
+ """
+ must correctly define list of dynamically linked libraries
+ """
+ mocker.patch("ahriman.models.package_archive.PackageArchive.is_elf", return_value=True)
+
+ linked = PackageArchive.dynamic_needed(Path(".tox") / "tests" / "bin" / "python")
+ assert linked
+ assert next(library for library in linked if library.startswith("libpython"))
+ assert next(library for library in linked if library.startswith("libc"))
+
+
+def test_dynamic_needed_not_elf(mocker: MockerFixture) -> None:
+ """
+ must skip checking if not an elf file
+ """
+ mocker.patch("ahriman.models.package_archive.PackageArchive.is_elf", return_value=False)
+ assert not PackageArchive.dynamic_needed(Path(".tox") / "tests" / "bin" / "python")
+
+
+def test_dynamic_needed_no_section(mocker: MockerFixture) -> None:
+ """
+ must skip checking if there was no dynamic section found
+ """
+ mocker.patch("ahriman.models.package_archive.PackageArchive.is_elf", return_value=True)
+ mocker.patch("elftools.elf.elffile.ELFFile.iter_sections", return_value=[])
+ assert not PackageArchive.dynamic_needed(Path(".tox") / "tests" / "bin" / "python")
+
+
+def test_is_elf() -> None:
+ """
+ must correctly define elf file
+ """
+ assert not PackageArchive.is_elf(BytesIO())
+ assert not PackageArchive.is_elf(BytesIO(b"random string"))
+ assert PackageArchive.is_elf(BytesIO(b"\x7fELF"))
+ assert PackageArchive.is_elf(BytesIO(b"\x7fELF\nrandom string"))
+
+
+def test_depends_on(package_archive_ahriman: PackageArchive, mocker: MockerFixture) -> None:
+ """
+ must extract packages and files which are dependencies for the package
+ """
+ mocker.patch("ahriman.models.package_archive.PackageArchive.installed_packages", return_value={
+ package_archive_ahriman.package.base: ([Path("usr") / "dir2"], [Path("file1")]),
+ "package1": (
+ [Path("package1") / "dir1", Path("usr") / "dir2"],
+ [Path("package1") / "file1", Path("package1") / "file2"],
+ ),
+ "package2": (
+ [Path("usr") / "dir2", Path("package2") / "dir3", Path("package2") / "dir4"],
+ [Path("package2") / "file4", Path("package2") / "file3"],
+ ),
+ })
+ mocker.patch("ahriman.models.package_archive.PackageArchive.depends_on_paths", return_value=(
+ {"file1", "file3"},
+ {Path("usr") / "dir2", Path("dir3"), Path("package2") / "dir4"},
+ ))
+
+ result = package_archive_ahriman.depends_on()
+ assert result.package_base == package_archive_ahriman.package.base
+ assert result.paths == {
+ Path("package1") / "file1": ["package1"],
+ Path("package2") / "file3": ["package2"],
+ Path("package2") / "dir4": ["package2"],
+ Path("package2") / "file3": ["package2"],
+ Path("usr") / "dir2": ["package1", "package2"]
+ }
+
+
+def test_depends_on_paths(package_archive_ahriman: PackageArchive, mocker: MockerFixture) -> None:
+ """
+ must correctly extract dependencies
+ """
+ package_dir = package_archive_ahriman.root / "build" / package_archive_ahriman.package.base / "pkg"
+ dynamic_mock = mocker.patch("ahriman.models.package_archive.PackageArchive.dynamic_needed", return_value=["lib"])
+ walk_mock = mocker.patch("ahriman.models.package_archive.walk", return_value=[
+ package_dir / package_archive_ahriman.package.base / "root" / "file",
+ Path("directory"),
+ ])
+ mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda path: path != Path("directory"))
+
+ dependencies, roots = package_archive_ahriman.depends_on_paths()
+ assert dependencies == {"lib"}
+ assert roots == {Path("root")}
+ dynamic_mock.assert_called_once_with(package_dir / package_archive_ahriman.package.base / "root" / "file")
+ walk_mock.assert_called_once_with(package_dir)
+
+
+def test_installed_packages(package_archive_ahriman: PackageArchive, mocker: MockerFixture,
+ resource_path_root: Path) -> None:
+ """
+ must load list of installed packages and their files
+ """
+ walk_mock = mocker.patch("ahriman.models.package_archive.walk", return_value=[
+ Path("ahriman-2.13.3-1") / "desc",
+ Path("ahriman-2.13.3-1") / "files",
+ ])
+ files = (resource_path_root / "models" / "package_ahriman_files").read_text(encoding="utf8")
+ read_mock = mocker.patch("pathlib.Path.read_text", return_value=files)
+
+ result = package_archive_ahriman.installed_packages()
+ assert result
+ assert Path("usr") in result[package_archive_ahriman.package.base][0]
+ assert Path("usr/bin/ahriman") in result[package_archive_ahriman.package.base][1]
+ walk_mock.assert_called_once_with(package_archive_ahriman.root / "var" / "lib" / "pacman" / "local")
+ read_mock.assert_called_once_with(encoding="utf8")
diff --git a/tests/ahriman/models/test_repository_paths.py b/tests/ahriman/models/test_repository_paths.py
index 3ab87d7c..dc673a20 100644
--- a/tests/ahriman/models/test_repository_paths.py
+++ b/tests/ahriman/models/test_repository_paths.py
@@ -282,18 +282,19 @@ def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -
prop
for prop in dir(repository_paths)
if not prop.startswith("_")
+ and prop not in (
+ "build_directory",
+ "logger_name",
+ "logger",
+ "repository_id",
+ "root",
+ "root_owner",
+ )
and not callable(getattr(repository_paths, prop))
- and prop not in ("logger_name",
- "logger",
- "repository_id",
- "root",
- "root_owner")
}
mkdir_mock = mocker.patch("pathlib.Path.mkdir")
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
- print(paths)
-
repository_paths.tree_create()
mkdir_mock.assert_has_calls([MockCall(mode=0o755, parents=True, exist_ok=True) for _ in paths], any_order=True)
chown_mock.assert_has_calls([MockCall(pytest.helpers.anyvar(int)) for _ in paths], any_order=True)
diff --git a/tests/testresources/core/ahriman.ini b/tests/testresources/core/ahriman.ini
index 3685f3ca..a757c885 100644
--- a/tests/testresources/core/ahriman.ini
+++ b/tests/testresources/core/ahriman.ini
@@ -8,6 +8,7 @@ database = /var/lib/pacman
mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
repositories = core extra multilib
root = /
+sync_files_database = no
use_ahriman_cache = no
[auth]
diff --git a/tests/testresources/core/arcanisrepo.files.tar.gz b/tests/testresources/core/arcanisrepo.files.tar.gz
new file mode 100644
index 00000000..70d52d3c
Binary files /dev/null and b/tests/testresources/core/arcanisrepo.files.tar.gz differ
diff --git a/tests/testresources/models/package_ahriman_files b/tests/testresources/models/package_ahriman_files
new file mode 100644
index 00000000..7565e344
--- /dev/null
+++ b/tests/testresources/models/package_ahriman_files
@@ -0,0 +1,1009 @@
+%FILES%
+etc/
+etc/ahriman.ini
+etc/ahriman.ini.d/
+etc/ahriman.ini.d/logging.ini
+usr/
+usr/bin/
+usr/bin/ahriman
+usr/lib/
+usr/lib/python3.11/
+usr/lib/python3.11/site-packages/
+usr/lib/python3.11/site-packages/ahriman-2.13.3.dist-info/
+usr/lib/python3.11/site-packages/ahriman-2.13.3.dist-info/COPYING
+usr/lib/python3.11/site-packages/ahriman-2.13.3.dist-info/METADATA
+usr/lib/python3.11/site-packages/ahriman-2.13.3.dist-info/RECORD
+usr/lib/python3.11/site-packages/ahriman-2.13.3.dist-info/WHEEL
+usr/lib/python3.11/site-packages/ahriman-2.13.3.dist-info/entry_points.txt
+usr/lib/python3.11/site-packages/ahriman/
+usr/lib/python3.11/site-packages/ahriman/__init__.py
+usr/lib/python3.11/site-packages/ahriman/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/
+usr/lib/python3.11/site-packages/ahriman/application/__init__.py
+usr/lib/python3.11/site-packages/ahriman/application/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/application/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/__pycache__/ahriman.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/__pycache__/ahriman.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/__pycache__/lock.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/__pycache__/lock.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/ahriman.py
+usr/lib/python3.11/site-packages/ahriman/application/application/
+usr/lib/python3.11/site-packages/ahriman/application/application/__init__.py
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/application.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/application.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/application_packages.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/application_packages.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/application_properties.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/application_properties.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/application_repository.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/application_repository.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/updates_iterator.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/__pycache__/updates_iterator.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/application.py
+usr/lib/python3.11/site-packages/ahriman/application/application/application_packages.py
+usr/lib/python3.11/site-packages/ahriman/application/application/application_properties.py
+usr/lib/python3.11/site-packages/ahriman/application/application/application_repository.py
+usr/lib/python3.11/site-packages/ahriman/application/application/updates_iterator.py
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__init__.py
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__pycache__/local_updater.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__pycache__/local_updater.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__pycache__/remote_updater.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__pycache__/remote_updater.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__pycache__/updater.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/__pycache__/updater.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/local_updater.py
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/remote_updater.py
+usr/lib/python3.11/site-packages/ahriman/application/application/workers/updater.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__init__.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/add.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/add.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/backup.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/backup.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/change.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/change.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/clean.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/clean.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/daemon.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/daemon.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/dump.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/dump.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/handler.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/handler.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/help.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/help.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/key_import.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/key_import.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/patch.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/patch.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/rebuild.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/rebuild.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/remove.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/remove.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/remove_unknown.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/remove_unknown.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/repositories.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/repositories.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/restore.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/restore.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/run.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/run.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/search.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/search.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/service_updates.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/service_updates.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/setup.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/setup.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/shell.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/shell.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/sign.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/sign.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/status.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/status.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/status_update.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/status_update.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/structure.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/structure.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/tree_migrate.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/tree_migrate.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/triggers.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/triggers.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/unsafe_commands.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/unsafe_commands.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/update.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/update.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/users.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/users.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/validate.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/validate.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/versions.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/versions.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/web.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/__pycache__/web.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/application/handlers/add.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/backup.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/change.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/clean.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/daemon.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/dump.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/handler.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/help.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/key_import.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/patch.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/rebuild.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/remove.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/remove_unknown.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/repositories.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/restore.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/run.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/search.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/service_updates.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/setup.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/shell.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/sign.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/status.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/status_update.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/structure.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/tree_migrate.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/triggers.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/unsafe_commands.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/update.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/users.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/validate.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/versions.py
+usr/lib/python3.11/site-packages/ahriman/application/handlers/web.py
+usr/lib/python3.11/site-packages/ahriman/application/lock.py
+usr/lib/python3.11/site-packages/ahriman/core/
+usr/lib/python3.11/site-packages/ahriman/core/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/exceptions.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/exceptions.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/spawn.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/spawn.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/tree.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/tree.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/util.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/__pycache__/util.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/
+usr/lib/python3.11/site-packages/ahriman/core/alpm/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/alpm/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/alpm/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/__pycache__/pacman.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/__pycache__/pacman.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/__pycache__/repo.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/__pycache__/repo.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/pacman.py
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/aur.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/aur.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/official.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/official.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/official_syncdb.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/official_syncdb.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/remote.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/__pycache__/remote.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/aur.py
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/official.py
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/official_syncdb.py
+usr/lib/python3.11/site-packages/ahriman/core/alpm/remote/remote.py
+usr/lib/python3.11/site-packages/ahriman/core/alpm/repo.py
+usr/lib/python3.11/site-packages/ahriman/core/auth/
+usr/lib/python3.11/site-packages/ahriman/core/auth/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/auth.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/auth.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/helpers.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/helpers.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/mapping.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/mapping.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/oauth.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/__pycache__/oauth.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/auth/auth.py
+usr/lib/python3.11/site-packages/ahriman/core/auth/helpers.py
+usr/lib/python3.11/site-packages/ahriman/core/auth/mapping.py
+usr/lib/python3.11/site-packages/ahriman/core/auth/oauth.py
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/__pycache__/sources.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/__pycache__/sources.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/__pycache__/task.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/__pycache__/task.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/sources.py
+usr/lib/python3.11/site-packages/ahriman/core/build_tools/task.py
+usr/lib/python3.11/site-packages/ahriman/core/configuration/
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/configuration.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/configuration.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/shell_interpolator.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/shell_interpolator.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/validator.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/__pycache__/validator.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/configuration/configuration.py
+usr/lib/python3.11/site-packages/ahriman/core/configuration/schema.py
+usr/lib/python3.11/site-packages/ahriman/core/configuration/shell_interpolator.py
+usr/lib/python3.11/site-packages/ahriman/core/configuration/validator.py
+usr/lib/python3.11/site-packages/ahriman/core/database/
+usr/lib/python3.11/site-packages/ahriman/core/database/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/database/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/database/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/__pycache__/sqlite.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/__pycache__/sqlite.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m000_initial.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m000_initial.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m001_package_source.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m001_package_source.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m002_user_access.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m002_user_access.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m003_patch_variables.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m003_patch_variables.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m004_logs.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m004_logs.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m005_make_opt_depends.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m005_make_opt_depends.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m006_packages_architecture_required.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m006_packages_architecture_required.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m007_check_depends.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m007_check_depends.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m008_packagers.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m008_packagers.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m009_local_source.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m009_local_source.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m010_version_based_logs_removal.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m010_version_based_logs_removal.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m011_repository_name.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m011_repository_name.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m012_last_commit_sha.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/__pycache__/m012_last_commit_sha.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m000_initial.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m001_package_source.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m002_user_access.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m003_patch_variables.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m004_logs.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m005_make_opt_depends.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m006_packages_architecture_required.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m007_check_depends.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m008_packagers.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m009_local_source.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m010_version_based_logs_removal.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m011_repository_name.py
+usr/lib/python3.11/site-packages/ahriman/core/database/migrations/m012_last_commit_sha.py
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/auth_operations.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/auth_operations.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/build_operations.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/build_operations.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/changes_operations.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/changes_operations.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/logs_operations.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/logs_operations.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/operations.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/operations.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/package_operations.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/package_operations.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/patch_operations.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/__pycache__/patch_operations.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/auth_operations.py
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/build_operations.py
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/changes_operations.py
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/logs_operations.py
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/operations.py
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/package_operations.py
+usr/lib/python3.11/site-packages/ahriman/core/database/operations/patch_operations.py
+usr/lib/python3.11/site-packages/ahriman/core/database/sqlite.py
+usr/lib/python3.11/site-packages/ahriman/core/distributed/
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/distributed_system.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/distributed_system.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/worker_loader_trigger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/worker_loader_trigger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/worker_trigger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/worker_trigger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/workers_cache.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/__pycache__/workers_cache.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/distributed/distributed_system.py
+usr/lib/python3.11/site-packages/ahriman/core/distributed/worker_loader_trigger.py
+usr/lib/python3.11/site-packages/ahriman/core/distributed/worker_trigger.py
+usr/lib/python3.11/site-packages/ahriman/core/distributed/workers_cache.py
+usr/lib/python3.11/site-packages/ahriman/core/exceptions.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/aur_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/aur_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/build_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/build_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/changes_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/changes_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/configuration_paths_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/configuration_paths_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/configuration_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/configuration_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/package_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/package_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/patch_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/patch_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/repository_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/repository_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/status_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/status_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/string_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/string_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/tree_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/tree_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/update_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/update_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/user_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/user_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/validation_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/validation_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/version_printer.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/__pycache__/version_printer.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/formatters/aur_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/build_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/changes_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/configuration_paths_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/configuration_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/package_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/patch_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/repository_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/status_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/string_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/tree_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/update_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/user_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/validation_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/formatters/version_printer.py
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/remote_pull.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/remote_pull.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/remote_pull_trigger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/remote_pull_trigger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/remote_push.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/remote_push.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/remote_push_trigger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/__pycache__/remote_push_trigger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/remote_pull.py
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/remote_pull_trigger.py
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/remote_push.py
+usr/lib/python3.11/site-packages/ahriman/core/gitremote/remote_push_trigger.py
+usr/lib/python3.11/site-packages/ahriman/core/http/
+usr/lib/python3.11/site-packages/ahriman/core/http/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/http/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/http/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/http/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/http/__pycache__/sync_ahriman_client.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/http/__pycache__/sync_ahriman_client.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/http/__pycache__/sync_http_client.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/http/__pycache__/sync_http_client.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/http/sync_ahriman_client.py
+usr/lib/python3.11/site-packages/ahriman/core/http/sync_http_client.py
+usr/lib/python3.11/site-packages/ahriman/core/log/
+usr/lib/python3.11/site-packages/ahriman/core/log/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/http_log_handler.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/http_log_handler.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/journal_handler.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/journal_handler.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/lazy_logging.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/lazy_logging.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/log_loader.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/__pycache__/log_loader.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/log/http_log_handler.py
+usr/lib/python3.11/site-packages/ahriman/core/log/journal_handler.py
+usr/lib/python3.11/site-packages/ahriman/core/log/lazy_logging.py
+usr/lib/python3.11/site-packages/ahriman/core/log/log_loader.py
+usr/lib/python3.11/site-packages/ahriman/core/report/
+usr/lib/python3.11/site-packages/ahriman/core/report/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/console.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/console.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/email.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/email.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/html.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/html.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/jinja_template.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/jinja_template.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/remote_call.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/remote_call.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/report.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/report.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/report_trigger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/report_trigger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/telegram.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/__pycache__/telegram.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/report/console.py
+usr/lib/python3.11/site-packages/ahriman/core/report/email.py
+usr/lib/python3.11/site-packages/ahriman/core/report/html.py
+usr/lib/python3.11/site-packages/ahriman/core/report/jinja_template.py
+usr/lib/python3.11/site-packages/ahriman/core/report/remote_call.py
+usr/lib/python3.11/site-packages/ahriman/core/report/report.py
+usr/lib/python3.11/site-packages/ahriman/core/report/report_trigger.py
+usr/lib/python3.11/site-packages/ahriman/core/report/telegram.py
+usr/lib/python3.11/site-packages/ahriman/core/repository/
+usr/lib/python3.11/site-packages/ahriman/core/repository/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/cleaner.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/cleaner.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/executor.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/executor.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/package_info.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/package_info.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/repository.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/repository.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/repository_properties.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/repository_properties.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/update_handler.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/__pycache__/update_handler.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/repository/cleaner.py
+usr/lib/python3.11/site-packages/ahriman/core/repository/executor.py
+usr/lib/python3.11/site-packages/ahriman/core/repository/package_info.py
+usr/lib/python3.11/site-packages/ahriman/core/repository/repository.py
+usr/lib/python3.11/site-packages/ahriman/core/repository/repository_properties.py
+usr/lib/python3.11/site-packages/ahriman/core/repository/update_handler.py
+usr/lib/python3.11/site-packages/ahriman/core/sign/
+usr/lib/python3.11/site-packages/ahriman/core/sign/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/sign/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/sign/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/sign/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/sign/__pycache__/gpg.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/sign/__pycache__/gpg.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/sign/gpg.py
+usr/lib/python3.11/site-packages/ahriman/core/spawn.py
+usr/lib/python3.11/site-packages/ahriman/core/status/
+usr/lib/python3.11/site-packages/ahriman/core/status/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/status/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/status/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/status/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/status/__pycache__/client.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/status/__pycache__/client.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/status/__pycache__/watcher.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/status/__pycache__/watcher.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/status/__pycache__/web_client.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/status/__pycache__/web_client.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/status/client.py
+usr/lib/python3.11/site-packages/ahriman/core/status/watcher.py
+usr/lib/python3.11/site-packages/ahriman/core/status/web_client.py
+usr/lib/python3.11/site-packages/ahriman/core/support/
+usr/lib/python3.11/site-packages/ahriman/core/support/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/support/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/support/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/__pycache__/keyring_trigger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/__pycache__/keyring_trigger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/__pycache__/mirrorlist_trigger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/__pycache__/mirrorlist_trigger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/__pycache__/package_creator.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/__pycache__/package_creator.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/keyring_trigger.py
+usr/lib/python3.11/site-packages/ahriman/core/support/mirrorlist_trigger.py
+usr/lib/python3.11/site-packages/ahriman/core/support/package_creator.py
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__pycache__/keyring_generator.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__pycache__/keyring_generator.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__pycache__/mirrorlist_generator.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__pycache__/mirrorlist_generator.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__pycache__/pkgbuild_generator.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/__pycache__/pkgbuild_generator.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/keyring_generator.py
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/mirrorlist_generator.py
+usr/lib/python3.11/site-packages/ahriman/core/support/pkgbuild/pkgbuild_generator.py
+usr/lib/python3.11/site-packages/ahriman/core/tree.py
+usr/lib/python3.11/site-packages/ahriman/core/triggers/
+usr/lib/python3.11/site-packages/ahriman/core/triggers/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/triggers/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/triggers/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/triggers/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/triggers/__pycache__/trigger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/triggers/__pycache__/trigger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/triggers/__pycache__/trigger_loader.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/triggers/__pycache__/trigger_loader.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/triggers/trigger.py
+usr/lib/python3.11/site-packages/ahriman/core/triggers/trigger_loader.py
+usr/lib/python3.11/site-packages/ahriman/core/upload/
+usr/lib/python3.11/site-packages/ahriman/core/upload/__init__.py
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/github.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/github.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/http_upload.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/http_upload.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/remote_service.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/remote_service.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/rsync.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/rsync.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/s3.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/s3.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/upload.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/upload.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/upload_trigger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/__pycache__/upload_trigger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/core/upload/github.py
+usr/lib/python3.11/site-packages/ahriman/core/upload/http_upload.py
+usr/lib/python3.11/site-packages/ahriman/core/upload/remote_service.py
+usr/lib/python3.11/site-packages/ahriman/core/upload/rsync.py
+usr/lib/python3.11/site-packages/ahriman/core/upload/s3.py
+usr/lib/python3.11/site-packages/ahriman/core/upload/upload.py
+usr/lib/python3.11/site-packages/ahriman/core/upload/upload_trigger.py
+usr/lib/python3.11/site-packages/ahriman/core/util.py
+usr/lib/python3.11/site-packages/ahriman/models/
+usr/lib/python3.11/site-packages/ahriman/models/__init__.py
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/action.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/action.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/aur_package.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/aur_package.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/auth_settings.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/auth_settings.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/build_status.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/build_status.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/changes.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/changes.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/context_key.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/context_key.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/counters.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/counters.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/internal_status.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/internal_status.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/log_handler.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/log_handler.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/log_record_id.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/log_record_id.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/migration.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/migration.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/migration_result.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/migration_result.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/package.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/package.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/package_description.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/package_description.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/package_source.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/package_source.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/packagers.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/packagers.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/pacman_synchronization.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/pacman_synchronization.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/pkgbuild_patch.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/pkgbuild_patch.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/process_status.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/process_status.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/property.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/property.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/remote_source.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/remote_source.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/report_settings.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/report_settings.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/repository_id.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/repository_id.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/repository_paths.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/repository_paths.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/result.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/result.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/sign_settings.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/sign_settings.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/smtp_ssl_settings.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/smtp_ssl_settings.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/upload_settings.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/upload_settings.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/user.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/user.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/user_access.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/user_access.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/waiter.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/waiter.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/worker.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/models/__pycache__/worker.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/models/action.py
+usr/lib/python3.11/site-packages/ahriman/models/aur_package.py
+usr/lib/python3.11/site-packages/ahriman/models/auth_settings.py
+usr/lib/python3.11/site-packages/ahriman/models/build_status.py
+usr/lib/python3.11/site-packages/ahriman/models/changes.py
+usr/lib/python3.11/site-packages/ahriman/models/context_key.py
+usr/lib/python3.11/site-packages/ahriman/models/counters.py
+usr/lib/python3.11/site-packages/ahriman/models/internal_status.py
+usr/lib/python3.11/site-packages/ahriman/models/log_handler.py
+usr/lib/python3.11/site-packages/ahriman/models/log_record_id.py
+usr/lib/python3.11/site-packages/ahriman/models/migration.py
+usr/lib/python3.11/site-packages/ahriman/models/migration_result.py
+usr/lib/python3.11/site-packages/ahriman/models/package.py
+usr/lib/python3.11/site-packages/ahriman/models/package_description.py
+usr/lib/python3.11/site-packages/ahriman/models/package_source.py
+usr/lib/python3.11/site-packages/ahriman/models/packagers.py
+usr/lib/python3.11/site-packages/ahriman/models/pacman_synchronization.py
+usr/lib/python3.11/site-packages/ahriman/models/pkgbuild_patch.py
+usr/lib/python3.11/site-packages/ahriman/models/process_status.py
+usr/lib/python3.11/site-packages/ahriman/models/property.py
+usr/lib/python3.11/site-packages/ahriman/models/remote_source.py
+usr/lib/python3.11/site-packages/ahriman/models/report_settings.py
+usr/lib/python3.11/site-packages/ahriman/models/repository_id.py
+usr/lib/python3.11/site-packages/ahriman/models/repository_paths.py
+usr/lib/python3.11/site-packages/ahriman/models/result.py
+usr/lib/python3.11/site-packages/ahriman/models/sign_settings.py
+usr/lib/python3.11/site-packages/ahriman/models/smtp_ssl_settings.py
+usr/lib/python3.11/site-packages/ahriman/models/upload_settings.py
+usr/lib/python3.11/site-packages/ahriman/models/user.py
+usr/lib/python3.11/site-packages/ahriman/models/user_access.py
+usr/lib/python3.11/site-packages/ahriman/models/waiter.py
+usr/lib/python3.11/site-packages/ahriman/models/worker.py
+usr/lib/python3.11/site-packages/ahriman/py.typed
+usr/lib/python3.11/site-packages/ahriman/web/
+usr/lib/python3.11/site-packages/ahriman/web/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/apispec.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/apispec.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/cors.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/cors.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/keys.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/keys.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/routes.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/routes.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/web.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/__pycache__/web.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/apispec.py
+usr/lib/python3.11/site-packages/ahriman/web/cors.py
+usr/lib/python3.11/site-packages/ahriman/web/keys.py
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/__pycache__/auth_handler.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/__pycache__/auth_handler.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/__pycache__/exception_handler.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/__pycache__/exception_handler.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/auth_handler.py
+usr/lib/python3.11/site-packages/ahriman/web/middlewares/exception_handler.py
+usr/lib/python3.11/site-packages/ahriman/web/routes.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/aur_package_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/aur_package_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/auth_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/auth_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/build_options_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/build_options_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/changes_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/changes_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/counters_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/counters_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/error_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/error_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/file_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/file_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/info_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/info_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/internal_status_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/internal_status_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/log_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/log_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/login_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/login_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/logs_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/logs_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/oauth2_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/oauth2_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_name_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_name_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_names_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_names_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_patch_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_patch_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_properties_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_properties_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_status_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/package_status_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/pagination_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/pagination_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/patch_name_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/patch_name_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/patch_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/patch_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/pgp_key_id_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/pgp_key_id_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/pgp_key_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/pgp_key_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/process_id_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/process_id_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/process_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/process_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/remote_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/remote_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/repository_id_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/repository_id_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/search_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/search_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/status_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/status_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/update_flags_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/update_flags_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/versioned_log_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/versioned_log_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/worker_schema.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/__pycache__/worker_schema.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/schemas/aur_package_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/auth_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/build_options_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/changes_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/counters_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/error_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/file_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/info_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/internal_status_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/log_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/login_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/logs_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/oauth2_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/package_name_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/package_names_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/package_patch_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/package_properties_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/package_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/package_status_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/pagination_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/patch_name_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/patch_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/pgp_key_id_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/pgp_key_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/process_id_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/process_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/remote_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/repository_id_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/search_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/status_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/update_flags_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/versioned_log_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/schemas/worker_schema.py
+usr/lib/python3.11/site-packages/ahriman/web/views/
+usr/lib/python3.11/site-packages/ahriman/web/views/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/base.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/base.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/index.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/index.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/static.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/static.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/status_view_guard.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/__pycache__/status_view_guard.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/api/
+usr/lib/python3.11/site-packages/ahriman/web/views/api/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/views/api/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/views/api/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/api/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/api/__pycache__/docs.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/api/__pycache__/docs.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/api/__pycache__/swagger.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/api/__pycache__/swagger.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/api/docs.py
+usr/lib/python3.11/site-packages/ahriman/web/views/api/swagger.py
+usr/lib/python3.11/site-packages/ahriman/web/views/base.py
+usr/lib/python3.11/site-packages/ahriman/web/views/index.py
+usr/lib/python3.11/site-packages/ahriman/web/views/static.py
+usr/lib/python3.11/site-packages/ahriman/web/views/status_view_guard.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/distributed/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/distributed/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/distributed/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/distributed/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/distributed/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/distributed/__pycache__/workers.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/distributed/__pycache__/workers.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/distributed/workers.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/add.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/add.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/pgp.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/pgp.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/process.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/process.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/rebuild.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/rebuild.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/remove.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/remove.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/request.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/request.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/search.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/search.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/update.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/update.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/upload.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/__pycache__/upload.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/add.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/pgp.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/process.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/rebuild.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/remove.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/request.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/search.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/update.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/service/upload.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/changes.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/changes.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/info.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/info.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/logs.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/logs.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/package.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/package.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/packages.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/packages.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/patch.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/patch.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/patches.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/patches.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/repositories.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/repositories.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/status.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/__pycache__/status.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/changes.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/info.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/logs.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/package.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/packages.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/patch.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/patches.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/repositories.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/status/status.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/__pycache__/login.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/__pycache__/login.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/__pycache__/logout.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/__pycache__/logout.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/login.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v1/user/logout.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/status/
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/status/__init__.py
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/status/__pycache__/
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/status/__pycache__/__init__.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/status/__pycache__/__init__.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/status/__pycache__/logs.cpython-311.opt-1.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/status/__pycache__/logs.cpython-311.pyc
+usr/lib/python3.11/site-packages/ahriman/web/views/v2/status/logs.py
+usr/lib/python3.11/site-packages/ahriman/web/web.py
+usr/lib/systemd/
+usr/lib/systemd/system/
+usr/lib/systemd/system/ahriman-daemon@.service
+usr/lib/systemd/system/ahriman-web.service
+usr/lib/systemd/system/ahriman-web@.service
+usr/lib/systemd/system/ahriman@.service
+usr/lib/systemd/system/ahriman@.timer
+usr/lib/sysusers.d/
+usr/lib/sysusers.d/ahriman.conf
+usr/lib/tmpfiles.d/
+usr/lib/tmpfiles.d/ahriman.conf
+usr/share/
+usr/share/ahriman/
+usr/share/ahriman/settings/
+usr/share/ahriman/settings/ahriman.ini
+usr/share/ahriman/settings/ahriman.ini.d/
+usr/share/ahriman/settings/ahriman.ini.d/logging.ini
+usr/share/ahriman/templates/
+usr/share/ahriman/templates/api.jinja2
+usr/share/ahriman/templates/build-status.jinja2
+usr/share/ahriman/templates/build-status/
+usr/share/ahriman/templates/build-status/alerts.jinja2
+usr/share/ahriman/templates/build-status/key-import-modal.jinja2
+usr/share/ahriman/templates/build-status/login-modal.jinja2
+usr/share/ahriman/templates/build-status/package-add-modal.jinja2
+usr/share/ahriman/templates/build-status/package-info-modal.jinja2
+usr/share/ahriman/templates/build-status/package-rebuild-modal.jinja2
+usr/share/ahriman/templates/build-status/table.jinja2
+usr/share/ahriman/templates/email-index.jinja2
+usr/share/ahriman/templates/error.jinja2
+usr/share/ahriman/templates/repo-index.jinja2
+usr/share/ahriman/templates/shell
+usr/share/ahriman/templates/static/
+usr/share/ahriman/templates/static/favicon.ico
+usr/share/ahriman/templates/static/logo.svg
+usr/share/ahriman/templates/telegram-index.jinja2
+usr/share/ahriman/templates/utils/
+usr/share/ahriman/templates/utils/bootstrap-scripts.jinja2
+usr/share/ahriman/templates/utils/style.jinja2
+usr/share/bash-completion/
+usr/share/bash-completion/completions/
+usr/share/bash-completion/completions/_ahriman
+usr/share/man/
+usr/share/man/man1/
+usr/share/man/man1/ahriman.1.gz
+usr/share/zsh/
+usr/share/zsh/site-functions/
+usr/share/zsh/site-functions/_ahriman
+
+%BACKUP%
+etc/ahriman.ini ddea8d127352019bc9ca803b389497ab
+etc/ahriman.ini.d/logging.ini 41a1e1ecfa3fcf86ed81f5a42a83481f
+