Compare commits

..

9 Commits

Author SHA1 Message Date
4b8177ae6a Release 2.19.2 2026-01-25 10:43:06 +02:00
f13a2fde85 type: use as keyword in case match 2026-01-25 10:39:15 +02:00
847c029c46 fix: correctly process trigger repo specific settings in validator (see #154) 2026-01-25 10:24:01 +02:00
a647783252 feat: expose repository name and architecure in configuration if available
In some cases there are reference to current repository settings. In
order to handle it correctly two ro options have been added

Related to #154
2026-01-25 10:23:30 +02:00
5cfcb5c3e8 fix: careful handling of file permissions during initialization
It has been found that during cold start (e.g. in docker container),
some permissions are invalid. In order to handle that, some operations
are not guarded with RepositoryPaths.preserve_root guard

In addition, it has been also found that in some cases (e.g. web server
start) migrations are performed on empty repository identifier which may
lead to wrong data (see also 435375721d),
as well as some unexpected results during database operations. In order
to handle that, now all watcher instances have their own databases (and
configurations)
2026-01-25 10:20:29 +02:00
6280c9dbe6 Release 2.19.1 2025-07-14 21:41:02 +03:00
3d1fdd5517 fix: fix migrations on empty repositories 2025-07-14 21:37:43 +03:00
ab022071e8 fix: trim provides/depends versions and lookup provides through pkgname
(#150)

Current implementation did it in wrong way. First of all, there was a
lookup through pkgbase instead of pkgname, which lead to errors, because
aur api doesn't allow to search by pkgbase (as well as provides is
basically pkgname instead)

It also was found that dependencies resolution lookup has been performed
by using raw packages array, which can include versions, descriptions
etc
2025-07-14 21:37:35 +03:00
a01f76df42 fix: separate ua by spaces 2025-07-14 21:37:25 +03:00
37 changed files with 578 additions and 116 deletions

View File

@@ -138,6 +138,8 @@ Build related configuration. Group name can refer to architecture, e.g. ``build:
Base repository settings. Base repository settings.
* ``architecture`` - repository architecture, string. This field is read-only and generated automatically from run options if possible.
* ``name`` - repository name, string. This field is read-only and generated automatically from run options if possible.
* ``root`` - root path for application, string, required. * ``root`` - root path for application, string, required.
``sign:*`` groups ``sign:*`` groups

View File

@@ -2,7 +2,7 @@
pkgbase='ahriman' pkgbase='ahriman'
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web') pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
pkgver=2.19.0 pkgver=2.19.2
pkgrel=1 pkgrel=1
pkgdesc="ArcH linux ReposItory MANager" pkgdesc="ArcH linux ReposItory MANager"
arch=('any') arch=('any')

View File

@@ -674,6 +674,7 @@ _shtab_ahriman() {
if [[ "$current_action_nargs" != "*" ]] && \ if [[ "$current_action_nargs" != "*" ]] && \
[[ "$current_action_nargs" != "+" ]] && \ [[ "$current_action_nargs" != "+" ]] && \
[[ "$current_action_nargs" != "?" ]] && \
[[ "$current_action_nargs" != *"..." ]] && \ [[ "$current_action_nargs" != *"..." ]] && \
(( $word_index + 1 - $current_action_args_start_index - $pos_only >= \ (( $word_index + 1 - $current_action_args_start_index - $pos_only >= \
$current_action_nargs )); then $current_action_nargs )); then

View File

@@ -1,4 +1,4 @@
.TH AHRIMAN "1" "2025\-06\-29" "ahriman 2.19.0" "ArcH linux ReposItory MANager" .TH AHRIMAN "1" "2026\-01\-25" "ahriman 2.19.2" "ArcH linux ReposItory MANager"
.SH NAME .SH NAME
ahriman \- ArcH linux ReposItory MANager ahriman \- ArcH linux ReposItory MANager
.SH SYNOPSIS .SH SYNOPSIS

View File

@@ -99,6 +99,9 @@ _shtab_ahriman_options=(
"--wait-timeout[wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of zero value, the application will wait infinitely (default\: -1)]:wait_timeout:" "--wait-timeout[wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of zero value, the application will wait infinitely (default\: -1)]:wait_timeout:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_defaults_added=0
_shtab_ahriman_add_options=( _shtab_ahriman_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:" {--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
@@ -113,6 +116,9 @@ _shtab_ahriman_add_options=(
"(*):package source (base name, path to local files, remote URL):" "(*):package source (base name, path to local files, remote URL):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_add_defaults_added=0
_shtab_ahriman_aur_search_options=( _shtab_ahriman_aur_search_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]" {-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@@ -121,6 +127,9 @@ _shtab_ahriman_aur_search_options=(
"(*):search terms, can be specified multiple times, the result will match all terms:" "(*):search terms, can be specified multiple times, the result will match all terms:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_aur_search_defaults_added=0
_shtab_ahriman_check_options=( _shtab_ahriman_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:" {--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
@@ -131,6 +140,9 @@ _shtab_ahriman_check_options=(
"(*)::filter check by package base (default\: None):" "(*)::filter check by package base (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_check_defaults_added=0
_shtab_ahriman_clean_options=( _shtab_ahriman_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches (default\: False)]:cache:" {--cache,--no-cache}"[clear directory with package caches (default\: False)]:cache:"
@@ -140,6 +152,9 @@ _shtab_ahriman_clean_options=(
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: False)]:pacman:" {--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: False)]:pacman:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_clean_defaults_added=0
_shtab_ahriman_config_options=( _shtab_ahriman_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--info,--no-info}"[show additional information, e.g. configuration files (default\: True)]:info:" {--info,--no-info}"[show additional information, e.g. configuration files (default\: True)]:info:"
@@ -148,11 +163,17 @@ _shtab_ahriman_config_options=(
":filter settings by key (default\: None):" ":filter settings by key (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_config_defaults_added=0
_shtab_ahriman_config_validate_options=( _shtab_ahriman_config_validate_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]" {-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_config_validate_defaults_added=0
_shtab_ahriman_copy_options=( _shtab_ahriman_copy_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]" {-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@@ -161,6 +182,9 @@ _shtab_ahriman_copy_options=(
"(*):package base:" "(*):package base:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_copy_defaults_added=0
_shtab_ahriman_daemon_options=( _shtab_ahriman_daemon_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:" {-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
@@ -178,25 +202,40 @@ _shtab_ahriman_daemon_options=(
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]" "*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_daemon_defaults_added=0
_shtab_ahriman_help_options=( _shtab_ahriman_help_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
":show help message for specific command (default\: None):" ":show help message for specific command (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_help_defaults_added=0
_shtab_ahriman_help_commands_unsafe_options=( _shtab_ahriman_help_commands_unsafe_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1 otherwise (default\: None):" "(*)::instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1 otherwise (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_help_commands_unsafe_defaults_added=0
_shtab_ahriman_help_updates_options=( _shtab_ahriman_help_updates_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit code if updates available (default\: False)]" {-e,--exit-code}"[return non-zero exit code if updates available (default\: False)]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_help_updates_defaults_added=0
_shtab_ahriman_help_version_options=( _shtab_ahriman_help_version_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_help_version_defaults_added=0
_shtab_ahriman_init_options=( _shtab_ahriman_init_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:" "--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
@@ -213,12 +252,18 @@ _shtab_ahriman_init_options=(
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:" "--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_init_defaults_added=0
_shtab_ahriman_key_import_options=( _shtab_ahriman_key_import_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--key-server[key server for key import (default\: keyserver.ubuntu.com)]:key_server:" "--key-server[key server for key import (default\: keyserver.ubuntu.com)]:key_server:"
":PGP key to import from public server:" ":PGP key to import from public server:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_key_import_defaults_added=0
_shtab_ahriman_package_add_options=( _shtab_ahriman_package_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:" {--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
@@ -233,17 +278,26 @@ _shtab_ahriman_package_add_options=(
"(*):package source (base name, path to local files, remote URL):" "(*):package source (base name, path to local files, remote URL):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_package_add_defaults_added=0
_shtab_ahriman_package_changes_options=( _shtab_ahriman_package_changes_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]" {-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
":package base:" ":package base:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_package_changes_defaults_added=0
_shtab_ahriman_package_changes_remove_options=( _shtab_ahriman_package_changes_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
":package base:" ":package base:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_package_changes_remove_defaults_added=0
_shtab_ahriman_package_copy_options=( _shtab_ahriman_package_copy_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]" {-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@@ -252,11 +306,17 @@ _shtab_ahriman_package_copy_options=(
"(*):package base:" "(*):package base:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_package_copy_defaults_added=0
_shtab_ahriman_package_remove_options=( _shtab_ahriman_package_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"(*):package name or base:" "(*):package name or base:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_package_remove_defaults_added=0
_shtab_ahriman_package_status_options=( _shtab_ahriman_package_status_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--ahriman[get service status itself (default\: False)]" "--ahriman[get service status itself (default\: False)]"
@@ -266,17 +326,26 @@ _shtab_ahriman_package_status_options=(
"(*)::filter status by package base (default\: None):" "(*)::filter status by package base (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_package_status_defaults_added=0
_shtab_ahriman_package_status_remove_options=( _shtab_ahriman_package_status_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"(*):remove specified packages from status page:" "(*):remove specified packages from status page:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_package_status_remove_defaults_added=0
_shtab_ahriman_package_status_update_options=( _shtab_ahriman_package_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new package build status (default\: success)]:status:(unknown pending building failed success)" {-s,--status}"[new package build status (default\: success)]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated (default\: None):" "(*)::set status for specified packages. If no packages supplied, service status will be updated (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_package_status_update_defaults_added=0
_shtab_ahriman_package_update_options=( _shtab_ahriman_package_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:" {--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
@@ -291,6 +360,9 @@ _shtab_ahriman_package_update_options=(
"(*):package source (base name, path to local files, remote URL):" "(*):package source (base name, path to local files, remote URL):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_package_update_defaults_added=0
_shtab_ahriman_patch_add_options=( _shtab_ahriman_patch_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
":package base:" ":package base:"
@@ -298,6 +370,9 @@ _shtab_ahriman_patch_add_options=(
":path to file which contains function or variable value. If not set, the value will be read from stdin (default\: None):" ":path to file which contains function or variable value. If not set, the value will be read from stdin (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_patch_add_defaults_added=0
_shtab_ahriman_patch_list_options=( _shtab_ahriman_patch_list_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]" {-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@@ -305,18 +380,27 @@ _shtab_ahriman_patch_list_options=(
":package base:" ":package base:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_patch_list_defaults_added=0
_shtab_ahriman_patch_remove_options=( _shtab_ahriman_patch_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"*"{-v,--variable}"[should be used for single-function patches in case if you wold like to remove only specified PKGBUILD variables. In case if not set, it will remove all patches related to the package (default\: None)]:variable:" "*"{-v,--variable}"[should be used for single-function patches in case if you wold like to remove only specified PKGBUILD variables. In case if not set, it will remove all patches related to the package (default\: None)]:variable:"
":package base:" ":package base:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_patch_remove_defaults_added=0
_shtab_ahriman_patch_set_add_options=( _shtab_ahriman_patch_set_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"*"{-t,--track}"[files which has to be tracked (default\: \[\'\*.diff\', \'\*.patch\'\])]:track:" "*"{-t,--track}"[files which has to be tracked (default\: \[\'\*.diff\', \'\*.patch\'\])]:track:"
":path to directory with changed files for patch addition\/update:" ":path to directory with changed files for patch addition\/update:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_patch_set_add_defaults_added=0
_shtab_ahriman_rebuild_options=( _shtab_ahriman_rebuild_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"*--depends-on[only rebuild packages that depend on specified packages (default\: None)]:depends_on:" "*--depends-on[only rebuild packages that depend on specified packages (default\: None)]:depends_on:"
@@ -328,21 +412,33 @@ _shtab_ahriman_rebuild_options=(
{-u,--username}"[build as user (default\: None)]:username:" {-u,--username}"[build as user (default\: None)]:username:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_rebuild_defaults_added=0
_shtab_ahriman_remove_options=( _shtab_ahriman_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"(*):package name or base:" "(*):package name or base:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_remove_defaults_added=0
_shtab_ahriman_remove_unknown_options=( _shtab_ahriman_remove_unknown_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for packages without removal (default\: False)]" "--dry-run[just perform check for packages without removal (default\: False)]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_remove_unknown_defaults_added=0
_shtab_ahriman_repo_backup_options=( _shtab_ahriman_repo_backup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
":path of the output archive:" ":path of the output archive:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_backup_defaults_added=0
_shtab_ahriman_repo_check_options=( _shtab_ahriman_repo_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:" {--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
@@ -353,6 +449,9 @@ _shtab_ahriman_repo_check_options=(
"(*)::filter check by package base (default\: None):" "(*)::filter check by package base (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_check_defaults_added=0
_shtab_ahriman_repo_clean_options=( _shtab_ahriman_repo_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches (default\: False)]:cache:" {--cache,--no-cache}"[clear directory with package caches (default\: False)]:cache:"
@@ -362,6 +461,9 @@ _shtab_ahriman_repo_clean_options=(
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: False)]:pacman:" {--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: False)]:pacman:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_clean_defaults_added=0
_shtab_ahriman_repo_config_options=( _shtab_ahriman_repo_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--info,--no-info}"[show additional information, e.g. configuration files (default\: True)]:info:" {--info,--no-info}"[show additional information, e.g. configuration files (default\: True)]:info:"
@@ -370,19 +472,31 @@ _shtab_ahriman_repo_config_options=(
":filter settings by key (default\: None):" ":filter settings by key (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_config_defaults_added=0
_shtab_ahriman_repo_config_validate_options=( _shtab_ahriman_repo_config_validate_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]" {-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_config_validate_defaults_added=0
_shtab_ahriman_repo_create_keyring_options=( _shtab_ahriman_repo_create_keyring_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_create_keyring_defaults_added=0
_shtab_ahriman_repo_create_mirrorlist_options=( _shtab_ahriman_repo_create_mirrorlist_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_create_mirrorlist_defaults_added=0
_shtab_ahriman_repo_daemon_options=( _shtab_ahriman_repo_daemon_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:" {-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
@@ -400,6 +514,9 @@ _shtab_ahriman_repo_daemon_options=(
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]" "*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_daemon_defaults_added=0
_shtab_ahriman_repo_init_options=( _shtab_ahriman_repo_init_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:" "--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
@@ -416,6 +533,9 @@ _shtab_ahriman_repo_init_options=(
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:" "--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_init_defaults_added=0
_shtab_ahriman_repo_rebuild_options=( _shtab_ahriman_repo_rebuild_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"*--depends-on[only rebuild packages that depend on specified packages (default\: None)]:depends_on:" "*--depends-on[only rebuild packages that depend on specified packages (default\: None)]:depends_on:"
@@ -427,21 +547,33 @@ _shtab_ahriman_repo_rebuild_options=(
{-u,--username}"[build as user (default\: None)]:username:" {-u,--username}"[build as user (default\: None)]:username:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_rebuild_defaults_added=0
_shtab_ahriman_repo_remove_unknown_options=( _shtab_ahriman_repo_remove_unknown_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for packages without removal (default\: False)]" "--dry-run[just perform check for packages without removal (default\: False)]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_remove_unknown_defaults_added=0
_shtab_ahriman_repo_report_options=( _shtab_ahriman_repo_report_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_report_defaults_added=0
_shtab_ahriman_repo_restore_options=( _shtab_ahriman_repo_restore_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-o,--output}"[root path of the extracted files (default\: \/)]:output:" {-o,--output}"[root path of the extracted files (default\: \/)]:output:"
":path of the input archive:" ":path of the input archive:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_restore_defaults_added=0
_shtab_ahriman_repo_setup_options=( _shtab_ahriman_repo_setup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:" "--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
@@ -458,11 +590,17 @@ _shtab_ahriman_repo_setup_options=(
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:" "--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_setup_defaults_added=0
_shtab_ahriman_repo_sign_options=( _shtab_ahriman_repo_sign_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::sign only specified packages (default\: None):" "(*)::sign only specified packages (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_sign_defaults_added=0
_shtab_ahriman_repo_statistics_options=( _shtab_ahriman_repo_statistics_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--chart[create updates chart and save it to the specified path (default\: None)]:chart:" "--chart[create updates chart and save it to the specified path (default\: None)]:chart:"
@@ -474,25 +612,40 @@ _shtab_ahriman_repo_statistics_options=(
":fetch only events for the specified package (default\: None):" ":fetch only events for the specified package (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_statistics_defaults_added=0
_shtab_ahriman_repo_status_update_options=( _shtab_ahriman_repo_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new status (default\: success)]:status:(unknown pending building failed success)" {-s,--status}"[new status (default\: success)]:status:(unknown pending building failed success)"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_status_update_defaults_added=0
_shtab_ahriman_repo_sync_options=( _shtab_ahriman_repo_sync_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_sync_defaults_added=0
_shtab_ahriman_repo_tree_options=( _shtab_ahriman_repo_tree_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-p,--partitions}"[also divide packages by independent partitions (default\: 1)]:partitions:" {-p,--partitions}"[also divide packages by independent partitions (default\: 1)]:partitions:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_tree_defaults_added=0
_shtab_ahriman_repo_triggers_options=( _shtab_ahriman_repo_triggers_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::instead of running all triggers as set by configuration, just process specified ones in order of mention (default\: None):" "(*)::instead of running all triggers as set by configuration, just process specified ones in order of mention (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_triggers_defaults_added=0
_shtab_ahriman_repo_update_options=( _shtab_ahriman_repo_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:" {--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
@@ -510,15 +663,24 @@ _shtab_ahriman_repo_update_options=(
"(*)::filter check by package base (default\: None):" "(*)::filter check by package base (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_repo_update_defaults_added=0
_shtab_ahriman_report_options=( _shtab_ahriman_report_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_report_defaults_added=0
_shtab_ahriman_run_options=( _shtab_ahriman_run_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"(*):command to be run (quoted) without \`\`ahriman\`\`:" "(*):command to be run (quoted) without \`\`ahriman\`\`:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_run_defaults_added=0
_shtab_ahriman_search_options=( _shtab_ahriman_search_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]" {-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@@ -527,6 +689,9 @@ _shtab_ahriman_search_options=(
"(*):search terms, can be specified multiple times, the result will match all terms:" "(*):search terms, can be specified multiple times, the result will match all terms:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_search_defaults_added=0
_shtab_ahriman_service_clean_options=( _shtab_ahriman_service_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches (default\: False)]:cache:" {--cache,--no-cache}"[clear directory with package caches (default\: False)]:cache:"
@@ -536,6 +701,9 @@ _shtab_ahriman_service_clean_options=(
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: False)]:pacman:" {--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: False)]:pacman:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_service_clean_defaults_added=0
_shtab_ahriman_service_config_options=( _shtab_ahriman_service_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--info,--no-info}"[show additional information, e.g. configuration files (default\: True)]:info:" {--info,--no-info}"[show additional information, e.g. configuration files (default\: True)]:info:"
@@ -544,27 +712,42 @@ _shtab_ahriman_service_config_options=(
":filter settings by key (default\: None):" ":filter settings by key (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_service_config_defaults_added=0
_shtab_ahriman_service_config_validate_options=( _shtab_ahriman_service_config_validate_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]" {-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_service_config_validate_defaults_added=0
_shtab_ahriman_service_key_import_options=( _shtab_ahriman_service_key_import_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--key-server[key server for key import (default\: keyserver.ubuntu.com)]:key_server:" "--key-server[key server for key import (default\: keyserver.ubuntu.com)]:key_server:"
":PGP key to import from public server:" ":PGP key to import from public server:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_service_key_import_defaults_added=0
_shtab_ahriman_service_repositories_options=( _shtab_ahriman_service_repositories_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--id-only,--no-id-only}"[show machine readable identifier instead (default\: False)]:id_only:" {--id-only,--no-id-only}"[show machine readable identifier instead (default\: False)]:id_only:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_service_repositories_defaults_added=0
_shtab_ahriman_service_run_options=( _shtab_ahriman_service_run_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"(*):command to be run (quoted) without \`\`ahriman\`\`:" "(*):command to be run (quoted) without \`\`ahriman\`\`:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_service_run_defaults_added=0
_shtab_ahriman_service_setup_options=( _shtab_ahriman_service_setup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:" "--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
@@ -581,16 +764,25 @@ _shtab_ahriman_service_setup_options=(
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:" "--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_service_setup_defaults_added=0
_shtab_ahriman_service_shell_options=( _shtab_ahriman_service_shell_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-o,--output}"[output commands and result to the file (default\: None)]:output:" {-o,--output}"[output commands and result to the file (default\: None)]:output:"
":instead of dropping into shell, just execute the specified code (default\: None):" ":instead of dropping into shell, just execute the specified code (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_service_shell_defaults_added=0
_shtab_ahriman_service_tree_migrate_options=( _shtab_ahriman_service_tree_migrate_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_service_tree_migrate_defaults_added=0
_shtab_ahriman_setup_options=( _shtab_ahriman_setup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:" "--build-as-user[force makepkg user to the specific one (default\: None)]:build_as_user:"
@@ -607,17 +799,26 @@ _shtab_ahriman_setup_options=(
"--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:" "--web-unix-socket[path to unix socket used for interprocess communications (default\: None)]:web_unix_socket:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_setup_defaults_added=0
_shtab_ahriman_shell_options=( _shtab_ahriman_shell_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-o,--output}"[output commands and result to the file (default\: None)]:output:" {-o,--output}"[output commands and result to the file (default\: None)]:output:"
":instead of dropping into shell, just execute the specified code (default\: None):" ":instead of dropping into shell, just execute the specified code (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_shell_defaults_added=0
_shtab_ahriman_sign_options=( _shtab_ahriman_sign_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::sign only specified packages (default\: None):" "(*)::sign only specified packages (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_sign_defaults_added=0
_shtab_ahriman_status_options=( _shtab_ahriman_status_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--ahriman[get service status itself (default\: False)]" "--ahriman[get service status itself (default\: False)]"
@@ -627,16 +828,25 @@ _shtab_ahriman_status_options=(
"(*)::filter status by package base (default\: None):" "(*)::filter status by package base (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_status_defaults_added=0
_shtab_ahriman_status_update_options=( _shtab_ahriman_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new package build status (default\: success)]:status:(unknown pending building failed success)" {-s,--status}"[new package build status (default\: success)]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated (default\: None):" "(*)::set status for specified packages. If no packages supplied, service status will be updated (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_status_update_defaults_added=0
_shtab_ahriman_sync_options=( _shtab_ahriman_sync_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_sync_defaults_added=0
_shtab_ahriman_update_options=( _shtab_ahriman_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:" {--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
@@ -654,6 +864,9 @@ _shtab_ahriman_update_options=(
"(*)::filter check by package base (default\: None):" "(*)::filter check by package base (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_update_defaults_added=0
_shtab_ahriman_user_add_options=( _shtab_ahriman_user_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
"--key[optional PGP key used by this user. The private key must be imported (default\: None)]:key:" "--key[optional PGP key used by this user. The private key must be imported (default\: None)]:key:"
@@ -663,6 +876,9 @@ _shtab_ahriman_user_add_options=(
":username for web service:" ":username for web service:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_user_add_defaults_added=0
_shtab_ahriman_user_list_options=( _shtab_ahriman_user_list_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]" {-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
@@ -670,26 +886,42 @@ _shtab_ahriman_user_list_options=(
":filter users by username (default\: None):" ":filter users by username (default\: None):"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_user_list_defaults_added=0
_shtab_ahriman_user_remove_options=( _shtab_ahriman_user_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
":username for web service:" ":username for web service:"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_user_remove_defaults_added=0
_shtab_ahriman_version_options=( _shtab_ahriman_version_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_version_defaults_added=0
_shtab_ahriman_web_options=( _shtab_ahriman_web_options=(
"(- : *)"{-h,--help}"[show this help message and exit]" "(- : *)"{-h,--help}"[show this help message and exit]"
) )
# guard to ensure default positional specs are added only once per session
_shtab_ahriman_web_defaults_added=0
_shtab_ahriman() { _shtab_ahriman() {
local context state line curcontext="$curcontext" one_or_more='(-)*' remainder='(*)' local context state line curcontext="$curcontext" one_or_more='(*)' remainder='(-)*' default='*::: :->ahriman'
if ((${_shtab_ahriman_options[(I)${(q)one_or_more}*]} + ${_shtab_ahriman_options[(I)${(q)remainder}*]} == 0)); then # noqa: E501 # Add default positional/remainder specs only if none exist, and only once per session
if (( ! _shtab_ahriman_defaults_added )); then
if (( ${_shtab_ahriman_options[(I)${(q)one_or_more}*]} + ${_shtab_ahriman_options[(I)${(q)remainder}*]} + ${_shtab_ahriman_options[(I)${(q)default}]} == 0 )); then
_shtab_ahriman_options+=(': :_shtab_ahriman_commands' '*::: :->ahriman') _shtab_ahriman_options+=(': :_shtab_ahriman_commands' '*::: :->ahriman')
fi fi
_shtab_ahriman_defaults_added=1
fi
_arguments -C -s $_shtab_ahriman_options _arguments -C -s $_shtab_ahriman_options
case $state in case $state in

View File

@@ -8,7 +8,7 @@ services:
AHRIMAN_OUTPUT: console AHRIMAN_OUTPUT: console
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD} AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
AHRIMAN_PORT: 8080 AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
AHRIMAN_REPOSITORY: ahriman-demo AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock

View File

@@ -8,7 +8,7 @@ services:
AHRIMAN_OUTPUT: console AHRIMAN_OUTPUT: console
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD} AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
AHRIMAN_PORT: 8080 AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
AHRIMAN_REPOSITORY: ahriman-demo AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock

View File

@@ -8,7 +8,7 @@ services:
AHRIMAN_OUTPUT: console AHRIMAN_OUTPUT: console
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD} AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
AHRIMAN_PORT: 8080 AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
AHRIMAN_REPOSITORY: ahriman-demo AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
@@ -62,7 +62,7 @@ services:
AHRIMAN_OUTPUT: console AHRIMAN_OUTPUT: console
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD} AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
AHRIMAN_PORT: 8080 AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
AHRIMAN_REPOSITORY: ahriman-demo AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_REPOSITORY_SERVER: http://frontend/repo/$$repo/$$arch AHRIMAN_REPOSITORY_SERVER: http://frontend/repo/$$repo/$$arch

View File

@@ -12,7 +12,7 @@ services:
AHRIMAN_PACMAN_MIRROR: https://de.mirror.archlinux32.org/$$arch/$$repo AHRIMAN_PACMAN_MIRROR: https://de.mirror.archlinux32.org/$$arch/$$repo
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD} AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
AHRIMAN_PORT: 8080 AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
AHRIMAN_REPOSITORY: ahriman-demo AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock

View File

@@ -8,8 +8,8 @@ services:
AHRIMAN_OUTPUT: console AHRIMAN_OUTPUT: console
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD} AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
AHRIMAN_PORT: 8080 AHRIMAN_PORT: 8080
AHRIMAN_POSTSETUP_COMMAND: ahriman --architecture x86_64 --repository another-demo service-setup --build-as-user ahriman --packager 'ahriman bot <ahriman@example.com>' AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full AHRIMAN_PRESETUP_COMMAND: ahriman --architecture x86_64 --repository another-demo service-setup --build-as-user ahriman --packager 'ahriman bot <ahriman@example.com>'
AHRIMAN_REPOSITORY: ahriman-demo AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock

View File

@@ -9,7 +9,7 @@ services:
AHRIMAN_OAUTH_CLIENT_SECRET: ${AHRIMAN_OAUTH_CLIENT_SECRET} AHRIMAN_OAUTH_CLIENT_SECRET: ${AHRIMAN_OAUTH_CLIENT_SECRET}
AHRIMAN_OUTPUT: console AHRIMAN_OUTPUT: console
AHRIMAN_PORT: 8080 AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: sudo -u ahriman ahriman user-add ${AHRIMAN_OAUTH_USER} -R full -p "" AHRIMAN_POSTSETUP_COMMAND: sudo -u ahriman ahriman user-add ${AHRIMAN_OAUTH_USER} -R full -p ""
AHRIMAN_REPOSITORY: ahriman-demo AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock

View File

@@ -6,7 +6,7 @@ services:
environment: environment:
AHRIMAN_DEBUG: yes AHRIMAN_DEBUG: yes
AHRIMAN_OUTPUT: console AHRIMAN_OUTPUT: console
AHRIMAN_PRESETUP_COMMAND: sudo -u ahriman gpg --import /run/secrets/key AHRIMAN_POSTSETUP_COMMAND: sudo -u ahriman gpg --import /run/secrets/key
AHRIMAN_REPOSITORY: ahriman-demo AHRIMAN_REPOSITORY: ahriman-demo
configs: configs:

View File

@@ -8,7 +8,7 @@ services:
AHRIMAN_OUTPUT: console AHRIMAN_OUTPUT: console
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD} AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
AHRIMAN_PORT: 8080 AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
AHRIMAN_REPOSITORY: ahriman-demo AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock

View File

@@ -17,4 +17,4 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
__version__ = "2.19.0" __version__ = "2.19.2"

View File

@@ -72,6 +72,7 @@ class Setup(Handler):
application = Application(repository_id, configuration, report=report) application = Application(repository_id, configuration, report=report)
with application.repository.paths.preserve_owner():
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths) Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths)
Setup.executable_create(application.repository.paths, repository_id) Setup.executable_create(application.repository.paths, repository_id)
repository_server = f"file://{application.repository.paths.repository}" if args.server is None else args.server repository_server = f"file://{application.repository.paths.repository}" if args.server is None else args.server
@@ -280,6 +281,5 @@ class Setup(Handler):
command = Setup.build_command(paths.root, repository_id) command = Setup.build_command(paths.root, repository_id)
command.unlink(missing_ok=True) command.unlink(missing_ok=True)
command.symlink_to(Setup.ARCHBUILD_COMMAND_PATH) command.symlink_to(Setup.ARCHBUILD_COMMAND_PATH)
paths.chown(command) # we would like to keep owner inside ahriman's home
arguments = [_set_service_setup_parser] arguments = [_set_service_setup_parser]

View File

@@ -52,7 +52,7 @@ class Validate(Handler):
""" """
from ahriman.core.configuration.validator import Validator from ahriman.core.configuration.validator import Validator
schema = Validate.schema(repository_id, configuration) schema = Validate.schema(configuration)
validator = Validator(configuration=configuration, schema=schema) validator = Validator(configuration=configuration, schema=schema)
if validator.validate(configuration.dump()): if validator.validate(configuration.dump()):
@@ -83,12 +83,11 @@ class Validate(Handler):
return parser return parser
@staticmethod @staticmethod
def schema(repository_id: RepositoryId, configuration: Configuration) -> ConfigurationSchema: def schema(configuration: Configuration) -> ConfigurationSchema:
""" """
get schema with triggers get schema with triggers
Args: Args:
repository_id(RepositoryId): repository unique identifier
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
Returns: Returns:
@@ -107,12 +106,12 @@ class Validate(Handler):
continue continue
# default settings if any # default settings if any
for schema_name, schema in trigger_class.configuration_schema(repository_id, None).items(): for schema_name, schema in trigger_class.configuration_schema(None).items():
erased = Validate.schema_erase_required(copy.deepcopy(schema)) erased = Validate.schema_erase_required(copy.deepcopy(schema))
root[schema_name] = Validate.schema_merge(root.get(schema_name, {}), erased) root[schema_name] = Validate.schema_merge(root.get(schema_name, {}), erased)
# settings according to enabled triggers # settings according to enabled triggers
for schema_name, schema in trigger_class.configuration_schema(repository_id, configuration).items(): for schema_name, schema in trigger_class.configuration_schema(configuration).items():
root[schema_name] = Validate.schema_merge(root.get(schema_name, {}), copy.deepcopy(schema)) root[schema_name] = Validate.schema_merge(root.get(schema_name, {}), copy.deepcopy(schema))
return root return root

View File

@@ -130,8 +130,8 @@ class Pacman(LazyLogging):
return # database for some reason deos not exist return # database for some reason deos not exist
self.logger.info("copy pacman database %s from operating system root to ahriman's home %s", src, dst) self.logger.info("copy pacman database %s from operating system root to ahriman's home %s", src, dst)
with self.repository_paths.preserve_owner(dst.parent):
shutil.copy(src, dst) shutil.copy(src, dst)
self.repository_paths.chown(dst)
def database_init(self, handle: Handle, repository: str, architecture: str) -> DB: def database_init(self, handle: Handle, repository: str, architecture: str) -> DB:
""" """
@@ -267,7 +267,8 @@ class Pacman(LazyLogging):
Package: list of packages which were returned by the query Package: list of packages which were returned by the query
""" """
def is_package_provided(package: Package) -> bool: def is_package_provided(package: Package) -> bool:
return package_name in package.provides provides = [trim_package(name) for name in package.provides]
return package_name in provides
for database in self.handle.get_syncdbs(): for database in self.handle.get_syncdbs():
yield from filter(is_package_provided, database.search(package_name)) yield from filter(is_package_provided, database.search(package_name))

View File

@@ -146,7 +146,7 @@ class AUR(Remote):
# search api provides reduced models # search api provides reduced models
for stub in self.package_search(package_name, pacman=pacman, search_by="provides") for stub in self.package_search(package_name, pacman=pacman, search_by="provides")
# verity that found package actually provides it # verity that found package actually provides it
if package_name in (package := self.package_info(stub.package_base, pacman=pacman)).provides if package_name in (package := self.package_info(stub.name, pacman=pacman)).provides
] ]
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]: def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:

View File

@@ -41,7 +41,6 @@ class Configuration(configparser.RawConfigParser):
SYSTEM_CONFIGURATION_PATH(Path): (class attribute) default system configuration path distributed by package SYSTEM_CONFIGURATION_PATH(Path): (class attribute) default system configuration path distributed by package
includes(list[Path]): list of includes which were read includes(list[Path]): list of includes which were read
path(Path | None): path to root configuration file path(Path | None): path to root configuration file
repository_id(RepositoryId | None): repository unique identifier
Examples: Examples:
Configuration class provides additional method in order to handle application configuration. Since this class is Configuration class provides additional method in order to handle application configuration. Since this class is
@@ -91,7 +90,7 @@ class Configuration(configparser.RawConfigParser):
}, },
) )
self.repository_id: RepositoryId | None = None self._repository_id: RepositoryId | None = None
self.path: Path | None = None self.path: Path | None = None
self.includes: list[Path] = [] self.includes: list[Path] = []
@@ -126,6 +125,32 @@ class Configuration(configparser.RawConfigParser):
""" """
return self.getpath("settings", "logging") return self.getpath("settings", "logging")
@property
def repository_id(self) -> RepositoryId | None:
"""
repository identifier
Returns:
RepositoryId: repository unique identifier
"""
return self._repository_id
@repository_id.setter
def repository_id(self, repository_id: RepositoryId | None) -> None:
"""
setter for repository identifier
Args:
repository_id(RepositoryId | None): repository unique identifier
"""
self._repository_id = repository_id
if repository_id is None or repository_id.is_empty:
self.remove_option("repository", "name")
self.remove_option("repository", "architecture")
else:
self.set_option("repository", "name", repository_id.name)
self.set_option("repository", "architecture", repository_id.architecture)
@property @property
def repository_name(self) -> str: def repository_name(self) -> str:
""" """

View File

@@ -57,7 +57,7 @@ class ConfigurationMultiDict(dict[str, Any]):
OptionError: if the key already exists in the dictionary, but not a single value list or a string OptionError: if the key already exists in the dictionary, but not a single value list or a string
""" """
match self.get(key): match self.get(key):
case [current_value] | str(current_value): case [current_value] | (str() as current_value):
value = f"{current_value} {value}" value = f"{current_value} {value}"
case None: case None:
pass pass

View File

@@ -254,6 +254,10 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
"repository": { "repository": {
"type": "dict", "type": "dict",
"schema": { "schema": {
"architecture": {
"type": "string",
"empty": False,
},
"name": { "name": {
"type": "string", "type": "string",
"empty": False, "empty": False,

View File

@@ -94,9 +94,13 @@ class SQLite(
sqlite3.register_adapter(list, json.dumps) sqlite3.register_adapter(list, json.dumps)
sqlite3.register_converter("json", json.loads) sqlite3.register_converter("json", json.loads)
if self._configuration.getboolean("settings", "apply_migrations", fallback=True): if not self._configuration.getboolean("settings", "apply_migrations", fallback=True):
return
if self._repository_id.is_empty:
return # do not perform migration on empty repository identifier (e.g. multirepo command)
with self._repository_paths.preserve_owner():
self.with_connection(lambda connection: Migrations.migrate(connection, self._configuration)) self.with_connection(lambda connection: Migrations.migrate(connection, self._configuration))
self._repository_paths.chown(self.path)
def package_clear(self, package_base: str, repository_id: RepositoryId | None = None) -> None: def package_clear(self, package_base: str, repository_id: RepositoryId | None = None) -> None:
""" """

View File

@@ -72,8 +72,8 @@ class SyncHttpClient(LazyLogging):
""" """
session = requests.Session() session = requests.Session()
python_version = ".".join(map(str, sys.version_info[:3])) # just major.minor.patch python_version = ".".join(map(str, sys.version_info[:3])) # just major.minor.patch
session.headers["User-Agent"] = f"ahriman/{__version__}" \ session.headers["User-Agent"] = f"ahriman/{__version__} " \
f"{requests.utils.default_user_agent()}" \ f"{requests.utils.default_user_agent()} " \
f"python/{python_version}" f"python/{python_version}"
return session return session

View File

@@ -80,8 +80,7 @@ class Trigger(LazyLogging):
return self.repository_id.architecture return self.repository_id.architecture
@classmethod @classmethod
def configuration_schema(cls, repository_id: RepositoryId, def configuration_schema(cls, configuration: Configuration | None) -> ConfigurationSchema:
configuration: Configuration | None) -> ConfigurationSchema:
""" """
configuration schema based on supplied service configuration configuration schema based on supplied service configuration
@@ -89,7 +88,6 @@ class Trigger(LazyLogging):
Schema must be in cerberus format, for details and examples you can check built-in triggers. Schema must be in cerberus format, for details and examples you can check built-in triggers.
Args: Args:
repository_id(str): repository unique identifier
configuration(Configuration | None): configuration instance. If set to None, the default schema configuration(Configuration | None): configuration instance. If set to None, the default schema
should be returned should be returned
@@ -101,10 +99,12 @@ class Trigger(LazyLogging):
result: ConfigurationSchema = {} result: ConfigurationSchema = {}
for target in cls.configuration_sections(configuration): for target in cls.configuration_sections(configuration):
if not configuration.has_section(target): for section in configuration.sections():
if not (section == target or section.startswith(f"{target}:")):
# either repository specific or exact name
continue continue
section, schema_name = configuration.gettype( schema_name = configuration.get(section, "type", fallback=section)
target, repository_id, fallback=cls.CONFIGURATION_SCHEMA_FALLBACK)
if schema_name not in cls.CONFIGURATION_SCHEMA: if schema_name not in cls.CONFIGURATION_SCHEMA:
continue continue
result[section] = cls.CONFIGURATION_SCHEMA[schema_name] result[section] = cls.CONFIGURATION_SCHEMA[schema_name]

View File

@@ -25,7 +25,7 @@ from dataclasses import dataclass, field, fields
from pyalpm import Package # type: ignore[import-not-found] from pyalpm import Package # type: ignore[import-not-found]
from typing import Any, Self from typing import Any, Self
from ahriman.core.utils import filter_json, full_version from ahriman.core.utils import filter_json, full_version, trim_package
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@@ -103,6 +103,17 @@ class AURPackage:
keywords: list[str] = field(default_factory=list) keywords: list[str] = field(default_factory=list)
groups: list[str] = field(default_factory=list) groups: list[str] = field(default_factory=list)
def __post_init__(self) -> None:
"""
update packages lists accordingly
"""
object.__setattr__(self, "depends", [trim_package(package) for package in self.depends])
object.__setattr__(self, "make_depends", [trim_package(package) for package in self.make_depends])
object.__setattr__(self, "opt_depends", [trim_package(package) for package in self.opt_depends])
object.__setattr__(self, "check_depends", [trim_package(package) for package in self.check_depends])
object.__setattr__(self, "conflicts", [trim_package(package) for package in self.conflicts])
object.__setattr__(self, "provides", [trim_package(package) for package in self.provides])
@classmethod @classmethod
def from_json(cls, dump: dict[str, Any]) -> Self: def from_json(cls, dump: dict[str, Any]) -> Self:
""" """

View File

@@ -83,12 +83,13 @@ class PackageDescription:
def __post_init__(self) -> None: def __post_init__(self) -> None:
""" """
update dependencies list accordingly update packages lists accordingly
""" """
self.depends = [trim_package(package) for package in self.depends] self.depends = [trim_package(package) for package in self.depends]
self.opt_depends = [trim_package(package) for package in self.opt_depends]
self.make_depends = [trim_package(package) for package in self.make_depends] self.make_depends = [trim_package(package) for package in self.make_depends]
self.opt_depends = [trim_package(package) for package in self.opt_depends]
self.check_depends = [trim_package(package) for package in self.check_depends] self.check_depends = [trim_package(package) for package in self.check_depends]
self.provides = [trim_package(package) for package in self.provides]
@property @property
def filepath(self) -> Path | None: def filepath(self) -> Path | None:

View File

@@ -17,6 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import contextlib
import os import os
import shutil import shutil
@@ -221,22 +222,14 @@ class RepositoryPaths(LazyLogging):
stat = path.stat() stat = path.stat()
return stat.st_uid, stat.st_gid return stat.st_uid, stat.st_gid
def cache_for(self, package_base: str) -> Path: def _chown(self, path: Path) -> None:
"""
get path to cached PKGBUILD and package sources for the package base
Args:
package_base(str): package base name
Returns:
Path: full path to directory for specified package base cache
"""
return self.cache / package_base
def chown(self, path: Path) -> None:
""" """
set owner of path recursively (from root) to root owner set owner of path recursively (from root) to root owner
Notes:
More likely you don't want to call this method explicitly, consider using :func:`preserve_owner`
as context manager instead
Args: Args:
path(Path): path to be chown path(Path): path to be chown
@@ -256,6 +249,56 @@ class RepositoryPaths(LazyLogging):
set_owner(path) set_owner(path)
path = path.parent path = path.parent
def cache_for(self, package_base: str) -> Path:
"""
get path to cached PKGBUILD and package sources for the package base
Args:
package_base(str): package base name
Returns:
Path: full path to directory for specified package base cache
"""
return self.cache / package_base
@contextlib.contextmanager
def preserve_owner(self, path: Path | None = None) -> Generator[None, None, None]:
"""
perform any action preserving owner for any newly created file or directory
Args:
path(Path | None, optional): use this path as root instead of repository root (Default value = None)
Examples:
This method is designed to use as context manager when you are going to perform operations which might
change filesystem, especially if you are doing it under unsafe flag, e.g.::
>>> with paths.preserve_owner():
>>> paths.tree_create()
Note, however, that this method doesn't handle any exceptions and will eventually interrupt
if there will be any.
"""
path = path or self.root
def walk(root: Path) -> Generator[Path, None, None]:
# basically walk, but skipping some content
for child in root.iterdir():
yield child
if child in (self.chroot.parent,):
yield from child.iterdir() # we only yield top-level in chroot directory
elif child.is_dir():
yield from walk(child)
# get current filesystem and run action
previous_snapshot = set(walk(path))
yield
# get newly created files and directories and chown them
new_entries = set(walk(path)).difference(previous_snapshot)
for entry in new_entries:
self._chown(entry)
def tree_clear(self, package_base: str) -> None: def tree_clear(self, package_base: str) -> None:
""" """
clear package specific files clear package specific files
@@ -274,6 +317,8 @@ class RepositoryPaths(LazyLogging):
""" """
if self.repository_id.is_empty: if self.repository_id.is_empty:
return # do not even try to create tree in case if no repository id set return # do not even try to create tree in case if no repository id set
with self.preserve_owner():
for directory in ( for directory in (
self.cache, self.cache,
self.chroot, self.chroot,
@@ -282,4 +327,3 @@ class RepositoryPaths(LazyLogging):
self.repository, self.repository,
): ):
directory.mkdir(mode=0o755, parents=True, exist_ok=True) directory.mkdir(mode=0o755, parents=True, exist_ok=True)
self.chown(directory)

View File

@@ -166,11 +166,16 @@ def setup_server(configuration: Configuration, spawner: Spawn, repositories: lis
# package cache # package cache
if not repositories: if not repositories:
raise InitializeError("No repositories configured, exiting") raise InitializeError("No repositories configured, exiting")
database = SQLite.load(configuration)
watchers: dict[RepositoryId, Watcher] = {} watchers: dict[RepositoryId, Watcher] = {}
configuration_path, _ = configuration.check_loaded()
for repository_id in repositories: for repository_id in repositories:
application.logger.info("load repository %s", repository_id) application.logger.info("load repository %s", repository_id)
client = Client.load(repository_id, configuration, database, report=False) # explicitly load local client # load settings explicitly for architecture if any
repository_configuration = Configuration.from_path(configuration_path, repository_id)
# load database instance, because it holds identifier
database = SQLite.load(repository_configuration)
# explicitly load local client
client = Client.load(repository_id, repository_configuration, database, report=False)
watchers[repository_id] = Watcher(client) watchers[repository_id] = Watcher(client)
application[WatcherKey] = watchers application[WatcherKey] = watchers
# workers cache # workers cache
@@ -179,6 +184,7 @@ def setup_server(configuration: Configuration, spawner: Spawn, repositories: lis
application[SpawnKey] = spawner application[SpawnKey] = spawner
application.logger.info("setup authorization") application.logger.info("setup authorization")
database = SQLite.load(configuration)
validator = application[AuthKey] = Auth.load(configuration, database) validator = application[AuthKey] = Auth.load(configuration, database)
if validator.enabled: if validator.enabled:
from ahriman.web.middlewares.auth_handler import setup_auth from ahriman.web.middlewares.auth_handler import setup_auth

View File

@@ -58,9 +58,11 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
sudo_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo") sudo_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo")
executable_mock = mocker.patch("ahriman.application.handlers.setup.Setup.executable_create") executable_mock = mocker.patch("ahriman.application.handlers.setup.Setup.executable_create")
init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init") init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init")
owner_guard_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.preserve_owner")
_, repository_id = configuration.check_loaded() _, repository_id = configuration.check_loaded()
Setup.run(args, repository_id, configuration, report=False) Setup.run(args, repository_id, configuration, report=False)
owner_guard_mock.assert_called_once_with()
ahriman_configuration_mock.assert_called_once_with(args, repository_id, configuration) ahriman_configuration_mock.assert_called_once_with(args, repository_id, configuration)
devtools_configuration_mock.assert_called_once_with( devtools_configuration_mock.assert_called_once_with(
repository_id, args.from_configuration, args.mirror, args.multilib, f"file://{repository_paths.repository}") repository_id, args.from_configuration, args.mirror, args.multilib, f"file://{repository_paths.repository}")
@@ -268,13 +270,11 @@ def test_executable_create(configuration: Configuration, repository_paths: Repos
""" """
must create executable must create executable
""" """
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
symlink_mock = mocker.patch("pathlib.Path.symlink_to") symlink_mock = mocker.patch("pathlib.Path.symlink_to")
unlink_mock = mocker.patch("pathlib.Path.unlink") unlink_mock = mocker.patch("pathlib.Path.unlink")
_, repository_id = configuration.check_loaded() _, repository_id = configuration.check_loaded()
Setup.executable_create(repository_paths, repository_id) Setup.executable_create(repository_paths, repository_id)
chown_mock.assert_called_once_with(Setup.build_command(repository_paths.root, repository_id))
symlink_mock.assert_called_once_with(Setup.ARCHBUILD_COMMAND_PATH) symlink_mock.assert_called_once_with(Setup.ARCHBUILD_COMMAND_PATH)
unlink_mock.assert_called_once_with(missing_ok=True) unlink_mock.assert_called_once_with(missing_ok=True)

View File

@@ -2,6 +2,7 @@ import argparse
import json import json
import pytest import pytest
from pathlib import Path
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from ahriman.application.handlers.validate import Validate from ahriman.application.handlers.validate import Validate
@@ -53,12 +54,50 @@ def test_run_skip(args: argparse.Namespace, configuration: Configuration, mocker
print_mock.assert_not_called() print_mock.assert_not_called()
def test_run_default(args: argparse.Namespace, configuration: Configuration) -> None:
"""
must run on default configuration without errors
"""
args.exit_code = True
_, repository_id = configuration.check_loaded()
default = Configuration.from_path(Configuration.SYSTEM_CONFIGURATION_PATH, repository_id)
# copy autogenerated values
for section, key in (("build", "build_command"), ("repository", "root")):
value = configuration.get(section, key)
default.set_option(section, key, value)
Validate.run(args, repository_id, default, report=False)
def test_run_repo_specific_triggers(args: argparse.Namespace, configuration: Configuration,
resource_path_root: Path) -> None:
"""
must correctly insert repo specific triggers
"""
args.exit_code = True
_, repository_id = configuration.check_loaded()
# remove unused sections
for section in ("customs3", "github:x86_64", "logs-rotation", "mirrorlist"):
configuration.remove_section(section)
configuration.set_option("report", "target", "test")
for section in ("test", "test:i686", "test:another-repo:x86_64"):
configuration.set_option(section, "type", "html")
configuration.set_option(section, "link_path", "http://link_path")
configuration.set_option(section, "path", "path")
configuration.set_option(section, "template", "template")
configuration.set_option(section, "templates", str(resource_path_root))
Validate.run(args, repository_id, configuration, report=False)
def test_schema(configuration: Configuration) -> None: def test_schema(configuration: Configuration) -> None:
""" """
must generate full schema correctly must generate full schema correctly
""" """
_, repository_id = configuration.check_loaded() schema = Validate.schema(configuration)
schema = Validate.schema(repository_id, configuration)
# defaults # defaults
assert schema.pop("console") assert schema.pop("console")
@@ -91,9 +130,7 @@ def test_schema_invalid_trigger(configuration: Configuration) -> None:
""" """
configuration.set_option("build", "triggers", "some.invalid.trigger.path.Trigger") configuration.set_option("build", "triggers", "some.invalid.trigger.path.Trigger")
configuration.remove_option("build", "triggers_known") configuration.remove_option("build", "triggers_known")
_, repository_id = configuration.check_loaded() assert Validate.schema(configuration) == CONFIGURATION_SCHEMA
assert Validate.schema(repository_id, configuration) == CONFIGURATION_SCHEMA
def test_schema_erase_required() -> None: def test_schema_erase_required() -> None:

View File

@@ -62,12 +62,12 @@ def test_database_copy(pacman: Pacman, mocker: MockerFixture) -> None:
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p.is_relative_to(path)) mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p.is_relative_to(path))
mkdir_mock = mocker.patch("pathlib.Path.mkdir") mkdir_mock = mocker.patch("pathlib.Path.mkdir")
copy_mock = mocker.patch("shutil.copy") copy_mock = mocker.patch("shutil.copy")
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown") owner_guard_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.preserve_owner")
pacman.database_copy(pacman.handle, database, path, 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) mkdir_mock.assert_called_once_with(mode=0o755, exist_ok=True)
copy_mock.assert_called_once_with(path / "sync" / "core.db", dst_path) copy_mock.assert_called_once_with(path / "sync" / "core.db", dst_path)
chown_mock.assert_called_once_with(dst_path) owner_guard_mock.assert_called_once_with(dst_path.parent)
def test_database_copy_skip(pacman: Pacman, mocker: MockerFixture) -> None: def test_database_copy_skip(pacman: Pacman, mocker: MockerFixture) -> None:
@@ -289,3 +289,4 @@ def test_package_provided_by(pacman: Pacman) -> None:
must search through the provides lists must search through the provides lists
""" """
assert list(pacman.provided_by("sh")) assert list(pacman.provided_by("sh"))
assert list(pacman.provided_by("libacl.so")) # case with exact version

View File

@@ -20,6 +20,40 @@ def test_architecture(configuration: Configuration) -> None:
assert configuration.architecture == "x86_64" assert configuration.architecture == "x86_64"
def test_repository_id(configuration: Configuration, repository_id: RepositoryId) -> None:
"""
must return repository identifier
"""
assert configuration.repository_id == repository_id
assert configuration.get("repository", "name") == repository_id.name
assert configuration.get("repository", "architecture") == repository_id.architecture
def test_repository_id_erase(configuration: Configuration) -> None:
"""
must remove repository identifier properties if empty identifier supplied
"""
configuration.repository_id = None
assert configuration.get("repository", "name", fallback=None) is None
assert configuration.get("repository", "architecture", fallback=None) is None
configuration.repository_id = RepositoryId("", "")
assert configuration.get("repository", "name", fallback=None) is None
assert configuration.get("repository", "architecture", fallback=None) is None
def test_repository_id_update(configuration: Configuration, repository_id: RepositoryId) -> None:
"""
must update repository identifier and related configuration options
"""
repository_id = RepositoryId("i686", repository_id.name)
configuration.repository_id = repository_id
assert configuration.repository_id == repository_id
assert configuration.get("repository", "name") == repository_id.name
assert configuration.get("repository", "architecture") == repository_id.architecture
def test_repository_name(configuration: Configuration) -> None: def test_repository_name(configuration: Configuration) -> None:
""" """
must return valid repository name must return valid repository name

View File

@@ -36,6 +36,17 @@ def test_init_skip_migration(database: SQLite, mocker: MockerFixture) -> None:
migrate_schema_mock.assert_not_called() migrate_schema_mock.assert_not_called()
def test_init_skip_empty_repository(database: SQLite, mocker: MockerFixture) -> None:
"""
must skip migrations if repository identifier is not set
"""
database._repository_id = RepositoryId("", "")
migrate_schema_mock = mocker.patch("ahriman.core.database.migrations.Migrations.migrate")
database.init()
migrate_schema_mock.assert_not_called()
def test_package_clear(database: SQLite, repository_id: RepositoryId, mocker: MockerFixture) -> None: def test_package_clear(database: SQLite, repository_id: RepositoryId, mocker: MockerFixture) -> None:
""" """
must clear package data must clear package data

View File

@@ -19,10 +19,9 @@ def test_configuration_schema(configuration: Configuration) -> None:
""" """
section = "console" section = "console"
configuration.set_option("report", "target", section) configuration.set_option("report", "target", section)
_, repository_id = configuration.check_loaded()
expected = {section: ReportTrigger.CONFIGURATION_SCHEMA[section]} expected = {section: ReportTrigger.CONFIGURATION_SCHEMA[section]}
assert ReportTrigger.configuration_schema(repository_id, configuration) == expected assert ReportTrigger.configuration_schema(configuration) == expected
def test_configuration_schema_no_section(configuration: Configuration) -> None: def test_configuration_schema_no_section(configuration: Configuration) -> None:
@@ -31,9 +30,7 @@ def test_configuration_schema_no_section(configuration: Configuration) -> None:
""" """
section = "abracadabra" section = "abracadabra"
configuration.set_option("report", "target", section) configuration.set_option("report", "target", section)
_, repository_id = configuration.check_loaded() assert ReportTrigger.configuration_schema(configuration) == {}
assert ReportTrigger.configuration_schema(repository_id, configuration) == {}
def test_configuration_schema_no_schema(configuration: Configuration) -> None: def test_configuration_schema_no_schema(configuration: Configuration) -> None:
@@ -43,17 +40,15 @@ def test_configuration_schema_no_schema(configuration: Configuration) -> None:
section = "abracadabra" section = "abracadabra"
configuration.set_option("report", "target", section) configuration.set_option("report", "target", section)
configuration.set_option(section, "key", "value") configuration.set_option(section, "key", "value")
_, repository_id = configuration.check_loaded()
assert ReportTrigger.configuration_schema(repository_id, configuration) == {} assert ReportTrigger.configuration_schema(configuration) == {}
def test_configuration_schema_empty(configuration: Configuration) -> None: def test_configuration_schema_empty(configuration: Configuration) -> None:
""" """
must return default schema if no configuration set must return default schema if no configuration set
""" """
_, repository_id = configuration.check_loaded() assert ReportTrigger.configuration_schema(None) == ReportTrigger.CONFIGURATION_SCHEMA
assert ReportTrigger.configuration_schema(repository_id, None) == ReportTrigger.CONFIGURATION_SCHEMA
def test_configuration_schema_variables() -> None: def test_configuration_schema_variables() -> None:

View File

@@ -2,7 +2,7 @@ import datetime
import json import json
import pyalpm # typing: ignore import pyalpm # typing: ignore
from dataclasses import asdict, fields from dataclasses import asdict, fields, replace
from pathlib import Path from pathlib import Path
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from typing import Any from typing import Any
@@ -38,6 +38,25 @@ def _get_official_data(resource_path_root: Path) -> dict[str, Any]:
return json.loads(response)["results"][0] return json.loads(response)["results"][0]
def test_post_init(aur_package_ahriman: AURPackage) -> None:
"""
must trim versions and descriptions from packages list
"""
package = replace(
aur_package_ahriman,
depends=["a=1"],
make_depends=["b>=3"],
opt_depends=["c: a description"],
check_depends=["d=4"],
provides=["e=5"],
)
assert package.depends == ["a"]
assert package.make_depends == ["b"]
assert package.opt_depends == ["c"]
assert package.check_depends == ["d"]
assert package.provides == ["e"]
def test_from_json(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None: def test_from_json(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
""" """
must load package from json must load package from json

View File

@@ -6,10 +6,15 @@ from ahriman.models.package_description import PackageDescription
def test_post_init() -> None: def test_post_init() -> None:
""" """
must trim versions and descriptions from dependencies list must trim versions and descriptions from packages list
""" """
assert PackageDescription(depends=["a=1"], make_depends=["b>=3"], opt_depends=["c: a description"]) == \ assert PackageDescription(
PackageDescription(depends=["a"], make_depends=["b"], opt_depends=["c"]) depends=["a=1"],
make_depends=["b>=3"],
opt_depends=["c: a description"],
check_depends=["d=4"],
provides=["e=5"]
) == PackageDescription(depends=["a"], make_depends=["b"], opt_depends=["c"], check_depends=["d"], provides=["e"])
def test_filepath(package_description_ahriman: PackageDescription) -> None: def test_filepath(package_description_ahriman: PackageDescription) -> None:

View File

@@ -198,15 +198,6 @@ def test_owner(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None
assert RepositoryPaths.owner(repository_paths.root) == (42, 142) assert RepositoryPaths.owner(repository_paths.root) == (42, 142)
def test_cache_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
"""
must return correct path for cache directory
"""
path = repository_paths.cache_for(package_ahriman.base)
assert path.name == package_ahriman.base
assert path.parent == repository_paths.cache
def test_chown(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None: def test_chown(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
""" """
must correctly set owner for the directory must correctly set owner for the directory
@@ -216,7 +207,7 @@ def test_chown(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None
chown_mock = mocker.patch("os.chown") chown_mock = mocker.patch("os.chown")
path = repository_paths.root / "path" path = repository_paths.root / "path"
repository_paths.chown(path) repository_paths._chown(path)
chown_mock.assert_called_once_with(path, 42, 42, follow_symlinks=False) chown_mock.assert_called_once_with(path, 42, 42, follow_symlinks=False)
@@ -229,7 +220,7 @@ def test_chown_parent(repository_paths: RepositoryPaths, mocker: MockerFixture)
chown_mock = mocker.patch("os.chown") chown_mock = mocker.patch("os.chown")
path = repository_paths.root / "parent" / "path" path = repository_paths.root / "parent" / "path"
repository_paths.chown(path) repository_paths._chown(path)
chown_mock.assert_has_calls([ chown_mock.assert_has_calls([
MockCall(path, 42, 42, follow_symlinks=False), MockCall(path, 42, 42, follow_symlinks=False),
MockCall(path.parent, 42, 42, follow_symlinks=False) MockCall(path.parent, 42, 42, follow_symlinks=False)
@@ -245,7 +236,7 @@ def test_chown_skip(repository_paths: RepositoryPaths, mocker: MockerFixture) ->
chown_mock = mocker.patch("os.chown") chown_mock = mocker.patch("os.chown")
path = repository_paths.root / "path" path = repository_paths.root / "path"
repository_paths.chown(path) repository_paths._chown(path)
chown_mock.assert_not_called() chown_mock.assert_not_called()
@@ -254,7 +245,46 @@ def test_chown_invalid_path(repository_paths: RepositoryPaths) -> None:
must raise invalid path exception in case if directory outside the root supplied must raise invalid path exception in case if directory outside the root supplied
""" """
with pytest.raises(PathError): with pytest.raises(PathError):
repository_paths.chown(repository_paths.root.parent) repository_paths._chown(repository_paths.root.parent)
def test_cache_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None:
"""
must return correct path for cache directory
"""
path = repository_paths.cache_for(package_ahriman.base)
assert path.name == package_ahriman.base
assert path.parent == repository_paths.cache
def test_preserve_owner(tmp_path: Path, repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must preserve file owner during operations
"""
repository_paths = RepositoryPaths(tmp_path, repository_id)
repository_paths.tree_create()
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths._chown")
with repository_paths.preserve_owner():
(repository_paths.root / "created1").touch()
(repository_paths.chroot / "created2").touch()
chown_mock.assert_has_calls([MockCall(repository_paths.root / "created1")])
def test_preserve_owner_specific(tmp_path: Path, repository_id: RepositoryId, mocker: MockerFixture) -> None:
"""
must preserve file owner during operations only in specific directory
"""
repository_paths = RepositoryPaths(tmp_path, repository_id)
repository_paths.tree_create()
(repository_paths.root / "content").mkdir()
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths._chown")
with repository_paths.preserve_owner(repository_paths.root / "content"):
(repository_paths.root / "created1").touch()
(repository_paths.root / "content" / "created2").touch()
(repository_paths.chroot / "created3").touch()
chown_mock.assert_has_calls([MockCall(repository_paths.root / "content" / "created2")])
def test_tree_clear(repository_paths: RepositoryPaths, package_ahriman: Package, mocker: MockerFixture) -> None: def test_tree_clear(repository_paths: RepositoryPaths, package_ahriman: Package, mocker: MockerFixture) -> None:
@@ -293,11 +323,11 @@ def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -
and not callable(getattr(repository_paths, prop)) and not callable(getattr(repository_paths, prop))
} }
mkdir_mock = mocker.patch("pathlib.Path.mkdir") mkdir_mock = mocker.patch("pathlib.Path.mkdir")
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown") owner_guard_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.preserve_owner")
repository_paths.tree_create() repository_paths.tree_create()
mkdir_mock.assert_has_calls([MockCall(mode=0o755, parents=True, exist_ok=True) for _ in paths], any_order=True) 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) owner_guard_mock.assert_called_once_with()
def test_tree_create_skip(mocker: MockerFixture) -> None: def test_tree_create_skip(mocker: MockerFixture) -> None: