Compare commits

...

16 Commits
2.4.0 ... 2.5.1

Author SHA1 Message Date
214d6d7fdd Release 2.5.1 2022-12-31 14:58:37 +02:00
e9512e9a6a remote log for calculate version as it cleans logs 2022-12-31 14:48:21 +02:00
f984ea75d0 fully lazy handle load
In case of immediate handle load it would try to sync databases (or at
least to create database files), which is not possible in case if
command is run as non-ahriman user. This commit makes handle load lazy
and allows to run some commands as non-ahriman user
2022-12-31 14:48:21 +02:00
b44e5c3a3d Release 2.5.0 2022-12-31 02:02:37 +02:00
1891d6c944 force upgrade sphinx documentation 2022-12-30 22:58:13 +02:00
08d68258ca add completion files generated by shtab 2022-12-30 21:19:23 +02:00
cd3d12a754 allow 3 symbols for package search 2022-12-30 21:19:23 +02:00
81d9526054 improve VCS packages checks
* Unlike older version, currently service will always try to pull AUR
  package to check version. Previously if no-vcs flag is set, it would
  ignore VCS packages completelly
* Introduce build.vcs_allowed_age option. If set, it will skip version
  calculation if package age (now - build_date) is less than this value
2022-12-30 21:19:23 +02:00
c7447f19f0 change another boolean info option to boolean action 2022-12-29 03:15:48 +02:00
7de94a8daa add repo-structure subcommand
This commit also changes Tree class, replacing load method by resolve
2022-12-27 10:35:03 +02:00
aa1a59d0a3 fix property description in docs 2022-12-26 02:32:14 +02:00
8864855c14 write patches via gitremote push trigger (#79)
* write patches via gitremote push trigger

* implement context variables intead of custom database class
2022-12-25 02:10:38 +03:00
b0b37e8169 Release 2.4.1 2022-12-11 05:17:10 +02:00
7df4adfc9d replace alert bodals with toasts 2022-12-09 11:33:19 +02:00
2d5b73c6dc remove badges from status page 2022-12-09 00:58:03 +02:00
1c8aed4a97 check that package has been created during setup workflow 2022-12-06 11:09:56 +02:00
95 changed files with 6892 additions and 4818 deletions

View File

@ -52,7 +52,7 @@ fi
# the build itself does not really work in the container
sudo -u ahriman -- ahriman package-add --now yay
# check if package was actually installed
#test -n "$(find "/var/lib/ahriman/repository/x86_64" -name "yay*pkg*")"
test -n "$(find "/var/lib/ahriman/repository/x86_64" -name "yay*pkg*")"
# run package check
sudo -u ahriman -- ahriman repo-update
# stop web service lol

View File

@ -38,7 +38,7 @@ html: specification
tox -e docs-html
push: specification archlinux
git add package/archlinux/PKGBUILD src/ahriman/version.py docs/ahriman-architecture.svg docs/ahriman.1
git add package/archlinux/PKGBUILD src/ahriman/version.py docs/ahriman-architecture.svg docs/ahriman.1 docs/completions/
git commit -m "Release $(VERSION)"
git tag "$(VERSION)"
git push

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 621 KiB

After

Width:  |  Height:  |  Size: 641 KiB

View File

@ -1,9 +1,9 @@
.TH AHRIMAN "1" "2022\-12\-05" "ahriman" "Generated Python Manual"
.TH AHRIMAN "1" "2022\-12\-31" "ahriman" "Generated Python Manual"
.SH NAME
ahriman
.SH SYNOPSIS
.B ahriman
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--report | --no-report] [-q] [--unsafe] [-V] {aur-search,search,daemon,help,help-commands-unsafe,key-import,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-clean,clean,repo-config,config,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-setup,init,repo-init,setup,repo-sign,sign,repo-status-update,repo-sync,sync,repo-triggers,repo-update,update,shell,user-add,user-list,user-remove,version,web} ...
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--report | --no-report] [-q] [--unsafe] [-V] {aur-search,search,daemon,help,help-commands-unsafe,key-import,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-clean,clean,repo-config,config,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-setup,init,repo-init,setup,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,shell,user-add,user-list,user-remove,version,web} ...
.SH DESCRIPTION
ArcH linux ReposItory MANager
@ -121,6 +121,9 @@ update repository status
\fBahriman\fR \fI\,repo\-sync\/\fR
sync repository
.TP
\fBahriman\fR \fI\,repo\-tree\/\fR
dump repository tree
.TP
\fBahriman\fR \fI\,repo\-triggers\/\fR
run triggers
.TP
@ -277,7 +280,8 @@ remove package from the repository
package name or base
.SH COMMAND \fI\,'ahriman package\-status'\/\fR
usage: ahriman package\-status [\-h] [\-\-ahriman] [\-e] [\-i] [\-s {unknown,pending,building,failed,success}] [package ...]
usage: ahriman package\-status [\-h] [\-\-ahriman] [\-e] [\-\-info | \-\-no\-info] [\-s {unknown,pending,building,failed,success}]
[package ...]
request status of the package
@ -295,8 +299,8 @@ get service status itself
return non\-zero exit status if result is empty
.TP
\fB\-i\fR, \fB\-\-info\fR
show additional package information
\fB\-\-info\fR, \fB\-\-no\-info\fR
show additional package information (default: False)
.TP
\fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
@ -583,6 +587,11 @@ usage: ahriman repo\-sync [\-h]
sync repository files to remote server according to current settings
.SH COMMAND \fI\,'ahriman repo\-tree'\/\fR
usage: ahriman repo\-tree [\-h]
dump repository tree based on packages dependencies
.SH COMMAND \fI\,'ahriman repo\-triggers'\/\fR
usage: ahriman repo\-triggers [\-h] [trigger ...]

View File

@ -156,6 +156,14 @@ ahriman.application.handlers.status\_update module
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.structure module
---------------------------------------------
.. automodule:: ahriman.application.handlers.structure
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.triggers module
--------------------------------------------

View File

@ -68,6 +68,14 @@ ahriman.core.formatters.string\_printer module
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.tree\_printer module
--------------------------------------------
.. automodule:: ahriman.core.formatters.tree_printer
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.formatters.update\_printer module
----------------------------------------------

View File

@ -36,6 +36,14 @@ ahriman.models.build\_status module
:no-undoc-members:
:show-inheritance:
ahriman.models.context\_key module
----------------------------------
.. automodule:: ahriman.models.context_key
:members:
:no-undoc-members:
:show-inheritance:
ahriman.models.counters module
------------------------------

View File

@ -169,6 +169,15 @@ Utils
For every external command run (which is actually not recommended if possible) custom wrapper for ``subprocess`` is used. Additional functions ``ahriman.core.auth.helpers`` provide safe calls for ``aiohttp_security`` methods and are required to make this dependency optional.
Context variables
^^^^^^^^^^^^^^^^^
Package provides implicit global variables which can be accessed from ``ahriman.core`` package as ``context`` variable, wrapped by ``contextvars.ContextVar`` class. The value of the variable is defaulting to private ``_Context`` class which is defined in the same module. The default values - such as ``database`` and ``sign`` - are being set on the service initialization.
The ``_Context`` class itself mimics default collection interface (as is Mapping) and can be modified by ``_Context.set`` method. The stored variables can be achieved by ``_Context.get`` method, which is unlike default ``Mapping`` interface also performs type and presence checks.
In order to provide statically typed interface, the ``ahriman.models.context_key.ContextKey`` class is used for both ``_Content.get`` and ``_Content.set`` methods; the context instance itself, however, does not store information about types.
Submodules
^^^^^^^^^^

View File

@ -0,0 +1,484 @@
# AUTOMATICALLY GENERATED by `shtab`
_shtab_ahriman_subparsers=('aur-search' 'search' 'daemon' 'help' 'help-commands-unsafe' 'key-import' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-clean' 'clean' 'repo-config' 'config' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-setup' 'init' 'repo-init' 'setup' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'shell' 'user-add' 'user-list' 'user-remove' 'version' 'web')
_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--report' '--no-report' '-q' '--quiet' '--unsafe' '-V' '--version')
_shtab_ahriman_aur_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
_shtab_ahriman_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
_shtab_ahriman_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_help_option_strings=('-h' '--help')
_shtab_ahriman_help_commands_unsafe_option_strings=('-h' '--help' '--command')
_shtab_ahriman_key_import_option_strings=('-h' '--help' '--key-server')
_shtab_ahriman_package_add_option_strings=('-h' '--help' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source' '--without-dependencies')
_shtab_ahriman_add_option_strings=('-h' '--help' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source' '--without-dependencies')
_shtab_ahriman_package_update_option_strings=('-h' '--help' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source' '--without-dependencies')
_shtab_ahriman_package_remove_option_strings=('-h' '--help')
_shtab_ahriman_remove_option_strings=('-h' '--help')
_shtab_ahriman_package_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
_shtab_ahriman_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
_shtab_ahriman_package_status_remove_option_strings=('-h' '--help')
_shtab_ahriman_package_status_update_option_strings=('-h' '--help' '-s' '--status')
_shtab_ahriman_status_update_option_strings=('-h' '--help' '-s' '--status')
_shtab_ahriman_patch_add_option_strings=('-h' '--help')
_shtab_ahriman_patch_list_option_strings=('-h' '--help' '-e' '--exit-code' '-v' '--variable')
_shtab_ahriman_patch_remove_option_strings=('-h' '--help' '-v' '--variable')
_shtab_ahriman_patch_set_add_option_strings=('-h' '--help' '-t' '--track')
_shtab_ahriman_repo_backup_option_strings=('-h' '--help')
_shtab_ahriman_repo_check_option_strings=('-h' '--help' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_check_option_strings=('-h' '--help' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_repo_config_option_strings=('-h' '--help')
_shtab_ahriman_config_option_strings=('-h' '--help')
_shtab_ahriman_repo_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '-e' '--exit-code')
_shtab_ahriman_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '-e' '--exit-code')
_shtab_ahriman_repo_remove_unknown_option_strings=('-h' '--help' '--dry-run')
_shtab_ahriman_remove_unknown_option_strings=('-h' '--help' '--dry-run')
_shtab_ahriman_repo_report_option_strings=('-h' '--help')
_shtab_ahriman_report_option_strings=('-h' '--help')
_shtab_ahriman_repo_restore_option_strings=('-h' '--help' '-o' '--output')
_shtab_ahriman_repo_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--makeflags-jobs' '--no-makeflags-jobs' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--makeflags-jobs' '--no-makeflags-jobs' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_repo_init_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--makeflags-jobs' '--no-makeflags-jobs' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_setup_option_strings=('-h' '--help' '--build-as-user' '--build-command' '--from-configuration' '--makeflags-jobs' '--no-makeflags-jobs' '--multilib' '--no-multilib' '--packager' '--repository' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
_shtab_ahriman_repo_sign_option_strings=('-h' '--help')
_shtab_ahriman_sign_option_strings=('-h' '--help')
_shtab_ahriman_repo_status_update_option_strings=('-h' '--help' '-s' '--status')
_shtab_ahriman_repo_sync_option_strings=('-h' '--help')
_shtab_ahriman_sync_option_strings=('-h' '--help')
_shtab_ahriman_repo_tree_option_strings=('-h' '--help')
_shtab_ahriman_repo_triggers_option_strings=('-h' '--help')
_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--dry-run' '-e' '--exit-code' '--aur' '--no-aur' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_update_option_strings=('-h' '--help' '--dry-run' '-e' '--exit-code' '--aur' '--no-aur' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_shell_option_strings=('-h' '--help')
_shtab_ahriman_user_add_option_strings=('-h' '--help' '-p' '--password' '-r' '--role' '-s' '--secure')
_shtab_ahriman_user_list_option_strings=('-h' '--help' '-e' '--exit-code' '-r' '--role')
_shtab_ahriman_user_remove_option_strings=('-h' '--help')
_shtab_ahriman_version_option_strings=('-h' '--help')
_shtab_ahriman_web_option_strings=('-h' '--help')
_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'daemon' 'help' 'help-commands-unsafe' 'key-import' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-clean' 'clean' 'repo-config' 'config' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-setup' 'init' 'repo-init' 'setup' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'shell' 'user-add' 'user-list' 'user-remove' 'version' 'web')
_shtab_ahriman_aur_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'url' 'url_path' 'version')
_shtab_ahriman_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'url' 'url_path' 'version')
_shtab_ahriman_package_add__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_add___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_add__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_add___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_update__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_update___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_status__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_setup___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_init___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_repo_init___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_setup___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_repo_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_user_add__r_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_user_add___role_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_user_list__r_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_user_list___role_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_pos_0_nargs=A...
_shtab_ahriman__h_nargs=0
_shtab_ahriman___help_nargs=0
_shtab_ahriman___force_nargs=0
_shtab_ahriman___report_nargs=0
_shtab_ahriman___no_report_nargs=0
_shtab_ahriman__q_nargs=0
_shtab_ahriman___quiet_nargs=0
_shtab_ahriman___unsafe_nargs=0
_shtab_ahriman__V_nargs=0
_shtab_ahriman___version_nargs=0
_shtab_ahriman_aur_search_pos_0_nargs=+
_shtab_ahriman_aur_search__h_nargs=0
_shtab_ahriman_aur_search___help_nargs=0
_shtab_ahriman_aur_search__e_nargs=0
_shtab_ahriman_aur_search___exit_code_nargs=0
_shtab_ahriman_aur_search___info_nargs=0
_shtab_ahriman_aur_search___no_info_nargs=0
_shtab_ahriman_search_pos_0_nargs=+
_shtab_ahriman_search__h_nargs=0
_shtab_ahriman_search___help_nargs=0
_shtab_ahriman_search__e_nargs=0
_shtab_ahriman_search___exit_code_nargs=0
_shtab_ahriman_search___info_nargs=0
_shtab_ahriman_search___no_info_nargs=0
_shtab_ahriman_daemon__h_nargs=0
_shtab_ahriman_daemon___help_nargs=0
_shtab_ahriman_daemon___aur_nargs=0
_shtab_ahriman_daemon___no_aur_nargs=0
_shtab_ahriman_daemon___local_nargs=0
_shtab_ahriman_daemon___no_local_nargs=0
_shtab_ahriman_daemon___manual_nargs=0
_shtab_ahriman_daemon___no_manual_nargs=0
_shtab_ahriman_daemon___vcs_nargs=0
_shtab_ahriman_daemon___no_vcs_nargs=0
_shtab_ahriman_daemon__y_nargs=0
_shtab_ahriman_daemon___refresh_nargs=0
_shtab_ahriman_help__h_nargs=0
_shtab_ahriman_help___help_nargs=0
_shtab_ahriman_help_commands_unsafe__h_nargs=0
_shtab_ahriman_help_commands_unsafe___help_nargs=0
_shtab_ahriman_key_import__h_nargs=0
_shtab_ahriman_key_import___help_nargs=0
_shtab_ahriman_package_add_pos_0_nargs=+
_shtab_ahriman_package_add__h_nargs=0
_shtab_ahriman_package_add___help_nargs=0
_shtab_ahriman_package_add__e_nargs=0
_shtab_ahriman_package_add___exit_code_nargs=0
_shtab_ahriman_package_add__n_nargs=0
_shtab_ahriman_package_add___now_nargs=0
_shtab_ahriman_package_add__y_nargs=0
_shtab_ahriman_package_add___refresh_nargs=0
_shtab_ahriman_package_add___without_dependencies_nargs=0
_shtab_ahriman_add_pos_0_nargs=+
_shtab_ahriman_add__h_nargs=0
_shtab_ahriman_add___help_nargs=0
_shtab_ahriman_add__e_nargs=0
_shtab_ahriman_add___exit_code_nargs=0
_shtab_ahriman_add__n_nargs=0
_shtab_ahriman_add___now_nargs=0
_shtab_ahriman_add__y_nargs=0
_shtab_ahriman_add___refresh_nargs=0
_shtab_ahriman_add___without_dependencies_nargs=0
_shtab_ahriman_package_update_pos_0_nargs=+
_shtab_ahriman_package_update__h_nargs=0
_shtab_ahriman_package_update___help_nargs=0
_shtab_ahriman_package_update__e_nargs=0
_shtab_ahriman_package_update___exit_code_nargs=0
_shtab_ahriman_package_update__n_nargs=0
_shtab_ahriman_package_update___now_nargs=0
_shtab_ahriman_package_update__y_nargs=0
_shtab_ahriman_package_update___refresh_nargs=0
_shtab_ahriman_package_update___without_dependencies_nargs=0
_shtab_ahriman_package_remove_pos_0_nargs=+
_shtab_ahriman_package_remove__h_nargs=0
_shtab_ahriman_package_remove___help_nargs=0
_shtab_ahriman_remove_pos_0_nargs=+
_shtab_ahriman_remove__h_nargs=0
_shtab_ahriman_remove___help_nargs=0
_shtab_ahriman_package_status_pos_0_nargs=*
_shtab_ahriman_package_status__h_nargs=0
_shtab_ahriman_package_status___help_nargs=0
_shtab_ahriman_package_status___ahriman_nargs=0
_shtab_ahriman_package_status__e_nargs=0
_shtab_ahriman_package_status___exit_code_nargs=0
_shtab_ahriman_package_status___info_nargs=0
_shtab_ahriman_package_status___no_info_nargs=0
_shtab_ahriman_status_pos_0_nargs=*
_shtab_ahriman_status__h_nargs=0
_shtab_ahriman_status___help_nargs=0
_shtab_ahriman_status___ahriman_nargs=0
_shtab_ahriman_status__e_nargs=0
_shtab_ahriman_status___exit_code_nargs=0
_shtab_ahriman_status___info_nargs=0
_shtab_ahriman_status___no_info_nargs=0
_shtab_ahriman_package_status_remove_pos_0_nargs=+
_shtab_ahriman_package_status_remove__h_nargs=0
_shtab_ahriman_package_status_remove___help_nargs=0
_shtab_ahriman_package_status_update_pos_0_nargs=*
_shtab_ahriman_package_status_update__h_nargs=0
_shtab_ahriman_package_status_update___help_nargs=0
_shtab_ahriman_status_update_pos_0_nargs=*
_shtab_ahriman_status_update__h_nargs=0
_shtab_ahriman_status_update___help_nargs=0
_shtab_ahriman_patch_add__h_nargs=0
_shtab_ahriman_patch_add___help_nargs=0
_shtab_ahriman_patch_list__h_nargs=0
_shtab_ahriman_patch_list___help_nargs=0
_shtab_ahriman_patch_list__e_nargs=0
_shtab_ahriman_patch_list___exit_code_nargs=0
_shtab_ahriman_patch_remove__h_nargs=0
_shtab_ahriman_patch_remove___help_nargs=0
_shtab_ahriman_patch_set_add__h_nargs=0
_shtab_ahriman_patch_set_add___help_nargs=0
_shtab_ahriman_repo_backup__h_nargs=0
_shtab_ahriman_repo_backup___help_nargs=0
_shtab_ahriman_repo_check_pos_0_nargs=*
_shtab_ahriman_repo_check__h_nargs=0
_shtab_ahriman_repo_check___help_nargs=0
_shtab_ahriman_repo_check__e_nargs=0
_shtab_ahriman_repo_check___exit_code_nargs=0
_shtab_ahriman_repo_check___vcs_nargs=0
_shtab_ahriman_repo_check___no_vcs_nargs=0
_shtab_ahriman_repo_check__y_nargs=0
_shtab_ahriman_repo_check___refresh_nargs=0
_shtab_ahriman_check_pos_0_nargs=*
_shtab_ahriman_check__h_nargs=0
_shtab_ahriman_check___help_nargs=0
_shtab_ahriman_check__e_nargs=0
_shtab_ahriman_check___exit_code_nargs=0
_shtab_ahriman_check___vcs_nargs=0
_shtab_ahriman_check___no_vcs_nargs=0
_shtab_ahriman_check__y_nargs=0
_shtab_ahriman_check___refresh_nargs=0
_shtab_ahriman_repo_clean__h_nargs=0
_shtab_ahriman_repo_clean___help_nargs=0
_shtab_ahriman_repo_clean___cache_nargs=0
_shtab_ahriman_repo_clean___no_cache_nargs=0
_shtab_ahriman_repo_clean___chroot_nargs=0
_shtab_ahriman_repo_clean___no_chroot_nargs=0
_shtab_ahriman_repo_clean___manual_nargs=0
_shtab_ahriman_repo_clean___no_manual_nargs=0
_shtab_ahriman_repo_clean___packages_nargs=0
_shtab_ahriman_repo_clean___no_packages_nargs=0
_shtab_ahriman_repo_clean___pacman_nargs=0
_shtab_ahriman_repo_clean___no_pacman_nargs=0
_shtab_ahriman_clean__h_nargs=0
_shtab_ahriman_clean___help_nargs=0
_shtab_ahriman_clean___cache_nargs=0
_shtab_ahriman_clean___no_cache_nargs=0
_shtab_ahriman_clean___chroot_nargs=0
_shtab_ahriman_clean___no_chroot_nargs=0
_shtab_ahriman_clean___manual_nargs=0
_shtab_ahriman_clean___no_manual_nargs=0
_shtab_ahriman_clean___packages_nargs=0
_shtab_ahriman_clean___no_packages_nargs=0
_shtab_ahriman_clean___pacman_nargs=0
_shtab_ahriman_clean___no_pacman_nargs=0
_shtab_ahriman_repo_config__h_nargs=0
_shtab_ahriman_repo_config___help_nargs=0
_shtab_ahriman_config__h_nargs=0
_shtab_ahriman_config___help_nargs=0
_shtab_ahriman_repo_rebuild__h_nargs=0
_shtab_ahriman_repo_rebuild___help_nargs=0
_shtab_ahriman_repo_rebuild___dry_run_nargs=0
_shtab_ahriman_repo_rebuild___from_database_nargs=0
_shtab_ahriman_repo_rebuild__e_nargs=0
_shtab_ahriman_repo_rebuild___exit_code_nargs=0
_shtab_ahriman_rebuild__h_nargs=0
_shtab_ahriman_rebuild___help_nargs=0
_shtab_ahriman_rebuild___dry_run_nargs=0
_shtab_ahriman_rebuild___from_database_nargs=0
_shtab_ahriman_rebuild__e_nargs=0
_shtab_ahriman_rebuild___exit_code_nargs=0
_shtab_ahriman_repo_remove_unknown__h_nargs=0
_shtab_ahriman_repo_remove_unknown___help_nargs=0
_shtab_ahriman_repo_remove_unknown___dry_run_nargs=0
_shtab_ahriman_remove_unknown__h_nargs=0
_shtab_ahriman_remove_unknown___help_nargs=0
_shtab_ahriman_remove_unknown___dry_run_nargs=0
_shtab_ahriman_repo_report__h_nargs=0
_shtab_ahriman_repo_report___help_nargs=0
_shtab_ahriman_report__h_nargs=0
_shtab_ahriman_report___help_nargs=0
_shtab_ahriman_repo_restore__h_nargs=0
_shtab_ahriman_repo_restore___help_nargs=0
_shtab_ahriman_repo_setup__h_nargs=0
_shtab_ahriman_repo_setup___help_nargs=0
_shtab_ahriman_repo_setup___makeflags_jobs_nargs=0
_shtab_ahriman_repo_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_repo_setup___multilib_nargs=0
_shtab_ahriman_repo_setup___no_multilib_nargs=0
_shtab_ahriman_init__h_nargs=0
_shtab_ahriman_init___help_nargs=0
_shtab_ahriman_init___makeflags_jobs_nargs=0
_shtab_ahriman_init___no_makeflags_jobs_nargs=0
_shtab_ahriman_init___multilib_nargs=0
_shtab_ahriman_init___no_multilib_nargs=0
_shtab_ahriman_repo_init__h_nargs=0
_shtab_ahriman_repo_init___help_nargs=0
_shtab_ahriman_repo_init___makeflags_jobs_nargs=0
_shtab_ahriman_repo_init___no_makeflags_jobs_nargs=0
_shtab_ahriman_repo_init___multilib_nargs=0
_shtab_ahriman_repo_init___no_multilib_nargs=0
_shtab_ahriman_setup__h_nargs=0
_shtab_ahriman_setup___help_nargs=0
_shtab_ahriman_setup___makeflags_jobs_nargs=0
_shtab_ahriman_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_setup___multilib_nargs=0
_shtab_ahriman_setup___no_multilib_nargs=0
_shtab_ahriman_repo_sign_pos_0_nargs=*
_shtab_ahriman_repo_sign__h_nargs=0
_shtab_ahriman_repo_sign___help_nargs=0
_shtab_ahriman_sign_pos_0_nargs=*
_shtab_ahriman_sign__h_nargs=0
_shtab_ahriman_sign___help_nargs=0
_shtab_ahriman_repo_status_update__h_nargs=0
_shtab_ahriman_repo_status_update___help_nargs=0
_shtab_ahriman_repo_sync__h_nargs=0
_shtab_ahriman_repo_sync___help_nargs=0
_shtab_ahriman_sync__h_nargs=0
_shtab_ahriman_sync___help_nargs=0
_shtab_ahriman_repo_tree__h_nargs=0
_shtab_ahriman_repo_tree___help_nargs=0
_shtab_ahriman_repo_triggers_pos_0_nargs=*
_shtab_ahriman_repo_triggers__h_nargs=0
_shtab_ahriman_repo_triggers___help_nargs=0
_shtab_ahriman_repo_update_pos_0_nargs=*
_shtab_ahriman_repo_update__h_nargs=0
_shtab_ahriman_repo_update___help_nargs=0
_shtab_ahriman_repo_update___dry_run_nargs=0
_shtab_ahriman_repo_update__e_nargs=0
_shtab_ahriman_repo_update___exit_code_nargs=0
_shtab_ahriman_repo_update___aur_nargs=0
_shtab_ahriman_repo_update___no_aur_nargs=0
_shtab_ahriman_repo_update___local_nargs=0
_shtab_ahriman_repo_update___no_local_nargs=0
_shtab_ahriman_repo_update___manual_nargs=0
_shtab_ahriman_repo_update___no_manual_nargs=0
_shtab_ahriman_repo_update___vcs_nargs=0
_shtab_ahriman_repo_update___no_vcs_nargs=0
_shtab_ahriman_repo_update__y_nargs=0
_shtab_ahriman_repo_update___refresh_nargs=0
_shtab_ahriman_update_pos_0_nargs=*
_shtab_ahriman_update__h_nargs=0
_shtab_ahriman_update___help_nargs=0
_shtab_ahriman_update___dry_run_nargs=0
_shtab_ahriman_update__e_nargs=0
_shtab_ahriman_update___exit_code_nargs=0
_shtab_ahriman_update___aur_nargs=0
_shtab_ahriman_update___no_aur_nargs=0
_shtab_ahriman_update___local_nargs=0
_shtab_ahriman_update___no_local_nargs=0
_shtab_ahriman_update___manual_nargs=0
_shtab_ahriman_update___no_manual_nargs=0
_shtab_ahriman_update___vcs_nargs=0
_shtab_ahriman_update___no_vcs_nargs=0
_shtab_ahriman_update__y_nargs=0
_shtab_ahriman_update___refresh_nargs=0
_shtab_ahriman_shell__h_nargs=0
_shtab_ahriman_shell___help_nargs=0
_shtab_ahriman_shell__v_nargs=0
_shtab_ahriman_shell___verbose_nargs=0
_shtab_ahriman_user_add__h_nargs=0
_shtab_ahriman_user_add___help_nargs=0
_shtab_ahriman_user_add__s_nargs=0
_shtab_ahriman_user_add___secure_nargs=0
_shtab_ahriman_user_list__h_nargs=0
_shtab_ahriman_user_list___help_nargs=0
_shtab_ahriman_user_list__e_nargs=0
_shtab_ahriman_user_list___exit_code_nargs=0
_shtab_ahriman_user_remove__h_nargs=0
_shtab_ahriman_user_remove___help_nargs=0
_shtab_ahriman_version__h_nargs=0
_shtab_ahriman_version___help_nargs=0
_shtab_ahriman_web__h_nargs=0
_shtab_ahriman_web___help_nargs=0
# $1=COMP_WORDS[1]
_shtab_compgen_files() {
compgen -f -- $1 # files
}
# $1=COMP_WORDS[1]
_shtab_compgen_dirs() {
compgen -d -- $1 # recurse into subdirs
}
# $1=COMP_WORDS[1]
_shtab_replace_nonword() {
echo "${1//[^[:word:]]/_}"
}
# set default values (called for the initial parser & any subparsers)
_set_parser_defaults() {
local subparsers_var="${prefix}_subparsers[@]"
sub_parsers=${!subparsers_var}
local current_option_strings_var="${prefix}_option_strings[@]"
current_option_strings=${!current_option_strings_var}
completed_positional_actions=0
_set_new_action "pos_${completed_positional_actions}" true
}
# $1=action identifier
# $2=positional action (bool)
# set all identifiers for an action's parameters
_set_new_action() {
current_action="${prefix}_$(_shtab_replace_nonword $1)"
local current_action_compgen_var=${current_action}_COMPGEN
current_action_compgen="${!current_action_compgen_var}"
local current_action_choices_var="${current_action}_choices[@]"
current_action_choices="${!current_action_choices_var}"
local current_action_nargs_var="${current_action}_nargs"
if [ -n "${!current_action_nargs_var}" ]; then
current_action_nargs="${!current_action_nargs_var}"
else
current_action_nargs=1
fi
current_action_args_start_index=$(( $word_index + 1 ))
current_action_is_positional=$2
}
# Notes:
# `COMPREPLY`: what will be rendered after completion is triggered
# `completing_word`: currently typed word to generate completions for
# `${!var}`: evaluates the content of `var` and expand its content as a variable
# hello="world"
# x="hello"
# ${!x} -> ${hello} -> "world"
_shtab_ahriman() {
local completing_word="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=()
prefix=_shtab_ahriman
word_index=0
_set_parser_defaults
word_index=1
# determine what arguments are appropriate for the current state
# of the arg parser
while [ $word_index -ne $COMP_CWORD ]; do
local this_word="${COMP_WORDS[$word_index]}"
if [[ -n $sub_parsers && " ${sub_parsers[@]} " =~ " ${this_word} " ]]; then
# valid subcommand: add it to the prefix & reset the current action
prefix="${prefix}_$(_shtab_replace_nonword $this_word)"
_set_parser_defaults
fi
if [[ " ${current_option_strings[@]} " =~ " ${this_word} " ]]; then
# a new action should be acquired (due to recognised option string or
# no more input expected from current action);
# the next positional action can fill in here
_set_new_action $this_word false
fi
if [[ "$current_action_nargs" != "*" ]] && \
[[ "$current_action_nargs" != "+" ]] && \
[[ "$current_action_nargs" != *"..." ]] && \
(( $word_index + 1 - $current_action_args_start_index >= \
$current_action_nargs )); then
$current_action_is_positional && let "completed_positional_actions += 1"
_set_new_action "pos_${completed_positional_actions}" true
fi
let "word_index+=1"
done
# Generate the completions
if [[ "${completing_word}" == -* ]]; then
# optional argument started: use option strings
COMPREPLY=( $(compgen -W "${current_option_strings[*]}" -- "${completing_word}") )
else
# use choices & compgen
local IFS=$'\n' # items may contain spaces, so delimit using newline
COMPREPLY=( $([ -n "${current_action_compgen}" ] \
&& "${current_action_compgen}" "${completing_word}") )
unset IFS
COMPREPLY+=( $(compgen -W "${current_action_choices[*]}" -- "${completing_word}") )
fi
return 0
}
complete -o filenames -F _shtab_ahriman ahriman

View File

@ -0,0 +1,531 @@
#compdef ahriman
# AUTOMATICALLY GENERATED by `shtab`
_shtab_ahriman_commands() {
local _commands=(
"add:add existing or new package to the build queue"
"aur-search:search for package in AUR using API"
"check:check for packages updates. Same as repo-update --dry-run --no-manual"
"clean:remove local caches"
"config:dump configuration for the specified architecture"
"daemon:start process which periodically will run update process"
"help:show help message for application or command and exit"
"help-commands-unsafe:list unsafe commands as defined in default args"
"init:create initial service configuration, requires root"
"key-import:import PGP key from public sources to the repository user"
"package-add:add existing or new package to the build queue"
"package-remove:remove package from the repository"
"package-status:request status of the package"
"package-status-remove:remove the package from the status page"
"package-status-update:update package status on the status page"
"package-update:add existing or new package to the build queue"
"patch-add:create or update patched PKGBUILD function or variable"
"patch-list:list available patches for the package"
"patch-remove:remove patches for the package"
"patch-set-add:create or update source patches"
"rebuild:force rebuild whole repository"
"remove:remove package from the repository"
"remove-unknown:remove packages which are missing in AUR and do not have local PKGBUILDs"
"repo-backup:backup repository settings and database"
"repo-check:check for packages updates. Same as repo-update --dry-run --no-manual"
"repo-clean:remove local caches"
"repo-config:dump configuration for the specified architecture"
"repo-init:create initial service configuration, requires root"
"repo-rebuild:force rebuild whole repository"
"repo-remove-unknown:remove packages which are missing in AUR and do not have local PKGBUILDs"
"repo-report:generate repository report according to current settings"
"repo-restore:restore settings and database"
"repo-setup:create initial service configuration, requires root"
"repo-sign:(re-)sign packages and repository database according to current settings"
"repo-status-update:update repository status on the status page"
"repo-sync:sync repository files to remote server according to current settings"
"repo-tree:dump repository tree based on packages dependencies"
"repo-triggers:run triggers on empty build result as configured by settings"
"repo-update:check for packages updates and run build process if requested"
"report:generate repository report according to current settings"
"search:search for package in AUR using API"
"setup:create initial service configuration, requires root"
"shell:drop into python shell while having created application"
"sign:(re-)sign packages and repository database according to current settings"
"status:request status of the package"
"status-update:update package status on the status page"
"sync:sync repository files to remote server according to current settings"
"update:check for packages updates and run build process if requested"
"user-add:update user for web services with the given password and role. In case if password was not entered it will be asked interactively"
"user-list:list users from the user mapping and their roles"
"user-remove:remove user from the user mapping and update the configuration"
"version:print application and its dependencies versions"
"web:start web server"
)
_describe 'ahriman commands' _commands
}
_shtab_ahriman_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*"{-a,--architecture}"[target architectures. For several subcommands it can be used multiple times]:architecture:"
{-c,--configuration}"[configuration path]:configuration:"
"--force[force run, remove file lock]"
{-l,--lock}"[lock file]:lock:"
{--report,--no-report}"[force enable or disable reporting to web service (default\: \%(default)s)]:report:"
{-q,--quiet}"[force disable any logging]"
"--unsafe[allow to run ahriman as non-ahriman user. Some actions might be unavailable]"
"(- : *)"{-V,--version}"[show program\'s version number and exit]"
)
_shtab_ahriman_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{-n,--now}"[run update function after]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
{-s,--source}"[explicitly specify the package source for this command]:source:(auto archive aur directory local remote repository)"
"--without-dependencies[do not add dependencies]"
"(*):package source (base name, path to local files, remote URL):"
)
_shtab_ahriman_aur_search_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--info,--no-info}"[show additional package information (default\: \%(default)s)]:info:"
"--sort-by[sort field by this field. In case if two packages have the same value of the specified field, they will be always sorted by name]:sort_by:(description first_submitted id last_modified maintainer name num_votes out_of_date package_base package_base_id popularity repository url url_path version)"
"(*):search terms, can be specified multiple times, the result will match all terms:"
)
_shtab_ahriman_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--vcs,--no-vcs}"[enable or disable checking of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
"(*)::filter check by package base:"
)
_shtab_ahriman_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches (default\: \%(default)s)]:cache:"
{--chroot,--no-chroot}"[clear build chroot (default\: \%(default)s)]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue (default\: \%(default)s)]:manual:"
{--packages,--no-packages}"[clear directory with built packages (default\: \%(default)s)]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: \%(default)s)]:pacman:"
)
_shtab_ahriman_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_daemon_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-i,--interval}"[interval between runs in seconds]:interval:"
{--aur,--no-aur}"[enable or disable checking for AUR updates. Implies --no-vcs (default\: \%(default)s)]:aur:"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: \%(default)s)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: \%(default)s)]:manual:"
{--vcs,--no-vcs}"[enable or disable checking of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
)
_shtab_ahriman_help_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":show help message for specific command:"
)
_shtab_ahriman_help_commands_unsafe_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--command[instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1 otherwise]:command:"
)
_shtab_ahriman_init_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one]:build_as_user:"
"--build-command[build command prefix]:build_command:"
"--from-configuration[path to default devtools pacman configuration]:from_configuration:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: \%(default)s)]:makeflags_jobs:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: \%(default)s)]:multilib:"
"--packager[packager name and email]:packager:"
"--repository[repository name]:repository:"
"--sign-key[sign key id]:sign_key:"
"*--sign-target[sign options]:sign_target:(disabled packages repository)"
"--web-port[port of the web service]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications]:web_unix_socket:"
)
_shtab_ahriman_key_import_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--key-server[key server for key import]:key_server:"
":PGP key to import from public server:"
)
_shtab_ahriman_package_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{-n,--now}"[run update function after]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
{-s,--source}"[explicitly specify the package source for this command]:source:(auto archive aur directory local remote repository)"
"--without-dependencies[do not add dependencies]"
"(*):package source (base name, path to local files, remote URL):"
)
_shtab_ahriman_package_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):package name or base:"
)
_shtab_ahriman_package_status_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--ahriman[get service status itself]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--info,--no-info}"[show additional package information (default\: \%(default)s)]:info:"
{-s,--status}"[filter packages by status]:status:(unknown pending building failed success)"
"(*)::filter status by package base:"
)
_shtab_ahriman_package_status_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):remove specified packages from status page:"
)
_shtab_ahriman_package_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new package build status]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated:"
)
_shtab_ahriman_package_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{-n,--now}"[run update function after]"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
{-s,--source}"[explicitly specify the package source for this command]:source:(auto archive aur directory local remote repository)"
"--without-dependencies[do not add dependencies]"
"(*):package source (base name, path to local files, remote URL):"
)
_shtab_ahriman_patch_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":package base:"
":PKGBUILD variable or function name. If variable is a function, it must end with ():"
":path to file which contains function or variable value. If not set, the value will be read from stdin:"
)
_shtab_ahriman_patch_list_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
"*"{-v,--variable}"[if set, show only patches for specified PKGBUILD variables]:variable:"
":package base:"
)
_shtab_ahriman_patch_remove_options=(
"(- : *)"{-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]:variable:"
":package base:"
)
_shtab_ahriman_patch_set_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*"{-t,--track}"[files which has to be tracked]:track:"
":path to directory with changed files for patch addition\/update:"
)
_shtab_ahriman_rebuild_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*--depends-on[only rebuild packages that depend on specified packages]:depends_on:"
"--dry-run[just perform check for packages without rebuild process itself]"
"--from-database[read packages from database instead of filesystem. This feature in particular is required in case if you would like to restore repository from another repository instance. Note, however, that in order to restore packages you need to have original ahriman instance run with web service and have run repo-update at least once.]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
)
_shtab_ahriman_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):package name or base:"
)
_shtab_ahriman_remove_unknown_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for packages without removal]"
)
_shtab_ahriman_repo_backup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":path of the output archive:"
)
_shtab_ahriman_repo_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--vcs,--no-vcs}"[enable or disable checking of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
"(*)::filter check by package base:"
)
_shtab_ahriman_repo_clean_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--cache,--no-cache}"[clear directory with package caches (default\: \%(default)s)]:cache:"
{--chroot,--no-chroot}"[clear build chroot (default\: \%(default)s)]:chroot:"
{--manual,--no-manual}"[clear manually added packages queue (default\: \%(default)s)]:manual:"
{--packages,--no-packages}"[clear directory with built packages (default\: \%(default)s)]:packages:"
{--pacman,--no-pacman}"[clear directory with pacman local database cache (default\: \%(default)s)]:pacman:"
)
_shtab_ahriman_repo_config_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_repo_init_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one]:build_as_user:"
"--build-command[build command prefix]:build_command:"
"--from-configuration[path to default devtools pacman configuration]:from_configuration:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: \%(default)s)]:makeflags_jobs:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: \%(default)s)]:multilib:"
"--packager[packager name and email]:packager:"
"--repository[repository name]:repository:"
"--sign-key[sign key id]:sign_key:"
"*--sign-target[sign options]:sign_target:(disabled packages repository)"
"--web-port[port of the web service]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications]:web_unix_socket:"
)
_shtab_ahriman_repo_rebuild_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"*--depends-on[only rebuild packages that depend on specified packages]:depends_on:"
"--dry-run[just perform check for packages without rebuild process itself]"
"--from-database[read packages from database instead of filesystem. This feature in particular is required in case if you would like to restore repository from another repository instance. Note, however, that in order to restore packages you need to have original ahriman instance run with web service and have run repo-update at least once.]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
)
_shtab_ahriman_repo_remove_unknown_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for packages without removal]"
)
_shtab_ahriman_repo_report_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_repo_restore_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-o,--output}"[root path of the extracted files]:output:"
":path of the input archive:"
)
_shtab_ahriman_repo_setup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one]:build_as_user:"
"--build-command[build command prefix]:build_command:"
"--from-configuration[path to default devtools pacman configuration]:from_configuration:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: \%(default)s)]:makeflags_jobs:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: \%(default)s)]:multilib:"
"--packager[packager name and email]:packager:"
"--repository[repository name]:repository:"
"--sign-key[sign key id]:sign_key:"
"*--sign-target[sign options]:sign_target:(disabled packages repository)"
"--web-port[port of the web service]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications]:web_unix_socket:"
)
_shtab_ahriman_repo_sign_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::sign only specified packages:"
)
_shtab_ahriman_repo_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new status]:status:(unknown pending building failed success)"
)
_shtab_ahriman_repo_sync_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_repo_tree_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_repo_triggers_options=(
"(- : *)"{-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:"
)
_shtab_ahriman_repo_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for updates, same as check command]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--aur,--no-aur}"[enable or disable checking for AUR updates. Implies --no-vcs (default\: \%(default)s)]:aur:"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: \%(default)s)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: \%(default)s)]:manual:"
{--vcs,--no-vcs}"[enable or disable checking of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
"(*)::filter check by package base:"
)
_shtab_ahriman_report_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_search_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--info,--no-info}"[show additional package information (default\: \%(default)s)]:info:"
"--sort-by[sort field by this field. In case if two packages have the same value of the specified field, they will be always sorted by name]:sort_by:(description first_submitted id last_modified maintainer name num_votes out_of_date package_base package_base_id popularity repository url url_path version)"
"(*):search terms, can be specified multiple times, the result will match all terms:"
)
_shtab_ahriman_setup_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--build-as-user[force makepkg user to the specific one]:build_as_user:"
"--build-command[build command prefix]:build_command:"
"--from-configuration[path to default devtools pacman configuration]:from_configuration:"
{--makeflags-jobs,--no-makeflags-jobs}"[append MAKEFLAGS variable with parallelism set to number of cores (default\: \%(default)s)]:makeflags_jobs:"
{--multilib,--no-multilib}"[add or do not multilib repository (default\: \%(default)s)]:multilib:"
"--packager[packager name and email]:packager:"
"--repository[repository name]:repository:"
"--sign-key[sign key id]:sign_key:"
"*--sign-target[sign options]:sign_target:(disabled packages repository)"
"--web-port[port of the web service]:web_port:"
"--web-unix-socket[path to unix socket used for interprocess communications]:web_unix_socket:"
)
_shtab_ahriman_shell_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":instead of dropping into shell, just execute the specified code:"
)
_shtab_ahriman_sign_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*)::sign only specified packages:"
)
_shtab_ahriman_status_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--ahriman[get service status itself]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--info,--no-info}"[show additional package information (default\: \%(default)s)]:info:"
{-s,--status}"[filter packages by status]:status:(unknown pending building failed success)"
"(*)::filter status by package base:"
)
_shtab_ahriman_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new package build status]:status:(unknown pending building failed success)"
"(*)::set status for specified packages. If no packages supplied, service status will be updated:"
)
_shtab_ahriman_sync_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--dry-run[just perform check for updates, same as check command]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{--aur,--no-aur}"[enable or disable checking for AUR updates. Implies --no-vcs (default\: \%(default)s)]:aur:"
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: \%(default)s)]:local:"
{--manual,--no-manual}"[include or exclude manual updates (default\: \%(default)s)]:manual:"
{--vcs,--no-vcs}"[enable or disable checking of VCS packages (default\: \%(default)s)]:vcs:"
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date]"
"(*)::filter check by package base:"
)
_shtab_ahriman_user_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-p,--password}"[user password. Blank password will be treated as empty password, which is in particular must be used for OAuth2 authorization type.]:password:"
{-r,--role}"[user access level]:role:(unauthorized read reporter full)"
{-s,--secure}"[set file permissions to user-only]"
":username for web service:"
)
_shtab_ahriman_user_list_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty]"
{-r,--role}"[filter users by role]:role:(unauthorized read reporter full)"
":filter users by username:"
)
_shtab_ahriman_user_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
":username for web service:"
)
_shtab_ahriman_version_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman_web_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
)
_shtab_ahriman() {
local context state line curcontext="$curcontext" one_or_more='(-)*' remainder='(*)'
if ((${_shtab_ahriman_options[(I)${(q)one_or_more}*]} + ${_shtab_ahriman_options[(I)${(q)remainder}*]} == 0)); then # noqa: E501
_shtab_ahriman_options+=(': :_shtab_ahriman_commands' '*::: :->ahriman')
fi
_arguments -C $_shtab_ahriman_options
case $state in
ahriman)
words=($line[1] "${words[@]}")
(( CURRENT += 1 ))
curcontext="${curcontext%:*:*}:_shtab_ahriman-$line[1]:"
case $line[1] in
add) _arguments -C $_shtab_ahriman_add_options ;;
aur-search) _arguments -C $_shtab_ahriman_aur_search_options ;;
check) _arguments -C $_shtab_ahriman_check_options ;;
clean) _arguments -C $_shtab_ahriman_clean_options ;;
config) _arguments -C $_shtab_ahriman_config_options ;;
daemon) _arguments -C $_shtab_ahriman_daemon_options ;;
help) _arguments -C $_shtab_ahriman_help_options ;;
help-commands-unsafe) _arguments -C $_shtab_ahriman_help_commands_unsafe_options ;;
init) _arguments -C $_shtab_ahriman_init_options ;;
key-import) _arguments -C $_shtab_ahriman_key_import_options ;;
package-add) _arguments -C $_shtab_ahriman_package_add_options ;;
package-remove) _arguments -C $_shtab_ahriman_package_remove_options ;;
package-status) _arguments -C $_shtab_ahriman_package_status_options ;;
package-status-remove) _arguments -C $_shtab_ahriman_package_status_remove_options ;;
package-status-update) _arguments -C $_shtab_ahriman_package_status_update_options ;;
package-update) _arguments -C $_shtab_ahriman_package_update_options ;;
patch-add) _arguments -C $_shtab_ahriman_patch_add_options ;;
patch-list) _arguments -C $_shtab_ahriman_patch_list_options ;;
patch-remove) _arguments -C $_shtab_ahriman_patch_remove_options ;;
patch-set-add) _arguments -C $_shtab_ahriman_patch_set_add_options ;;
rebuild) _arguments -C $_shtab_ahriman_rebuild_options ;;
remove) _arguments -C $_shtab_ahriman_remove_options ;;
remove-unknown) _arguments -C $_shtab_ahriman_remove_unknown_options ;;
repo-backup) _arguments -C $_shtab_ahriman_repo_backup_options ;;
repo-check) _arguments -C $_shtab_ahriman_repo_check_options ;;
repo-clean) _arguments -C $_shtab_ahriman_repo_clean_options ;;
repo-config) _arguments -C $_shtab_ahriman_repo_config_options ;;
repo-init) _arguments -C $_shtab_ahriman_repo_init_options ;;
repo-rebuild) _arguments -C $_shtab_ahriman_repo_rebuild_options ;;
repo-remove-unknown) _arguments -C $_shtab_ahriman_repo_remove_unknown_options ;;
repo-report) _arguments -C $_shtab_ahriman_repo_report_options ;;
repo-restore) _arguments -C $_shtab_ahriman_repo_restore_options ;;
repo-setup) _arguments -C $_shtab_ahriman_repo_setup_options ;;
repo-sign) _arguments -C $_shtab_ahriman_repo_sign_options ;;
repo-status-update) _arguments -C $_shtab_ahriman_repo_status_update_options ;;
repo-sync) _arguments -C $_shtab_ahriman_repo_sync_options ;;
repo-tree) _arguments -C $_shtab_ahriman_repo_tree_options ;;
repo-triggers) _arguments -C $_shtab_ahriman_repo_triggers_options ;;
repo-update) _arguments -C $_shtab_ahriman_repo_update_options ;;
report) _arguments -C $_shtab_ahriman_report_options ;;
search) _arguments -C $_shtab_ahriman_search_options ;;
setup) _arguments -C $_shtab_ahriman_setup_options ;;
shell) _arguments -C $_shtab_ahriman_shell_options ;;
sign) _arguments -C $_shtab_ahriman_sign_options ;;
status) _arguments -C $_shtab_ahriman_status_options ;;
status-update) _arguments -C $_shtab_ahriman_status_update_options ;;
sync) _arguments -C $_shtab_ahriman_sync_options ;;
update) _arguments -C $_shtab_ahriman_update_options ;;
user-add) _arguments -C $_shtab_ahriman_user_add_options ;;
user-list) _arguments -C $_shtab_ahriman_user_list_options ;;
user-remove) _arguments -C $_shtab_ahriman_user_remove_options ;;
version) _arguments -C $_shtab_ahriman_version_options ;;
web) _arguments -C $_shtab_ahriman_web_options ;;
esac
esac
}
typeset -A opt_args
_shtab_ahriman "$@"

View File

@ -59,6 +59,7 @@ Build related configuration. Group name can refer to architecture, e.g. ``build:
* ``makepkg_flags`` - additional flags passed to ``makepkg`` command, space separated list of strings, optional.
* ``makechrootpkg_flags`` - additional flags passed to ``makechrootpkg`` command, space separated list of strings, optional.
* ``triggers`` - list of ``ahriman.core.triggers.Trigger`` class implementation (e.g. ``ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger``) which will be loaded and run at the end of processing, space separated list of strings, optional. You can also specify triggers by their paths, e.g. ``/usr/lib/python3.10/site-packages/ahriman/core/report/report.py.ReportTrigger``. Triggers are run in the order of mention.
* ``vcs_allowed_age`` - maximal age in seconds of the VCS packages before their version will be updated with its remote source, int, optional, default ``0``.
``repository`` group
--------------------

View File

@ -36,6 +36,40 @@ Trigger which can be used for reporting. It implements ``on_result`` method and
This trigger takes build result (``on_result``) and performs syncing of the local packages to the remote mirror (e.g. S3 or just by rsync).
Context variables
-----------------
By default, only configuration and architecture are passed to triggers. However, some triggers might want to have access to other high-level wrappers. In order to provide such ability and avoid (double) initialization, the service provides a global context variables, which can be accessed from ``ahriman.core`` package:
.. code-block:: python
from ahriman.core import context
ctx = context.get()
Just because context is wrapped inside ``contexvars.ContextVar``, you need to explicitly extract variable by ``get()`` method. Later you can extract any variable if it is set, e.g.:
.. code-block:: python
from ahriman.core.database import SQLite
from ahriman.models.context_key import ContextKey
database = ctx.get(ContextKey("database", SQLite))
In order to provide typed API, all variables are stored together with their type. The ``get(ContextKey)`` method will throw ``KeyError`` in case if key is missing. Alternatively you can set your own variable inside context:
.. code-block:: python
ctx.set(ContextKey("answer", int), 42)
context.set(ctx)
Note, however, that there are several limitations:
* Context variables are immutable, thus you cannot override value if the key already presented.
* The ``return_type`` of ``ContextKey`` should match the value type, otherwise exception will be thrown.
The ``context`` also implements collection methods such as ``__iter__`` and ``__len__``.
Trigger example
---------------

View File

@ -1,7 +1,7 @@
# Maintainer: Evgeniy Alekseev
pkgname='ahriman'
pkgver=2.4.0
pkgver=2.5.1
pkgrel=1
pkgdesc="ArcH linux ReposItory MANager"
arch=('any')

View File

@ -24,6 +24,7 @@ ignore_packages =
makechrootpkg_flags =
makepkg_flags = --nocolor --ignorearch
triggers = ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger
vcs_allowed_age = 604800
[repository]
name = aur-clone

View File

@ -15,16 +15,15 @@
{% include "utils/bootstrap-scripts.jinja2" %}
<div class="container">
<h1>ahriman
<img id="badge-version" src="https://img.shields.io/badge/version-unknown-informational" alt="unknown">
<img id="badge-repository" src="https://img.shields.io/badge/repository-unknown-informational" alt="unknown">
<img id="badge-architecture" src="https://img.shields.io/badge/architecture-unknown-informational" alt="unknown">
<img id="badge-status" src="https://img.shields.io/badge/service%20status-unknown-inactive" alt="unknown">
</h1>
<h1 id="badge-repository">ahriman</h1>
</div>
<div id="alert-placeholder" class="toast-container p3 top-0 start-50 translate-middle-x"></div>
<div class="container">
<div id="toolbar" class="dropdown">
<a id="badge-status" tabindex="0" role="button" class="btn btn-outline-secondary" data-bs-toggle="popover" data-bs-trigger="focus" data-bs-content="no run data"><i class="bi bi-info-circle"></i></a>
{% if not auth.enabled or auth.username is not none %}
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-box"></i> packages
@ -51,11 +50,13 @@
</button>
</li>
</ul>
<button id="key-import-btn" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#key-import-modal" hidden>
<button id="key-import-btn" type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#key-import-modal" hidden>
<i class="bi bi-key"></i> import key
</button>
{% endif %}
<button class="btn btn-secondary" onclick="reload()">
<button type="button" class="btn btn-secondary" onclick="reload()">
<i class="bi bi-arrow-clockwise"></i> reload
</button>
</div>
@ -96,7 +97,7 @@
<div class="container">
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
<ul class="nav">
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources"><i class="bi bi-github"></i> ahriman</a></li>
<li><a id="badge-version" class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources"><i class="bi bi-github"></i> ahriman</a></li>
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
</ul>
@ -127,8 +128,7 @@
{% include "build-status/login-modal.jinja2" %}
{% endif %}
{% include "build-status/failed-modal.jinja2" %}
{% include "build-status/success-modal.jinja2" %}
{% include "build-status/alerts.jinja2" %}
{% include "build-status/package-add-modal.jinja2" %}
{% include "build-status/package-rebuild-modal.jinja2" %}

View File

@ -0,0 +1,45 @@
<script>
const alertPlaceholder = $("#alert-placeholder");
function createAlert(title, message, clz) {
const wrapper = document.createElement("div");
wrapper.classList.add("toast", clz);
wrapper.role = "alert";
wrapper.ariaLive = "assertive";
wrapper.ariaAtomic = "true";
wrapper.style.width = "500px"; // 500px is default modal size
const header = document.createElement("div");
header.classList.add("toast-header");
header.innerHTML = `<strong class="me-auto">${safe(title)}</strong> <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="close"></button>`;
wrapper.appendChild(header);
const body = document.createElement("div");
body.classList.add("toast-body", "text-bg-light");
body.innerText = message;
wrapper.appendChild(body);
alertPlaceholder.append(wrapper);
const toast = new bootstrap.Toast(wrapper);
wrapper.addEventListener("hidden.bs.toast", () => {
wrapper.remove(); // bootstrap doesn't remove elements
reload();
});
toast.show();
}
function showFailure(title, description, jqXHR, errorThrown) {
let details;
try {
details = $.parseJSON(jqXHR.responseText).error; // execution handler json error response
} catch (_) {
details = errorThrown;
}
createAlert(title, description(details), "text-bg-danger");
}
function showSuccess(title, description) {
createAlert(title, description, "text-bg-success");
}
</script>

View File

@ -1,34 +0,0 @@
<div id="failed-modal" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h4 id="failed-title" class="modal-title"></h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<p id="failed-description"></p>
<p id="failed-details"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i> close</button>
</div>
</div>
</div>
</div>
<script>
const failedModal = $("#failed-modal");
failedModal.on("hidden.bs.modal", () => { reload(); });
const failedDescription = $("#failed-description");
const failedDetails = $("#failed-details");
const failedTitle = $("#failed-title");
function showFailure(title, description, details) {
failedTitle.text(title);
failedDescription.text(description);
failedDetails.text(details);
failedModal.modal("show");
}
</script>

View File

@ -22,7 +22,7 @@
<div class="form-group row">
<div class="col-sm-2"></div>
<div class="col-sm-10">
<pre class="language-less"><code id="key-body-input" class="pre-scrollable language-less"></code><button id="key-copy-btn" type="button" class="btn language-less" onclick="copyPgpKey()"><i class="bi bi-clipboard"></i> copy</button></pre>
<pre class="language-less"><samp id="key-body-input" class="pre-scrollable language-less"></samp><button id="key-copy-btn" type="button" class="btn language-less" onclick="copyPgpKey()"><i class="bi bi-clipboard"></i> copy</button></pre>
</div>
</div>
</div>
@ -81,10 +81,11 @@
contentType: "application/json",
success: _ => {
keyImportModal.modal("hide");
showSuccess("Success", `Key ${key} has been imported`, "");
showSuccess("Success", `Key ${key} has been imported`);
},
error: (jqXHR, _, errorThrown) => {
showFailure("Action failed", `Could not import key ${key} from ${server}`, errorThrown);
const message = _ => { return `Could not import key ${key} from ${server}`; };
showFailure("Action failed", message, jqXHR, errorThrown);
},
});
}

View File

@ -60,7 +60,9 @@
const packages = packageInput.val();
if (packages) {
packageAddModal.modal("hide");
doPackageAction("/api/v1/service/add", [packages], "The following package has been added:", "Package addition failed:");
const onSuccess = update => { return `Packages ${update} have been added`; };
const onFailure = error => { return `Package addition failed: ${error}`; };
doPackageAction("/api/v1/service/add", [packages], onSuccess, onFailure);
}
}
@ -68,7 +70,9 @@
const packages = packageInput.val();
if (packages) {
packageAddModal.modal("hide");
doPackageAction("/api/v1/service/request", [packages], "The following package has been requested:", "Package request failed:");
const onSuccess = update => { return `Packages ${update} have been requested`; };
const onFailure = error => { return `Package request failed: ${error}`; };
doPackageAction("/api/v1/service/request", [packages], onSuccess, onFailure);
}
}
</script>

View File

@ -6,7 +6,7 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<pre class="language-logs"><code id="package-info-logs-input" class="pre-scrollable language-logs"></code><button id="logs-copy-btn" type="button" class="btn language-logs" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
<pre class="language-logs"><samp id="package-info-logs-input" class="pre-scrollable language-logs"></samp><button id="logs-copy-btn" type="button" class="btn language-logs" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="showLogs()"><i class="bi bi-arrow-clockwise"></i> reload</button>
@ -60,7 +60,10 @@
},
error: (jqXHR, _, errorThrown) => {
// show failed modal in case if first time loading
if (isPackageBaseSet) showFailure("Load failure", `Could not load package ${packageBase} logs:`, errorThrown);
if (isPackageBaseSet) {
const message = error => { return `Could not load package ${packageBase} logs: ${error}`; };
showFailure("Load failure", message, jqXHR, errorThrown);
}
},
});
}

View File

@ -33,7 +33,9 @@
const packages = dependencyInput.val();
if (packages) {
packageRebuildModal.modal("hide");
doPackageAction("/api/v1/service/rebuild", [packages], "Repository rebuild ran for the following dependencies:", "Repository rebuild failed:");
const onSuccess = update => { return `Repository rebuild has been run for packages which depend on ${update}`; };
const onFailure = error => { return `Repository rebuild failed: ${error}`; };
doPackageAction("/api/v1/service/rebuild", [packages], onSuccess, onFailure);
}
}
</script>

View File

@ -1,34 +0,0 @@
<div id="success-modal" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header bg-success text-white">
<h4 id="success-title" class="modal-title"></h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<p id="success-description"></p>
<ul id="success-details"></ul>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i> close</button>
</div>
</div>
</div>
</div>
<script>
const successModal = $("#success-modal");
successModal.on("hidden.bs.modal", () => { reload(); });
const successDescription = $("#success-description");
const successDetails = $("#success-details");
const successTitle = $("#success-title");
function showSuccess(title, description, details) {
successTitle.text(title);
successDescription.text(description);
successDetails.empty().append(details);
successModal.modal("show");
}
</script>

View File

@ -16,7 +16,6 @@
} else showLogs(data.id);
});
const architectureBadge = $("#badge-architecture");
const repositoryBadge = $("#badge-repository");
const statusBadge = $("#badge-status");
const versionBadge = $("#badge-version");
@ -28,15 +27,11 @@
type: "POST",
contentType: "application/json",
success: _ => {
const details = packages.map(pkg => {
const li = document.createElement("li");
li.innerText = pkg;
return li;
});
showSuccess("Success", successText, details);
const message = successText(packages.join(", "));
showSuccess("Success", message);
},
error: (jqXHR, _, errorThrown) => {
showFailure("Action failed", failureText, errorThrown);
showFailure("Action failed", failureText, jqXHR, errorThrown);
},
});
}
@ -46,13 +41,18 @@
}
function removePackages() {
doPackageAction("/api/v1/service/remove", getSelection(), "The following packages have been removed:", "Packages removal failed:");
const onSuccess = update => { return `Packages ${update} have been removed`; };
const onFailure = error => { return `Could not remove packages: ${error}`; };
doPackageAction("/api/v1/service/remove", getSelection(), onSuccess, onFailure);
}
function updatePackages() {
const currentSelection = getSelection();
const url = currentSelection.length === 0 ? "/api/v1/service/update" : "/api/v1/service/add";
doPackageAction(url, getSelection(), "Packages update has been run", "Packages update failed:");
const [url, onSuccess] = currentSelection.length === 0
? ["/api/v1/service/update", _ => { return "Repository update has been run"; }]
: ["/api/v1/service/add", update => { return `Run update for packages ${update}`; }];
const onFailure = error => { return `Packages update failed: ${error}`; };
doPackageAction(url, currentSelection, onSuccess, onFailure);
}
function hideControls(hidden) {
@ -66,6 +66,14 @@
function reload() {
table.bootstrapTable("showLoading");
const badgeClass = status => {
if (status === "pending") return "btn-outline-warning";
if (status === "building") return "btn-outline-warning";
if (status === "failed") return "btn-outline-danger";
if (status === "success") return "btn-outline-success";
return "btn-outline-secondary";
};
$.ajax({
url: "/api/v1/packages",
type: "GET",
@ -112,7 +120,8 @@
table.bootstrapTable("hideLoading");
} else {
// other errors
showFailure("Load failure", "Could not load list of packages:", errorThrown);
const messaga = error => { return `Could not load list of packages: ${error}`; };
showFailure("Load failure", messaga, jqXHR, errorThrown);
}
hideControls(true);
},
@ -123,39 +132,20 @@
type: "GET",
dataType: "json",
success: response => {
const badgeColor = status => {
if (status === "pending") return "yellow";
if (status === "building") return "yellow";
if (status === "failed") return "critical";
if (status === "success") return "success";
return "inactive";
};
repositoryBadge.text(`${response.repository} ${response.architecture}`);
versionBadge.html(`<i class="bi bi-github"></i> ahriman ${safe(response.version)}`);
architectureBadge
.attr("src", `https://img.shields.io/badge/architecture-${response.architecture}-informational`)
.attr("alt", response.architecture);
repositoryBadge
.attr("src", `https://img.shields.io/badge/repository-${response.repository.replace(/-/g, "--")}-informational`)
.attr("alt", response.repository);
statusBadge
.attr("src", `https://img.shields.io/badge/service%20status-${response.status.status}-${badgeColor(response.status.status)}`)
.attr("alt", response.status.status)
.attr("title", `at ${new Date(1000 * response.status.timestamp).toISOString()}`);
versionBadge
.attr("src", `https://img.shields.io/badge/version-${response.version}-informational`)
.attr("alt", response.version);
.popover("dispose")
.attr("data-bs-content", `${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOString()}`)
.popover();
statusBadge.removeClass();
statusBadge.addClass("btn");
statusBadge.addClass(badgeClass(response.status.status));
},
});
}
function safe(string) {
return String(string)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function statusFormat(value) {
const cellClass = status => {
if (status === "pending") return "table-warning";
@ -169,6 +159,7 @@
$(() => {
table.bootstrapTable({});
statusBadge.popover();
reload();
});
</script>

View File

@ -30,4 +30,12 @@
button.html("<i class=\"bi bi-clipboard\"></i> copy");
}, 2000);
}
function safe(string) {
return String(string)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
</script>

View File

@ -3,7 +3,9 @@
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.21.1/dist/bootstrap-table.min.css" type="text/css">
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" type="text/css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.2.2/dist/cosmo/bootstrap.min.css" integrity="sha256-5t++JZpgVLzo9vF7snO5Qw0y3fA5/NkoJENWB7kpg0E=" crossorigin="anonymous" type="text/css">
<style>
.pre-scrollable {
@ -16,7 +18,7 @@
position: relative;
}
pre[class*="language-"] button{
pre[class*="language-"] button {
position: absolute;
top: 0;
right: 5px;

View File

@ -52,17 +52,20 @@ setup(
"package/bin/ahriman",
],
data_files=[
# configuration
("share/ahriman/settings", [
"package/share/ahriman/settings/ahriman.ini",
]),
("share/ahriman/settings/ahriman.ini.d", [
"package/share/ahriman/settings/ahriman.ini.d/logging.ini",
]),
# systemd files
("lib/systemd/system", [
"package/lib/systemd/system/ahriman@.service",
"package/lib/systemd/system/ahriman@.timer",
"package/lib/systemd/system/ahriman-web@.service",
]),
# templates
("share/ahriman/templates", [
"package/share/ahriman/templates/build-status.jinja2",
"package/share/ahriman/templates/email-index.jinja2",
@ -72,13 +75,12 @@ setup(
"package/share/ahriman/templates/telegram-index.jinja2",
]),
("share/ahriman/templates/build-status", [
"package/share/ahriman/templates/build-status/failed-modal.jinja2",
"package/share/ahriman/templates/build-status/alerts.jinja2",
"package/share/ahriman/templates/build-status/key-import-modal.jinja2",
"package/share/ahriman/templates/build-status/login-modal.jinja2",
"package/share/ahriman/templates/build-status/package-add-modal.jinja2",
"package/share/ahriman/templates/build-status/package-info-modal.jinja2",
"package/share/ahriman/templates/build-status/package-rebuild-modal.jinja2",
"package/share/ahriman/templates/build-status/success-modal.jinja2",
"package/share/ahriman/templates/build-status/table.jinja2",
]),
("share/ahriman/templates/static", [
@ -88,9 +90,17 @@ setup(
"package/share/ahriman/templates/utils/bootstrap-scripts.jinja2",
"package/share/ahriman/templates/utils/style.jinja2",
]),
# man pages
("share/man/man1", [
"docs/ahriman.1",
])
]),
# shell completions
("share/bash-completion/completions", [
"docs/completions/bash/_ahriman",
]),
("share/zsh/site-functions", [
"docs/completions/zsh/_ahriman",
]),
],
extras_require={
@ -104,7 +114,9 @@ setup(
"Sphinx",
"argparse-manpage",
"pydeps",
"shtab",
"sphinx-argparse",
"sphinx-rtd-theme>=1.1.1", # https://stackoverflow.com/a/74355734
"sphinxcontrib-napoleon",
],
# FIXME technically this dependency is required, but in some cases we do not have access to

View File

@ -109,6 +109,7 @@ def _parser() -> argparse.ArgumentParser:
_set_repo_sign_parser(subparsers)
_set_repo_status_update_parser(subparsers)
_set_repo_sync_parser(subparsers)
_set_repo_tree_parser(subparsers)
_set_repo_triggers_parser(subparsers)
_set_repo_update_parser(subparsers)
_set_shell_parser(subparsers)
@ -305,7 +306,8 @@ def _set_package_status_parser(root: SubParserAction) -> argparse.ArgumentParser
parser.add_argument("package", help="filter status by package base", nargs="*")
parser.add_argument("--ahriman", help="get service status itself", action="store_true")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
parser.add_argument("--info", help="show additional package information",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("-s", "--status", help="filter packages by status",
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
parser.set_defaults(handler=handlers.Status, lock=None, report=False, quiet=True, unsafe=True)
@ -703,6 +705,23 @@ def _set_repo_sync_parser(root: SubParserAction) -> argparse.ArgumentParser:
return parser
def _set_repo_tree_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository tree subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-tree", help="dump repository tree",
description="dump repository tree based on packages dependencies",
formatter_class=_formatter)
parser.set_defaults(handler=handlers.Structure, lock=None, report=False, quiet=True)
return parser
def _set_repo_triggers_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for repository triggers subcommand

View File

@ -50,5 +50,5 @@ class ApplicationProperties(LazyLogging):
self.configuration = configuration
self.architecture = architecture
self.database = SQLite.load(configuration)
self.repository = Repository(architecture, configuration, self.database,
report=report, unsafe=unsafe, refresh_pacman_database=refresh_pacman_database)
self.repository = Repository.load(architecture, configuration, self.database, report=report, unsafe=unsafe,
refresh_pacman_database=refresh_pacman_database)

View File

@ -145,8 +145,8 @@ class ApplicationRepository(ApplicationProperties):
process_update(packages, build_result)
# process manual packages
tree = Tree.load(updates, self.repository.paths, self.database)
for num, level in enumerate(tree.levels()):
tree = Tree.resolve(updates, self.repository.paths, self.database)
for num, level in enumerate(tree):
self.logger.info("processing level #%i %s", num, [package.base for package in level])
build_result = self.repository.process_build(level)
packages = self.repository.packages_built()
@ -181,8 +181,12 @@ class ApplicationRepository(ApplicationProperties):
local_versions = {package.base: package.version for package in self.repository.packages()}
updated_packages = [package for _, package in sorted(updates.items())]
for package in updated_packages:
UpdatePrinter(package, local_versions.get(package.base)).print(
verbose=True, log_fn=log_fn, separator=" -> ")
# reorder updates according to the dependency tree
tree = Tree.resolve(updated_packages, self.repository.paths, self.database)
for level in tree:
for package in level:
UpdatePrinter(package, local_versions.get(package.base)).print(
verbose=True, log_fn=log_fn, separator=" -> ")
return updated_packages

View File

@ -37,6 +37,7 @@ from ahriman.application.handlers.shell import Shell
from ahriman.application.handlers.sign import Sign
from ahriman.application.handlers.status import Status
from ahriman.application.handlers.status_update import StatusUpdate
from ahriman.application.handlers.structure import Structure
from ahriman.application.handlers.triggers import Triggers
from ahriman.application.handlers.unsafe_commands import UnsafeCommands
from ahriman.application.handlers.update import Update

View File

@ -71,6 +71,8 @@ class Setup(Handler):
Setup.configuration_create_sudo(application.repository.paths, args.build_command, architecture)
application.repository.repo.init()
# lazy database sync
application.repository.pacman.handle # pylint: disable=pointless-statement
@staticmethod
def build_command(root: Path, prefix: str, architecture: str) -> Path:
@ -78,7 +80,7 @@ class Setup(Handler):
generate build command name
Args:
root(Path): root directory for the build command (must be root of the reporitory)
root(Path): root directory for the build command (must be root of the repository)
prefix(str): command prefix in {prefix}-{architecture}-build
architecture(str): repository architecture

View File

@ -0,0 +1,56 @@
#
# Copyright (c) 2021-2022 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import argparse
from typing import Type
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import TreePrinter
from ahriman.core.tree import Tree
class Structure(Handler):
"""
dump repository structure handler
"""
ALLOW_AUTO_ARCHITECTURE_RUN = False
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
report: bool, unsafe: bool) -> None:
"""
callback for command line
Args:
args(argparse.Namespace): command line args
architecture(str): repository architecture
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
"""
application = Application(architecture, configuration, report=report, unsafe=unsafe)
packages = application.repository.packages()
tree = Tree.resolve(packages, application.repository.paths, application.database)
for num, level in enumerate(tree):
TreePrinter(num, level).print(verbose=True, separator=" ")

View File

@ -17,3 +17,82 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from contextvars import ContextVar
from typing import Any, Dict, Iterator, TypeVar
from ahriman.models.context_key import ContextKey
T = TypeVar("T")
class _Context:
"""
simple ahriman global context implementation
"""
def __init__(self) -> None:
"""
default constructor. Must not be used directly
"""
self._content: Dict[str, Any] = {}
def get(self, key: ContextKey[T]) -> T:
"""
get value for the specified key
Args:
key(ContextKey[T]): context key name
Returns:
T: value associated with the key
Raises:
KeyError: in case if the specified context variable was not found
ValueError: in case if type of value is not an instance of specified return type
"""
if key.key not in self._content:
raise KeyError(key.key)
value = self._content[key.key]
if not isinstance(value, key.return_type):
raise ValueError(f"Value {value} is not an instance of {key.return_type}")
return value
def set(self, key: ContextKey[T], value: T) -> None:
"""
set value for the specified key
Args:
key(ContextKey[T]): context key name
value(T): context value associated with the specified key
Raises:
KeyError: in case if the specified context variable already exists
ValueError: in case if type of value is not an instance of specified return type
"""
if key.key in self._content:
raise KeyError(key.key)
if not isinstance(value, key.return_type):
raise ValueError(f"Value {value} is not an instance of {key.return_type}")
self._content[key.key] = value
def __iter__(self) -> Iterator[str]:
"""
iterate over keys in local storage
Returns:
str: context key iterator
"""
return iter(self._content)
def __len__(self) -> int:
"""
get count of the context variables set
Returns:
int: count of stored context variables
"""
return len(self._content)
context = ContextVar("context", default=_Context())

View File

@ -21,7 +21,7 @@ import shutil
from pathlib import Path
from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore
from typing import Generator, Set
from typing import Any, Callable, Generator, Set
from ahriman.core.configuration import Configuration
from ahriman.core.log import LazyLogging
@ -36,6 +36,8 @@ class Pacman(LazyLogging):
handle(Handle): pyalpm root ``Handle``
"""
handle: Handle
def __init__(self, architecture: str, configuration: Configuration, *, refresh_database: int) -> None:
"""
default constructor
@ -46,6 +48,22 @@ class Pacman(LazyLogging):
refresh_database(int): synchronize local cache to remote. If set to ``0``, no syncronization will be
enabled, if set to ``1`` - normal syncronization, if set to ``2`` - force syncronization
"""
self.__create_handle_fn: Callable[[], Handle] = lambda: self.__create_handle(
architecture, configuration, refresh_database=refresh_database)
def __create_handle(self, architecture: str, configuration: Configuration, *, refresh_database: int) -> Handle:
"""
create lazy handle function
Args:
architecture(str): repository architecture
configuration(Configuration): configuration instance
refresh_database(int): synchronize local cache to remote. If set to ``0``, no syncronization will be
enabled, if set to ``1`` - normal syncronization, if set to ``2`` - force syncronization
Returns:
Handle: fully initialized pacman handle
"""
root = configuration.getpath("alpm", "root")
pacman_root = configuration.getpath("alpm", "database")
use_ahriman_cache = configuration.getboolean("alpm", "use_ahriman_cache")
@ -53,20 +71,42 @@ class Pacman(LazyLogging):
paths = configuration.repository_paths
database_path = paths.pacman if use_ahriman_cache else pacman_root
self.handle = Handle(str(root), str(database_path))
handle = Handle(str(root), str(database_path))
for repository in configuration.getlist("alpm", "repositories"):
database = self.database_init(repository, mirror, architecture)
self.database_copy(database, pacman_root, paths, use_ahriman_cache=use_ahriman_cache)
database = self.database_init(handle, repository, mirror, architecture)
self.database_copy(handle, database, pacman_root, paths, use_ahriman_cache=use_ahriman_cache)
if use_ahriman_cache and refresh_database:
self.database_sync(refresh_database > 1)
self.database_sync(handle, force=refresh_database > 1)
def database_copy(self, database: DB, pacman_root: Path, paths: RepositoryPaths, *,
return handle
def __getattr__(self, item: str) -> Any:
"""
pacman handle extractor
Args:
item(str): property name
Returns:
Any: attribute by its name
Raises:
AttributeError: in case if no such attribute found
"""
if item == "handle":
handle = self.__create_handle_fn()
setattr(self, item, handle)
return handle
return super().__getattr__(item) # required for logging attribute
def database_copy(self, handle: Handle, database: DB, pacman_root: Path, paths: RepositoryPaths, *,
use_ahriman_cache: bool) -> None:
"""
copy database from the operating system root to the ahriman local home
Args:
handle(Handle): pacman handle which will be used for database copying
database(DB): pacman database instance to be copied
pacman_root(Path): operating system pacman root
paths(RepositoryPaths): repository paths instance
@ -78,7 +118,7 @@ class Pacman(LazyLogging):
if not use_ahriman_cache:
return
# copy root database if no local copy found
pacman_db_path = Path(self.handle.dbpath)
pacman_db_path = Path(handle.dbpath)
if not pacman_db_path.is_dir():
return # root directory does not exist yet
dst = repository_database(pacman_db_path)
@ -92,11 +132,12 @@ class Pacman(LazyLogging):
shutil.copy(src, dst)
paths.chown(dst)
def database_init(self, repository: str, mirror: str, architecture: str) -> DB:
def database_init(self, handle: Handle, repository: str, mirror: str, architecture: str) -> DB:
"""
create database instance from pacman handler and set its properties
Args:
handle(Handle): pacman handle which will be used for database initializing
repository(str): pacman repository name (e.g. core)
mirror(str): arch linux mirror url
architecture(str): repository architecture
@ -104,21 +145,23 @@ class Pacman(LazyLogging):
Returns:
DB: loaded pacman database instance
"""
database: DB = self.handle.register_syncdb(repository, SIG_PACKAGE)
self.logger.info("loading pacman databases")
database: DB = handle.register_syncdb(repository, SIG_PACKAGE)
# replace variables in mirror address
database.servers = [mirror.replace("$repo", repository).replace("$arch", architecture)]
return database
def database_sync(self, force: bool) -> None:
def database_sync(self, handle: Handle, *, force: bool) -> None:
"""
sync local database
Args:
handle(Handle): pacman handle which will be used for database sync
force(bool): force database syncronization (same as ``pacman -Syy``)
"""
self.logger.info("refresh ahriman's home pacman database (force refresh %s)", force)
transaction = self.handle.init_transaction()
for database in self.handle.get_syncdbs():
transaction = handle.init_transaction()
for database in handle.get_syncdbs():
try:
database.update(force)
except PyalpmError:

View File

@ -72,7 +72,7 @@ class Remote(LazyLogging):
"""
instance = cls()
packages: Dict[str, AURPackage] = {}
for term in filter(lambda word: len(word) > 3, keywords):
for term in filter(lambda word: len(word) >= 3, keywords):
portion = instance.search(term, pacman=pacman)
packages = {
package.name: package # not mistake to group them by name

View File

@ -17,14 +17,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import datetime
import shutil
from pathlib import Path
from typing import List, Optional
from ahriman.core.log import LazyLogging
from ahriman.core.util import check_output, walk
from ahriman.core.util import check_output, utcnow, walk
from ahriman.models.package import Package
from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.remote_source import RemoteSource
@ -215,7 +214,7 @@ class Sources(LazyLogging):
author(Optional[str], optional): optional commit author if any (Default value = None)
"""
if message is None:
message = f"Autogenerated commit at {datetime.datetime.utcnow()}"
message = f"Autogenerated commit at {utcnow()}"
args = ["--allow-empty", "--message", message]
if author is not None:
args.extend(["--author", author])

View File

@ -26,6 +26,7 @@ from ahriman.core.formatters.configuration_printer import ConfigurationPrinter
from ahriman.core.formatters.package_printer import PackagePrinter
from ahriman.core.formatters.patch_printer import PatchPrinter
from ahriman.core.formatters.status_printer import StatusPrinter
from ahriman.core.formatters.tree_printer import TreePrinter
from ahriman.core.formatters.update_printer import UpdatePrinter
from ahriman.core.formatters.user_printer import UserPrinter
from ahriman.core.formatters.version_printer import VersionPrinter

View File

@ -0,0 +1,53 @@
#
# Copyright (c) 2021-2022 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from typing import Iterable, List
from ahriman.core.formatters import StringPrinter
from ahriman.models.package import Package
from ahriman.models.property import Property
class TreePrinter(StringPrinter):
"""
print content of the package tree level
Attributes:
packages(Iterable[Package]): packages which belong to this level
"""
def __init__(self, level: int, packages: Iterable[Package]) -> None:
"""
default constructor
Args:
level(int): dependencies tree level
packages(Iterable[Package]): packages which belong to this level
"""
StringPrinter.__init__(self, f"level {level}")
self.packages = packages
def properties(self) -> List[Property]:
"""
convert content into printable data
Returns:
List[Property]: list of content properties
"""
return [Property(package.base, package.version, is_required=True) for package in self.packages]

View File

@ -25,6 +25,9 @@ from ahriman.core.triggers import Trigger
class RemotePullTrigger(Trigger):
"""
trigger based on pulling PKGBUILDs before the actions
Attributes:
targets(List[str]): git remote target list
"""
def __init__(self, architecture: str, configuration: Configuration) -> None:

View File

@ -25,6 +25,7 @@ from typing import Generator
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import GitRemoteError
from ahriman.core.log import LazyLogging
from ahriman.models.package import Package
@ -39,17 +40,20 @@ class RemotePush(LazyLogging):
Attributes:
commit_author(Optional[str]): optional commit author in form of git config (i.e. ``user <user@host>``)
database(SQLite): database instance
remote_source(RemoteSource): repository remote source (remote pull url and branch)
"""
def __init__(self, configuration: Configuration, section: str) -> None:
def __init__(self, configuration: Configuration, database: SQLite, section: str) -> None:
"""
default constructor
Args:
configuration(Configuration): configuration instance
remote_push_trigger.py
database(SQLite): database instance
section(str): settings section name
"""
self.database = database
self.commit_author = configuration.get(section, "commit_author", fallback=None)
self.remote_source = RemoteSource(
git_url=configuration.get(section, "push_url"),
@ -59,8 +63,7 @@ class RemotePush(LazyLogging):
source=PackageSource.Local,
)
@staticmethod
def package_update(package: Package, target_dir: Path) -> str:
def package_update(self, package: Package, target_dir: Path) -> str:
"""
clone specified package and update its content in cloned PKGBUILD repository
@ -79,11 +82,14 @@ class RemotePush(LazyLogging):
Sources.fetch(package_target_dir, package.remote)
# ...and last, but not least, we remove the dot-git directory...
shutil.rmtree(package_target_dir / ".git", ignore_errors=True)
# ...copy all patches...
for patch in self.database.patches_get(package.base):
filename = f"ahriman-{package.base}.patch" if patch.key is None else f"ahriman-{patch.key}.patch"
patch.write(package_target_dir / filename)
# ...and finally return path to the copied directory
return package.base
@staticmethod
def packages_update(result: Result, target_dir: Path) -> Generator[str, None, None]:
def packages_update(self, result: Result, target_dir: Path) -> Generator[str, None, None]:
"""
update all packages from the build result
@ -95,7 +101,7 @@ class RemotePush(LazyLogging):
str: path to updated files
"""
for package in result.success:
yield RemotePush.package_update(package, target_dir)
yield self.package_update(package, target_dir)
def run(self, result: Result) -> None:
"""
@ -107,7 +113,7 @@ class RemotePush(LazyLogging):
try:
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name, (clone_dir := Path(dir_name)):
Sources.fetch(clone_dir, self.remote_source)
Sources.push(clone_dir, self.remote_source, *RemotePush.packages_update(result, clone_dir),
Sources.push(clone_dir, self.remote_source, *self.packages_update(result, clone_dir),
commit_author=self.commit_author)
except Exception:
self.logger.exception("git push failed")

View File

@ -19,9 +19,12 @@
#
from typing import Iterable
from ahriman.core import context
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.gitremote.remote_push import RemotePush
from ahriman.core.triggers import Trigger
from ahriman.models.context_key import ContextKey
from ahriman.models.package import Package
from ahriman.models.result import Result
@ -29,6 +32,9 @@ from ahriman.models.result import Result
class RemotePushTrigger(Trigger):
"""
trigger for syncing PKGBUILDs to remote repository
Attributes:
targets(List[str]): git remote target list
"""
def __init__(self, architecture: str, configuration: Configuration) -> None:
@ -49,8 +55,14 @@ class RemotePushTrigger(Trigger):
Args:
result(Result): build result
packages(Iterable[Package]): list of all available packages
Raises:
GitRemoteError: if database is not set in context
"""
ctx = context.get()
database = ctx.get(ContextKey("database", SQLite))
for target in self.targets:
section, _ = self.configuration.gettype(target, self.architecture)
runner = RemotePush(self.configuration, section)
runner = RemotePush(self.configuration, database, section)
runner.run(result)

View File

@ -38,7 +38,7 @@ class LazyLogging:
logger extractor
Args:
item(str) property name:
item(str): property name
Returns:
Any: attribute by its name

View File

@ -17,7 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import datetime
import smtplib
from email.mime.multipart import MIMEMultipart
@ -27,7 +26,7 @@ from typing import Dict, Iterable
from ahriman.core.configuration import Configuration
from ahriman.core.report.jinja_template import JinjaTemplate
from ahriman.core.report.report import Report
from ahriman.core.util import pretty_datetime
from ahriman.core.util import pretty_datetime, utcnow
from ahriman.models.package import Package
from ahriman.models.result import Result
from ahriman.models.smtp_ssl_settings import SmtpSSLSettings
@ -86,7 +85,7 @@ class Email(Report, JinjaTemplate):
message = MIMEMultipart()
message["From"] = self.sender
message["To"] = ", ".join(self.receivers)
message["Subject"] = f"{self.name} build report at {pretty_datetime(datetime.datetime.utcnow())}"
message["Subject"] = f"{self.name} build report at {pretty_datetime(utcnow())}"
message.attach(MIMEText(text, "html"))
for filename, content in attachment.items():

View File

@ -17,12 +17,20 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from pathlib import Path
from typing import Dict, Iterable, List, Optional
from __future__ import annotations
from pathlib import Path
from typing import Dict, Iterable, List, Optional, Type
from ahriman.core import context
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository.executor import Executor
from ahriman.core.repository.update_handler import UpdateHandler
from ahriman.core.sign.gpg import GPG
from ahriman.core.util import package_like
from ahriman.models.context_key import ContextKey
from ahriman.models.package import Package
@ -39,7 +47,7 @@ class Repository(Executor, UpdateHandler):
>>>
>>> configuration = Configuration()
>>> database = SQLite.load(configuration)
>>> repository = Repository("x86_64", configuration, database, report=True, unsafe=False)
>>> repository = Repository.load("x86_64", configuration, database, report=True, unsafe=False)
>>> known_packages = repository.packages()
>>>
>>> build_result = repository.process_build(known_packages)
@ -49,6 +57,41 @@ class Repository(Executor, UpdateHandler):
>>> repository.triggers.on_result(update_result, repository.packages())
"""
@classmethod
def load(cls: Type[Repository], architecture: str, configuration: Configuration, database: SQLite, *,
report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> Repository:
"""
load instance from argument list
Args:
architecture(str): repository architecture
configuration(Configuration): configuration instance
database(SQLite): database instance
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
refresh_pacman_database(int, optional): pacman database syncronization level, ``0`` is disabled
(Default value = 0)
"""
instance = cls(architecture, configuration, database,
report=report, unsafe=unsafe, refresh_pacman_database=refresh_pacman_database)
instance._set_context()
return instance
def _set_context(self) -> None:
"""
set context variables
"""
ctx = context.get()
ctx.set(ContextKey("database", SQLite), self.database)
ctx.set(ContextKey("configuration", Configuration), self.configuration)
ctx.set(ContextKey("pacman", Pacman), self.pacman)
ctx.set(ContextKey("sign", GPG), self.sign)
ctx.set(ContextKey("repository", type(self)), self)
context.set(ctx)
def load_archives(self, packages: Iterable[Path]) -> List[Package]:
"""
load packages from list of archives

View File

@ -45,10 +45,11 @@ class RepositoryProperties(LazyLogging):
reporter(Client): build status reporter instance
sign(GPG): GPG wrapper instance
triggers(TriggerLoader): triggers holder
vcs_allowed_age(int): maximal age of the VCS packages before they will be checked
"""
def __init__(self, architecture: str, configuration: Configuration, database: SQLite, *,
report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> None:
report: bool, unsafe: bool, refresh_pacman_database: int) -> None:
"""
default constructor
@ -59,13 +60,13 @@ class RepositoryProperties(LazyLogging):
report(bool): force enable or disable reporting
unsafe(bool): if set no user check will be performed before path creation
refresh_pacman_database(int, optional): pacman database syncronization level, ``0`` is disabled
(Default value = 0)
"""
self.architecture = architecture
self.configuration = configuration
self.database = database
self.name = configuration.get("repository", "name")
self.vcs_allowed_age = configuration.getint("build", "vcs_allowed_age", fallback=0)
self.paths = configuration.repository_paths
try:

View File

@ -21,6 +21,7 @@ from typing import Iterable, List
from ahriman.core.build_tools.sources import Sources
from ahriman.core.repository.cleaner import Cleaner
from ahriman.core.util import utcnow
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
@ -53,14 +54,16 @@ class UpdateHandler(Cleaner):
Returns:
List[Package]: list of packages which are out-of-dated
"""
# don't think there are packages older then 1970
now = utcnow()
min_vcs_build_date = (now.timestamp() - self.vcs_allowed_age) if vcs else now.timestamp()
result: List[Package] = []
for local in self.packages():
with self.in_package_context(local.base):
if local.base in self.ignore_list:
continue
if local.is_vcs and not vcs:
continue
if filter_packages and local.base not in filter_packages:
continue
source = local.remote.source if local.remote is not None else None
@ -70,7 +73,9 @@ class UpdateHandler(Cleaner):
remote = Package.from_official(local.base, self.pacman)
else:
remote = Package.from_aur(local.base, self.pacman)
if local.is_outdated(remote, self.paths):
calculate_version = not local.is_newer_than(min_vcs_build_date)
if local.is_outdated(remote, self.paths, calculate_version=calculate_version):
self.reporter.set_pending(local.base)
result.append(remote)
except Exception:
@ -99,7 +104,7 @@ class UpdateHandler(Cleaner):
if local is None:
self.reporter.set_unknown(remote)
result.append(remote)
elif local.is_outdated(remote, self.paths):
elif local.is_outdated(remote, self.paths, calculate_version=True):
self.reporter.set_pending(local.base)
result.append(remote)
except Exception:

View File

@ -55,7 +55,7 @@ class Watcher(LazyLogging):
"""
self.architecture = architecture
self.database = database
self.repository = Repository(architecture, configuration, database, report=False, unsafe=False)
self.repository = Repository.load(architecture, configuration, database, report=False, unsafe=False)
self.known: Dict[str, Tuple[Package, BuildStatus]] = {}
self.status = BuildStatus()

View File

@ -19,9 +19,11 @@
#
from __future__ import annotations
import itertools
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Iterable, List, Set, Type
from typing import Callable, Iterable, List, Set, Tuple, Type
from ahriman.core.build_tools.sources import Sources
from ahriman.core.database import SQLite
@ -77,6 +79,21 @@ class Leaf:
dependencies = Package.dependencies(clone_dir)
return cls(package, dependencies)
def is_dependency(self, packages: Iterable[Leaf]) -> bool:
"""
check if the package is dependency of any other package from list or not
Args:
packages(Iterable[Leaf]): list of known leaves
Returns:
bool: True in case if package is dependency of others and False otherwise
"""
for leaf in packages:
if leaf.dependencies.intersection(self.items):
return True
return False
def is_root(self, packages: Iterable[Leaf]) -> bool:
"""
check if package depends on any other package from list of not
@ -110,11 +127,11 @@ class Tree:
>>>
>>> configuration = Configuration()
>>> database = SQLite.load(configuration)
>>> repository = Repository("x86_64", configuration, database, report=True, unsafe=False)
>>> repository = Repository.load("x86_64", configuration, database, report=True, unsafe=False)
>>> packages = repository.packages()
>>>
>>> tree = Tree.load(packages, configuration.repository_paths, database)
>>> for tree_level in tree.levels():
>>> tree = Tree.resolve(packages, configuration.repository_paths, database)
>>> for tree_level in tree:
>>> for package in tree_level:
>>> print(package.base)
>>> print()
@ -141,9 +158,10 @@ class Tree:
self.leaves = leaves
@classmethod
def load(cls: Type[Tree], packages: Iterable[Package], paths: RepositoryPaths, database: SQLite) -> Tree:
def resolve(cls: Type[Tree], packages: Iterable[Package], paths: RepositoryPaths,
database: SQLite) -> List[List[Package]]:
"""
load tree from packages
resolve dependency tree
Args:
packages(Iterable[Package]): packages list
@ -151,22 +169,45 @@ class Tree:
database(SQLite): database instance
Returns:
Tree: loaded class
List[List[Package]]: list of packages lists based on their dependencies
"""
return cls([Leaf.load(package, paths, database) for package in packages])
leaves = [Leaf.load(package, paths, database) for package in packages]
tree = cls(leaves)
return tree.levels()
def levels(self) -> List[List[Package]]:
"""
get build levels starting from the packages which do not require any other package to build
Returns:
List[List[Package]]: list of packages lists
List[List[Package]]: sorted list of packages lists based on their dependencies
"""
result: List[List[Package]] = []
# https://docs.python.org/dev/library/itertools.html#itertools-recipes
def partition(source: List[Leaf]) -> Tuple[List[Leaf], Iterable[Leaf]]:
first_iter, second_iter = itertools.tee(source)
filter_fn: Callable[[Leaf], bool] = lambda leaf: leaf.is_dependency(next_level)
# materialize first list and leave second as iterator
return list(filter(filter_fn, first_iter)), itertools.filterfalse(filter_fn, second_iter)
unsorted: List[List[Leaf]] = []
# build initial tree
unprocessed = self.leaves[:]
while unprocessed:
result.append([leaf.package for leaf in unprocessed if leaf.is_root(unprocessed)])
unsorted.append([leaf for leaf in unprocessed if leaf.is_root(unprocessed)])
unprocessed = [leaf for leaf in unprocessed if not leaf.is_root(unprocessed)]
return result
# move leaves to the end if they are not required at the next level
for current_num, current_level in enumerate(unsorted[:-1]):
next_num = current_num + 1
next_level = unsorted[next_num]
# change lists inside the collection
unsorted[current_num], to_be_moved = partition(current_level)
unsorted[next_num].extend(to_be_moved)
comparator: Callable[[Package], str] = lambda package: package.base
return [
sorted([leaf.package for leaf in level], key=comparator)
for level in unsorted if level
]

View File

@ -69,11 +69,11 @@ class TriggerLoader(LazyLogging):
self.architecture = architecture
self.configuration = configuration
self._on_stop_requested = False
self.triggers = [
self.load_trigger(trigger)
for trigger in configuration.getlist("build", "triggers")
]
self._on_stop_requested = False
def __del__(self) -> None:
"""

View File

@ -34,8 +34,8 @@ from ahriman.core.exceptions import OptionError, UnsafeRunError
from ahriman.models.repository_paths import RepositoryPaths
__all__ = ["check_output", "check_user", "exception_response_text", "filter_json", "full_version", "enum_values",
"package_like", "pretty_datetime", "pretty_size", "safe_filename", "walk"]
__all__ = ["check_output", "check_user", "enum_values", "exception_response_text", "filter_json", "full_version",
"package_like", "pretty_datetime", "pretty_size", "safe_filename", "utcnow", "walk"]
def check_output(*args: str, exception: Optional[Exception] = None, cwd: Optional[Path] = None,
@ -295,6 +295,16 @@ def safe_filename(source: str) -> str:
return re.sub(r"[^A-Za-z\d\-._~:\[\]@]", "-", source)
def utcnow() -> datetime.datetime:
"""
get current time
Returns:
datetime.datetime: current time in UTC
"""
return datetime.datetime.utcnow()
def walk(directory_path: Path) -> Generator[Path, None, None]:
"""
list all file paths in given directory

View File

@ -19,13 +19,11 @@
#
from __future__ import annotations
import datetime
from dataclasses import dataclass, field, fields
from enum import Enum
from typing import Any, Dict, Type
from ahriman.core.util import filter_json, pretty_datetime
from ahriman.core.util import filter_json, pretty_datetime, utcnow
class BuildStatusEnum(str, Enum):
@ -58,7 +56,7 @@ class BuildStatus:
"""
status: BuildStatusEnum = BuildStatusEnum.Unknown
timestamp: int = field(default_factory=lambda: int(datetime.datetime.utcnow().timestamp()))
timestamp: int = field(default_factory=lambda: int(utcnow().timestamp()))
def __post_init__(self) -> None:
"""

View File

@ -0,0 +1,37 @@
#
# Copyright (c) 2021-2022 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from dataclasses import dataclass
from typing import Generic, Type, TypeVar
T = TypeVar("T")
@dataclass(frozen=True)
class ContextKey(Generic[T]):
"""
ahriman context key for typing purposes
Attributes:
key(str): context key to lookup
return_type(Type[T]): return type used for the specified context key
"""
key: str
return_type: Type[T]

View File

@ -17,6 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# pylint: disable=too-many-lines
from __future__ import annotations
import copy
@ -25,7 +26,7 @@ from dataclasses import asdict, dataclass
from pathlib import Path
from pyalpm import vercmp # type: ignore
from srcinfo.parse import parse_srcinfo # type: ignore
from typing import Any, Dict, Iterable, List, Optional, Set, Type
from typing import Any, Dict, Iterable, List, Optional, Set, Type, Union
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import AUR, Official, OfficialSyncdb
@ -358,7 +359,24 @@ class Package(LazyLogging):
return sorted(result)
def is_outdated(self, remote: Package, paths: RepositoryPaths, *, calculate_version: bool = True) -> bool:
def is_newer_than(self, timestamp: Union[float, int]) -> bool:
"""
check if package was built after the specified timestamp
Args:
timestamp(int): timestamp to check build date against
Returns:
bool: True in case if package was built after the specified date and False otherwise. In case if build date
is not set by any of packages, it returns False
"""
return any(
package.build_date > timestamp
for package in self.packages.values()
if package.build_date is not None
)
def is_outdated(self, remote: Package, paths: RepositoryPaths, *, calculate_version: bool) -> bool:
"""
check if package is out-of-dated

View File

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

View File

@ -7,28 +7,31 @@ from ahriman.application.application.application_properties import ApplicationPr
from ahriman.application.application.application_repository import ApplicationRepository
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
@pytest.fixture
def application_packages(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> ApplicationPackages:
def application_packages(configuration: Configuration, database: SQLite, repository: Repository,
mocker: MockerFixture) -> ApplicationPackages:
"""
fixture for application with package functions
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
ApplicationPackages: application test instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
return ApplicationPackages("x86_64", configuration, report=False, unsafe=False)
@pytest.fixture
def application_properties(configuration: Configuration, database: SQLite,
def application_properties(configuration: Configuration, database: SQLite, repository: Repository,
mocker: MockerFixture) -> ApplicationProperties:
"""
fixture for application with properties only
@ -36,18 +39,19 @@ def application_properties(configuration: Configuration, database: SQLite,
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
ApplicationProperties: application test instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
return ApplicationProperties("x86_64", configuration, report=False, unsafe=False)
@pytest.fixture
def application_repository(configuration: Configuration, database: SQLite,
def application_repository(configuration: Configuration, database: SQLite, repository: Repository,
mocker: MockerFixture) -> ApplicationRepository:
"""
fixture for application with repository functions
@ -55,11 +59,12 @@ def application_repository(configuration: Configuration, database: SQLite,
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
ApplicationRepository: application test instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
return ApplicationRepository("x86_64", configuration, report=False, unsafe=False)

View File

@ -163,7 +163,7 @@ def test_update(application_repository: ApplicationRepository, package_ahriman:
paths = [package.filepath for package in package_ahriman.packages.values()]
tree = Tree([Leaf(package_ahriman, set())])
mocker.patch("ahriman.core.tree.Tree.load", return_value=tree)
mocker.patch("ahriman.core.tree.Tree.resolve", return_value=tree.levels())
mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=paths)
build_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_build", return_value=result)
update_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_update", return_value=result)
@ -183,7 +183,7 @@ def test_update_empty(application_repository: ApplicationRepository, package_ahr
"""
tree = Tree([Leaf(package_ahriman, set())])
mocker.patch("ahriman.core.tree.Tree.load", return_value=tree)
mocker.patch("ahriman.core.tree.Tree.resolve", return_value=tree.levels())
mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=[])
mocker.patch("ahriman.core.repository.executor.Executor.process_build")
update_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_update")
@ -197,6 +197,9 @@ def test_updates_all(application_repository: ApplicationRepository, package_ahri
"""
must get updates for all
"""
tree = Tree([Leaf(package_ahriman, set())])
mocker.patch("ahriman.core.tree.Tree.resolve", return_value=tree.levels())
mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[])
updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur",
return_value=[package_ahriman])

View File

@ -8,16 +8,19 @@ from ahriman.application.application import Application
from ahriman.application.lock import Lock
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
@pytest.fixture
def application(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> Application:
def application(configuration: Configuration, repository: Repository, database: SQLite,
mocker: MockerFixture) -> Application:
"""
fixture for application
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
@ -25,6 +28,7 @@ def application(configuration: Configuration, database: SQLite, mocker: MockerFi
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
return Application("x86_64", configuration, report=False, unsafe=False)

View File

@ -5,6 +5,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Add
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.result import Result
@ -29,12 +30,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.add")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
@ -43,7 +45,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
on_start_mock.assert_called_once_with()
def test_run_with_updates(args: argparse.Namespace, configuration: Configuration,
def test_run_with_updates(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command with updates after
@ -53,7 +55,7 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration
result = Result()
result.add_success(package_ahriman)
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
@ -65,7 +67,8 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration
check_mock.assert_called_once_with(False, False)
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty result
"""
@ -73,7 +76,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
args.now = True
args.exit_code = True
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.update", return_value=Result())
mocker.patch("ahriman.application.application.Application.updates")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")

View File

@ -4,6 +4,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Clean
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@ -24,12 +25,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.clean")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")

View File

@ -4,13 +4,15 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Dump
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
application_mock = mocker.patch("ahriman.core.configuration.Configuration.dump",
return_value=configuration.dump())

View File

@ -4,6 +4,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import KeyImport
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@ -21,12 +22,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.sign.gpg.GPG.key_import")
KeyImport.run(args, "x86_64", configuration, report=False, unsafe=False)

View File

@ -8,6 +8,7 @@ from pytest_mock import MockerFixture
from ahriman.application.application import Application
from ahriman.application.handlers import Patch
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.action import Action
from ahriman.models.package import Package
from ahriman.models.pkgbuild_patch import PkgbuildPatch
@ -31,13 +32,14 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
args.action = Action.Update
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
patch_mock = mocker.patch("ahriman.application.handlers.Patch.patch_create_from_diff",
return_value=(args.package, PkgbuildPatch(None, "patch")))
application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_create")
@ -47,7 +49,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, PkgbuildPatch(None, "patch"))
def test_run_function(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_function(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with patch function flag
"""
@ -56,7 +59,7 @@ def test_run_function(args: argparse.Namespace, configuration: Configuration, mo
args.patch = "patch"
args.variable = "version"
patch = PkgbuildPatch(args.variable, args.patch)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
patch_mock = mocker.patch("ahriman.application.handlers.Patch.patch_create_from_function", return_value=patch)
application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_create")
@ -65,28 +68,30 @@ def test_run_function(args: argparse.Namespace, configuration: Configuration, mo
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, patch)
def test_run_list(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_list(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with list flag
"""
args = _default_args(args)
args.action = Action.List
args.variable = ["version"]
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_list")
Patch.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.package, ["version"], False)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with remove flag
"""
args = _default_args(args)
args.action = Action.Remove
args.variable = ["version"]
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_remove")
Patch.run(args, "x86_64", configuration, report=False, unsafe=False)

View File

@ -7,6 +7,7 @@ from unittest.mock import call as MockCall
from ahriman.application.application import Application
from ahriman.application.handlers import Rebuild
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
from ahriman.models.result import Result
@ -28,15 +29,15 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, package_ahriman: Package,
configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
repository: Repository, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
result = Result()
result.add_success(package_ahriman)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
extract_mock = mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[package_ahriman])
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on",
return_value=[package_ahriman])
@ -52,14 +53,15 @@ def test_run(args: argparse.Namespace, package_ahriman: Package,
on_start_mock.assert_called_once_with()
def test_run_extract_packages(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_extract_packages(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
args.from_database = True
args.dry_run = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.add")
extract_mock = mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
@ -67,14 +69,14 @@ def test_run_extract_packages(args: argparse.Namespace, configuration: Configura
extract_mock.assert_called_once_with(pytest.helpers.anyvar(int), from_database=args.from_database)
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration,
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command without update itself
"""
args = _default_args(args)
args.dry_run = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[package_ahriman])
application_mock = mocker.patch("ahriman.application.application.Application.update")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
@ -84,14 +86,15 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration,
check_mock.assert_called_once_with(False, False)
def test_run_filter(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_filter(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with depends on filter
"""
args = _default_args(args)
args.depends_on = ["python-aur"]
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
@ -99,13 +102,14 @@ def test_run_filter(args: argparse.Namespace, configuration: Configuration, mock
application_packages_mock.assert_called_once_with([], {"python-aur"})
def test_run_without_filter(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_without_filter(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command for all packages if no filter supplied
"""
args = _default_args(args)
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
@ -113,7 +117,7 @@ def test_run_without_filter(args: argparse.Namespace, configuration: Configurati
application_packages_mock.assert_called_once_with([], None)
def test_run_update_empty_exception(args: argparse.Namespace, configuration: Configuration,
def test_run_update_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty update list
@ -121,7 +125,7 @@ def test_run_update_empty_exception(args: argparse.Namespace, configuration: Con
args = _default_args(args)
args.exit_code = True
args.dry_run = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages")
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
@ -130,14 +134,14 @@ def test_run_update_empty_exception(args: argparse.Namespace, configuration: Con
check_mock.assert_called_once_with(True, True)
def test_run_build_empty_exception(args: argparse.Namespace, configuration: Configuration,
def test_run_build_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty update result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages")
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.Application.update", return_value=Result())

View File

@ -4,6 +4,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Remove
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@ -20,12 +21,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.remove")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")

View File

@ -4,6 +4,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import RemoveUnknown
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
@ -21,13 +22,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, package_ahriman: Package,
configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
repository: Repository, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.unknown",
return_value=[package_ahriman])
remove_mock = mocker.patch("ahriman.application.application.Application.remove")
@ -39,14 +40,14 @@ def test_run(args: argparse.Namespace, package_ahriman: Package,
on_start_mock.assert_called_once_with()
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run simplified command
"""
args = _default_args(args)
args.dry_run = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.unknown",
return_value=[package_ahriman])
remove_mock = mocker.patch("ahriman.application.application.Application.remove")

View File

@ -8,6 +8,7 @@ from unittest.mock import call as MockCall
from ahriman.application.handlers import Search
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import OptionError
from ahriman.core.repository import Repository
from ahriman.models.aur_package import AURPackage
@ -28,13 +29,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
aur_search_mock = mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman])
official_search_mock = mocker.patch("ahriman.core.alpm.remote.Official.multisearch",
return_value=[aur_package_ahriman])
@ -48,7 +49,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, aur_package
print_mock.assert_has_calls([MockCall(False), MockCall(False)])
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty result list
"""
@ -57,22 +59,22 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[])
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.core.formatters.Printer.print")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
Search.run(args, "x86_64", configuration, report=False, unsafe=False)
check_mock.assert_called_once_with(True, True)
def test_run_sort(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
mocker: MockerFixture) -> None:
def test_run_sort(args: argparse.Namespace, configuration: Configuration, repository: Repository,
aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must run command with sorting
"""
args = _default_args(args)
mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
sort_mock = mocker.patch("ahriman.application.handlers.Search.sort")
Search.run(args, "x86_64", configuration, report=False, unsafe=False)
@ -82,8 +84,8 @@ def test_run_sort(args: argparse.Namespace, configuration: Configuration, aur_pa
])
def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
mocker: MockerFixture) -> None:
def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, repository: Repository,
aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must run command with sorting by specified field
"""
@ -91,7 +93,7 @@ def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, aur
args.sort_by = "field"
mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
sort_mock = mocker.patch("ahriman.application.handlers.Search.sort")
Search.run(args, "x86_64", configuration, report=False, unsafe=False)

View File

@ -8,6 +8,7 @@ from unittest.mock import call as MockCall
from ahriman.application.handlers import Setup
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.repository_paths import RepositoryPaths
from ahriman.models.sign_settings import SignSettings
@ -36,13 +37,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, repository_paths: RepositoryPaths,
mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
ahriman_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_ahriman")
devtools_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_devtools")
makepkg_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_makepkg")

View File

@ -5,6 +5,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Shell
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@ -22,38 +23,41 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("code.interact")
Shell.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with(local=pytest.helpers.anyvar(int))
def test_run_eval(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_eval(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
args.code = """print("hello world")"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("code.InteractiveConsole.runcode")
Shell.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with(args.code)
def test_run_verbose(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_verbose(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command with verbose option
"""
args = _default_args(args)
args.verbose = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
application_mock = mocker.patch("code.interact")

View File

@ -4,6 +4,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Sign
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@ -20,12 +21,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.sign")
Sign.run(args, "x86_64", configuration, report=False, unsafe=False)

View File

@ -5,6 +5,8 @@ from unittest.mock import call as MockCall
from ahriman.application.handlers import Status
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
@ -27,13 +29,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package,
package_python_schedule: Package, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, package_python_schedule: Package, mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.status.client.Client.get_internal")
packages_mock = mocker.patch("ahriman.core.status.client.Client.get",
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
@ -48,13 +50,14 @@ def test_run(args: argparse.Namespace, configuration: Configuration, package_ahr
print_mock.assert_has_calls([MockCall(False) for _ in range(3)])
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty status result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.client.Client.get_internal")
mocker.patch("ahriman.core.status.client.Client.get", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
@ -63,14 +66,14 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
check_mock.assert_called_once_with(True, True)
def test_run_verbose(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
def test_run_verbose(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command with detailed info
"""
args = _default_args(args)
args.info = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.client.Client.get",
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
@ -79,14 +82,14 @@ def test_run_verbose(args: argparse.Namespace, configuration: Configuration, pac
print_mock.assert_has_calls([MockCall(True) for _ in range(2)])
def test_run_with_package_filter(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
def test_run_with_package_filter(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command with package filter
"""
args = _default_args(args)
args.package = [package_ahriman.base]
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
packages_mock = mocker.patch("ahriman.core.status.client.Client.get",
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
@ -94,8 +97,8 @@ def test_run_with_package_filter(args: argparse.Namespace, configuration: Config
packages_mock.assert_called_once_with(package_ahriman.base)
def test_run_by_status(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package,
package_python_schedule: Package, mocker: MockerFixture) -> None:
def test_run_by_status(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, package_python_schedule: Package, mocker: MockerFixture) -> None:
"""
must filter packages by status
"""
@ -104,23 +107,25 @@ def test_run_by_status(args: argparse.Namespace, configuration: Configuration, p
mocker.patch("ahriman.core.status.client.Client.get",
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
(package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
Status.run(args, "x86_64", configuration, report=False, unsafe=False)
print_mock.assert_has_calls([MockCall(False) for _ in range(2)])
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must create application object with native reporting
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
Status.run(args, "x86_64", configuration, report=False, unsafe=False)
load_mock.assert_called_once_with(configuration, report=True)
load_mock.assert_called_once_with("x86_64", configuration, database,
report=True, unsafe=False, refresh_pacman_database=0)
def test_disallow_auto_architecture_run() -> None:

View File

@ -4,6 +4,8 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import StatusUpdate
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.models.action import Action
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
@ -25,57 +27,60 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
update_self_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
StatusUpdate.run(args, "x86_64", configuration, report=False, unsafe=False)
update_self_mock.assert_called_once_with(args.status)
def test_run_packages(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
def test_run_packages(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command with specified packages
"""
args = _default_args(args)
args.package = [package_ahriman.base]
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
update_mock = mocker.patch("ahriman.core.status.client.Client.update")
StatusUpdate.run(args, "x86_64", configuration, report=False, unsafe=False)
update_mock.assert_called_once_with(package_ahriman.base, args.status)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must remove package from status page
"""
args = _default_args(args)
args.package = [package_ahriman.base]
args.action = Action.Remove
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
update_mock = mocker.patch("ahriman.core.status.client.Client.remove")
StatusUpdate.run(args, "x86_64", configuration, report=False, unsafe=False)
update_mock.assert_called_once_with(package_ahriman.base)
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, database: SQLite,
mocker: MockerFixture) -> None:
"""
must create application object with native reporting
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
load_mock = mocker.patch("ahriman.core.status.client.Client.load")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
StatusUpdate.run(args, "x86_64", configuration, report=False, unsafe=False)
load_mock.assert_called_once_with(configuration, report=True)
load_mock.assert_called_once_with("x86_64", configuration, database,
report=True, unsafe=False, refresh_pacman_database=0)
def test_disallow_auto_architecture_run() -> None:

View File

@ -0,0 +1,31 @@
import argparse
import pytest
from pytest_mock import MockerFixture
from ahriman.application.handlers import Structure
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command
"""
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
application_mock = mocker.patch("ahriman.core.tree.Tree.resolve", return_value=[[package_ahriman]])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
Structure.run(args, "x86_64", configuration, report=False, unsafe=False)
application_mock.assert_called_once_with([package_ahriman], repository.paths, pytest.helpers.anyvar(int))
print_mock.assert_called_once_with(verbose=True, separator=" ")
def test_disallow_auto_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Structure.ALLOW_AUTO_ARCHITECTURE_RUN

View File

@ -4,6 +4,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Triggers
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
from ahriman.models.result import Result
@ -22,12 +23,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.on_result")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
@ -36,15 +38,15 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
on_start_mock.assert_called_once_with()
def test_run_trigger(args: argparse.Namespace, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
def test_run_trigger(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run triggers specified by command line
"""
args = _default_args(args)
args.trigger = ["ahriman.core.report.ReportTrigger"]
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
report_mock = mocker.patch("ahriman.core.report.ReportTrigger.on_result")
upload_mock = mocker.patch("ahriman.core.upload.UploadTrigger.on_result")

View File

@ -7,6 +7,7 @@ from unittest.mock import call as MockCall
from ahriman.application.application import Application
from ahriman.application.handlers import Update
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
from ahriman.models.result import Result
@ -32,15 +33,15 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, package_ahriman: Package,
configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
result = Result()
result.add_success(package_ahriman)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
@ -54,14 +55,15 @@ def test_run(args: argparse.Namespace, package_ahriman: Package,
on_start_mock.assert_called_once_with()
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty update list
"""
args = _default_args(args)
args.exit_code = True
args.dry_run = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.updates", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
@ -69,14 +71,14 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
check_mock.assert_called_once_with(True, True)
def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: Package,
configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
repository: Repository, mocker: MockerFixture) -> None:
"""
must raise ExitCode exception on empty build result
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.update", return_value=Result())
mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
@ -85,13 +87,14 @@ def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: P
check_mock.assert_has_calls([MockCall(True, False), MockCall(True, True)])
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run simplified command
"""
args = _default_args(args)
args.dry_run = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.update")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
updates_mock = mocker.patch("ahriman.application.application.Application.updates")

View File

@ -5,6 +5,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Web
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@ -21,13 +22,14 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args
def test_run(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None:
"""
must run command
"""
args = _default_args(args)
mocker.patch("ahriman.core.spawn.Spawn.start")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
setup_mock = mocker.patch("ahriman.web.web.setup_service")
run_mock = mocker.patch("ahriman.web.web.run_server")

View File

@ -550,9 +550,29 @@ def test_subparsers_repo_sync_architecture(parser: argparse.ArgumentParser) -> N
"""
repo-sync command must correctly parse architecture list
"""
args = parser.parse_args(["repo-report"])
args = parser.parse_args(["repo-sync"])
assert args.architecture is None
args = parser.parse_args(["-a", "x86_64", "repo-report"])
args = parser.parse_args(["-a", "x86_64", "repo-sync"])
assert args.architecture == ["x86_64"]
def test_subparsers_repo_tree(parser: argparse.ArgumentParser) -> None:
"""
repo-tree command must imply lock, report and quiet
"""
args = parser.parse_args(["repo-tree"])
assert args.lock is None
assert not args.report
assert args.quiet
def test_subparsers_repo_tree_architecture(parser: argparse.ArgumentParser) -> None:
"""
repo-tree command must correctly parse architecture list
"""
args = parser.parse_args(["repo-tree"])
assert args.architecture is None
args = parser.parse_args(["-a", "x86_64", "repo-tree"])
assert args.architecture == ["x86_64"]

View File

@ -10,6 +10,7 @@ from ahriman.core.alpm.pacman import Pacman
from ahriman.core.auth import Auth
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.core.spawn import Spawn
from ahriman.core.status.watcher import Watcher
from ahriman.models.aur_package import AURPackage
@ -388,6 +389,24 @@ def remote_source() -> RemoteSource:
return RemoteSource.from_source(PackageSource.AUR, "ahriman", "aur")
@pytest.fixture
def repository(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> Repository:
"""
fixture for repository
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
mocker(MockerFixture): mocker object
Returns:
Repository: repository test instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository._set_context")
return Repository.load("x86_64", configuration, database, report=False, unsafe=False)
@pytest.fixture
def repository_paths(configuration: Configuration) -> RepositoryPaths:
"""
@ -444,17 +463,18 @@ def user() -> User:
@pytest.fixture
def watcher(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> Watcher:
def watcher(configuration: Configuration, database: SQLite, repository: Repository, mocker: MockerFixture) -> Watcher:
"""
package status watcher fixture
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
Watcher: package status watcher test instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
return Watcher("x86_64", configuration, database)

View File

@ -1,3 +1,5 @@
import pytest
from pathlib import Path
from pyalpm import error as PyalpmError
from pytest_mock import MockerFixture
@ -21,8 +23,9 @@ def test_init_with_local_cache(configuration: Configuration, mocker: MockerFixtu
with TemporaryDirectory(ignore_cleanup_errors=True) as pacman_root:
mocker.patch.object(RepositoryPaths, "pacman", Path(pacman_root))
# during the creation pyalpm.Handle will create also version file which we would like to remove later
Pacman("x86_64", configuration, refresh_database=1)
sync_mock.assert_called_once_with(False)
pacman = Pacman("x86_64", configuration, refresh_database=1)
assert pacman.handle
sync_mock.assert_called_once_with(pytest.helpers.anyvar(int), force=False)
def test_init_with_local_cache_forced(configuration: Configuration, mocker: MockerFixture) -> None:
@ -37,8 +40,9 @@ def test_init_with_local_cache_forced(configuration: Configuration, mocker: Mock
with TemporaryDirectory(ignore_cleanup_errors=True) as pacman_root:
mocker.patch.object(RepositoryPaths, "pacman", Path(pacman_root))
# during the creation pyalpm.Handle will create also version file which we would like to remove later
Pacman("x86_64", configuration, refresh_database=2)
sync_mock.assert_called_once_with(True)
pacman = Pacman("x86_64", configuration, refresh_database=2)
assert pacman.handle
sync_mock.assert_called_once_with(pytest.helpers.anyvar(int), force=True)
def test_database_copy(pacman: Pacman, repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
@ -54,7 +58,7 @@ def test_database_copy(pacman: Pacman, repository_paths: RepositoryPaths, mocker
copy_mock = mocker.patch("shutil.copy")
chown_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.chown")
pacman.database_copy(database, path, repository_paths, use_ahriman_cache=True)
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
copy_mock.assert_called_once_with(path / "sync" / "core.db", dst_path)
chown_mock.assert_called_once_with(dst_path)
@ -70,7 +74,7 @@ def test_database_copy_skip(pacman: Pacman, repository_paths: RepositoryPaths, m
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: True if p.is_relative_to(path) else False)
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(database, path, repository_paths, use_ahriman_cache=False)
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=False)
copy_mock.assert_not_called()
@ -85,7 +89,7 @@ def test_database_copy_no_directory(pacman: Pacman, repository_paths: Repository
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: True if p.is_relative_to(path) else False)
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(database, path, repository_paths, use_ahriman_cache=True)
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
copy_mock.assert_not_called()
@ -100,7 +104,7 @@ def test_database_copy_no_root_file(pacman: Pacman, repository_paths: Repository
mocker.patch("pathlib.Path.is_file", return_value=False)
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(database, path, repository_paths, use_ahriman_cache=True)
pacman.database_copy(pacman.handle, database, path, repository_paths, use_ahriman_cache=True)
copy_mock.assert_not_called()
@ -114,7 +118,7 @@ def test_database_copy_database_exist(pacman: Pacman, repository_paths: Reposito
mocker.patch("pathlib.Path.is_file", return_value=True)
copy_mock = mocker.patch("shutil.copy")
pacman.database_copy(database, Path("root"), repository_paths, use_ahriman_cache=True)
pacman.database_copy(pacman.handle, database, Path("root"), repository_paths, use_ahriman_cache=True)
copy_mock.assert_not_called()
@ -123,7 +127,7 @@ def test_database_init(pacman: Pacman, configuration: Configuration) -> None:
must init database with settings
"""
mirror = configuration.get("alpm", "mirror")
database = pacman.database_init("test", mirror, "x86_64")
database = pacman.database_init(pacman.handle, "test", mirror, "x86_64")
assert len(database.servers) == 1
@ -139,7 +143,7 @@ def test_database_sync(pacman: Pacman) -> None:
handle_mock.init_transaction.return_value = transaction_mock
pacman.handle = handle_mock
pacman.database_sync(False)
pacman.database_sync(pacman.handle, force=False)
handle_mock.init_transaction.assert_called_once_with()
core_mock.update.assert_called_once_with(False)
extra_mock.update.assert_called_once_with(False)
@ -157,7 +161,7 @@ def test_database_sync_failed(pacman: Pacman) -> None:
handle_mock.get_syncdbs.return_value = [core_mock, extra_mock]
pacman.handle = handle_mock
pacman.database_sync(False)
pacman.database_sync(pacman.handle, force=False)
extra_mock.update.assert_called_once_with(False)
@ -170,7 +174,7 @@ def test_database_sync_forced(pacman: Pacman) -> None:
handle_mock.get_syncdbs.return_value = [core_mock]
pacman.handle = handle_mock
pacman.database_sync(True)
pacman.database_sync(pacman.handle, force=True)
handle_mock.init_transaction.assert_called_once_with()
core_mock.update.assert_called_once_with(True)

View File

@ -1,7 +1,7 @@
import pytest
from ahriman.core.formatters import AurPrinter, ConfigurationPrinter, PackagePrinter, PatchPrinter, StatusPrinter, \
StringPrinter, UpdatePrinter, UserPrinter, VersionPrinter
StringPrinter, TreePrinter, UpdatePrinter, UserPrinter, VersionPrinter
from ahriman.models.aur_package import AURPackage
from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package
@ -85,15 +85,29 @@ def string_printer() -> StringPrinter:
@pytest.fixture
def update_printer(package_ahriman: Package) -> UpdatePrinter:
def tree_printer(package_ahriman: Package) -> TreePrinter:
"""
fixture for build status printer
fixture for tree printer
Args:
package_ahriman(Package): package fixture
Returns:
UpdatePrinter: build status printer test instance
TreePrinter: tree printer test instance
"""
return TreePrinter(0, [package_ahriman])
@pytest.fixture
def update_printer(package_ahriman: Package) -> UpdatePrinter:
"""
fixture for update printer
Args:
package_ahriman(Package): package fixture
Returns:
UpdatePrinter: udpate printer test instance
"""
return UpdatePrinter(package_ahriman, None)

View File

@ -0,0 +1,15 @@
from ahriman.core.formatters import TreePrinter
def test_properties(tree_printer: TreePrinter) -> None:
"""
must return non-empty properties list
"""
assert tree_printer.properties()
def test_title(tree_printer: TreePrinter) -> None:
"""
must return non-empty title
"""
assert tree_printer.title() is not None

View File

@ -3,7 +3,7 @@ from ahriman.core.formatters import UpdatePrinter
def test_properties(update_printer: UpdatePrinter) -> None:
"""
must return empty properties list
must return non-empty properties list
"""
assert update_printer.properties()

View File

@ -5,48 +5,65 @@ from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import GitRemoteError
from ahriman.core.gitremote.remote_push import RemotePush
from ahriman.models.package import Package
from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.result import Result
def test_package_update(package_ahriman: Package, mocker: MockerFixture) -> None:
def test_package_update(database: SQLite, configuration: Configuration, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must update single package
"""
patch1 = PkgbuildPatch(None, "patch")
patch2 = PkgbuildPatch("key", "value")
rmtree_mock = mocker.patch("shutil.rmtree")
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
patches_mock = mocker.patch("ahriman.core.database.SQLite.patches_get", return_value=[patch1, patch2])
patches_write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
runner = RemotePush(configuration, database, "gitremote")
local = Path("local")
RemotePush.package_update(package_ahriman, local)
assert runner.package_update(package_ahriman, local) == package_ahriman.base
rmtree_mock.assert_has_calls([
MockCall(local / package_ahriman.base, ignore_errors=True),
MockCall(local / package_ahriman.base / ".git", ignore_errors=True),
])
fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), package_ahriman.remote)
patches_mock.assert_called_once_with(package_ahriman.base)
patches_write_mock.assert_has_calls([
MockCall(local / package_ahriman.base / f"ahriman-{package_ahriman.base}.patch"),
MockCall(local / package_ahriman.base / f"ahriman-{patch2.key}.patch"),
])
def test_packages_update(result: Result, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_packages_update(database: SQLite, configuration: Configuration, result: Result, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must generate packages update
"""
update_mock = mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.package_update",
return_value=[package_ahriman.base])
runner = RemotePush(configuration, database, "gitremote")
local = Path("local")
assert list(RemotePush.packages_update(result, local))
assert list(runner.packages_update(result, local))
update_mock.assert_called_once_with(package_ahriman, local)
def test_run(configuration: Configuration, result: Result, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_run(database: SQLite, configuration: Configuration, result: Result, package_ahriman: Package,
mocker: MockerFixture) -> None:
"""
must push changes on result
"""
mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.packages_update", return_value=[package_ahriman.base])
fetch_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.fetch")
push_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.push")
runner = RemotePush(configuration, "gitremote")
runner = RemotePush(configuration, database, "gitremote")
runner.run(result)
fetch_mock.assert_called_once_with(pytest.helpers.anyvar(int), runner.remote_source)
@ -55,12 +72,12 @@ def test_run(configuration: Configuration, result: Result, package_ahriman: Pack
)
def test_run_failed(configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
def test_run_failed(database: SQLite, configuration: Configuration, result: Result, mocker: MockerFixture) -> None:
"""
must reraise exception on error occurred
"""
mocker.patch("ahriman.core.build_tools.sources.Sources.fetch", side_effect=Exception())
runner = RemotePush(configuration, "gitremote")
runner = RemotePush(configuration, database, "gitremote")
with pytest.raises(GitRemoteError):
runner.run(result)

View File

@ -1,18 +1,22 @@
from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.gitremote import RemotePushTrigger
from ahriman.models.context_key import ContextKey
from ahriman.models.package import Package
from ahriman.models.result import Result
def test_on_result(configuration: Configuration, result: Result, package_ahriman: Package,
mocker: MockerFixture) -> None:
database: SQLite, mocker: MockerFixture) -> None:
"""
must push changes on result
"""
database_mock = mocker.patch("ahriman.core._Context.get", return_value=database)
run_mock = mocker.patch("ahriman.core.gitremote.remote_push.RemotePush.run")
trigger = RemotePushTrigger("x86_64", configuration)
trigger.on_result(result, [package_ahriman])
database_mock.assert_called_once_with(ContextKey("database", SQLite))
run_mock.assert_called_once_with(result)

View File

@ -4,7 +4,6 @@ from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.core.repository.cleaner import Cleaner
from ahriman.core.repository.executor import Executor
from ahriman.core.repository.update_handler import UpdateHandler
@ -24,7 +23,7 @@ def cleaner(configuration: Configuration, database: SQLite, mocker: MockerFixtur
Cleaner: cleaner test instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
return Cleaner("x86_64", configuration, database, report=False, unsafe=False)
return Cleaner("x86_64", configuration, database, report=False, unsafe=False, refresh_pacman_database=0)
@pytest.fixture
@ -45,24 +44,7 @@ def executor(configuration: Configuration, database: SQLite, mocker: MockerFixtu
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_queue")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
return Executor("x86_64", configuration, database, report=False, unsafe=False)
@pytest.fixture
def repository(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> Repository:
"""
fixture for repository
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
mocker(MockerFixture): mocker object
Returns:
Repository: repository test instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
return Repository("x86_64", configuration, database, report=False, unsafe=False)
return Executor("x86_64", configuration, database, report=False, unsafe=False, refresh_pacman_database=0)
@pytest.fixture
@ -83,4 +65,4 @@ def update_handler(configuration: Configuration, database: SQLite, mocker: Mocke
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_queue")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
return UpdateHandler("x86_64", configuration, database, report=False, unsafe=False)
return UpdateHandler("x86_64", configuration, database, report=False, unsafe=False, refresh_pacman_database=0)

View File

@ -2,11 +2,44 @@ import pytest
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.core.sign.gpg import GPG
from ahriman.models.context_key import ContextKey
from ahriman.models.package import Package
def test_load(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
"""
must correctly load instance
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
context_mock = mocker.patch("ahriman.core.repository.Repository._set_context")
Repository.load("x86_64", configuration, database, report=False, unsafe=False)
context_mock.assert_called_once_with()
def test_set_context(configuration: Configuration, database: SQLite, mocker: MockerFixture) -> None:
"""
must set context variables
"""
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
set_mock = mocker.patch("ahriman.core._Context.set")
instance = Repository.load("x86_64", configuration, database, report=False, unsafe=False)
set_mock.assert_has_calls([
MockCall(ContextKey("database", SQLite), instance.database),
MockCall(ContextKey("configuration", Configuration), instance.configuration),
MockCall(ContextKey("pacman", Pacman), instance.pacman),
MockCall(ContextKey("sign", GPG), instance.sign),
MockCall(ContextKey("repository", Repository), instance),
])
def test_load_archives(package_ahriman: Package, package_python_schedule: Package,
repository: Repository, mocker: MockerFixture) -> None:
"""

View File

@ -12,7 +12,7 @@ def test_create_tree_on_load(configuration: Configuration, database: SQLite, moc
"""
mocker.patch("ahriman.core.repository.repository_properties.check_user")
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
RepositoryProperties("x86_64", configuration, database, report=False, unsafe=False)
RepositoryProperties("x86_64", configuration, database, report=False, unsafe=False, refresh_pacman_database=0)
tree_create_mock.assert_called_once_with()
@ -23,6 +23,6 @@ def test_create_tree_on_load_unsafe(configuration: Configuration, database: SQLi
"""
mocker.patch("ahriman.core.repository.repository_properties.check_user", side_effect=UnsafeRunError(0, 1))
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
RepositoryProperties("x86_64", configuration, database, report=False, unsafe=False)
RepositoryProperties("x86_64", configuration, database, report=False, unsafe=False, refresh_pacman_database=0)
tree_create_mock.assert_not_called()

View File

@ -4,6 +4,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.core.repository.update_handler import UpdateHandler
from ahriman.core.util import utcnow
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.remote_source import RemoteSource
@ -82,7 +83,7 @@ def test_updates_aur_ignore(update_handler: UpdateHandler, package_ahriman: Pack
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
package_load_mock = mocker.patch("ahriman.models.package.Package.from_aur")
update_handler.updates_aur([], vcs=True)
assert not update_handler.updates_aur([], vcs=True)
package_load_mock.assert_not_called()
@ -92,11 +93,16 @@ def test_updates_aur_ignore_vcs(update_handler: UpdateHandler, package_ahriman:
must skip VCS packages check if requested
"""
mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.packages", return_value=[package_ahriman])
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
mocker.patch("ahriman.models.package.Package.is_vcs", return_value=True)
package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated")
package_is_newer_than_mock = mocker.patch("ahriman.models.package.Package.is_newer_than", return_value=True)
package_is_outdated_mock = mocker.patch("ahriman.models.package.Package.is_outdated", return_value=False)
ts1 = utcnow().timestamp()
update_handler.updates_aur([], vcs=False)
package_is_outdated_mock.assert_not_called()
assert not update_handler.updates_aur([], vcs=False)
package_is_newer_than_mock.assert_called_once_with(pytest.helpers.anyvar(float, strict=True))
assert ts1 < package_is_newer_than_mock.call_args[0][0] < utcnow().timestamp()
package_is_outdated_mock.assert_called_once_with(package_ahriman, update_handler.paths, calculate_version=False)
def test_updates_local(update_handler: UpdateHandler, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -6,7 +6,6 @@ from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.status.watcher import Watcher
from ahriman.core.status.web_client import WebClient
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.log_record_id import LogRecordId
from ahriman.models.package import Package
@ -17,10 +16,10 @@ def test_force_no_report(configuration: Configuration, database: SQLite, mocker:
must force dummy report client
"""
configuration.set_option("web", "port", "8080")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
watcher = Watcher("x86_64", configuration, database)
assert not isinstance(watcher.repository.reporter, WebClient)
load_mock.assert_called_once_with("x86_64", configuration, database, report=False, unsafe=False)
def test_get(watcher: Watcher, package_ahriman: Package) -> None:

View File

@ -0,0 +1,92 @@
import pytest
from ahriman.core import _Context
from ahriman.models.context_key import ContextKey
def test_get_set() -> None:
"""
must set and get variable
"""
key, value = ContextKey("key", int), 42
ctx = _Context()
ctx.set(key, value)
assert ctx.get(key) == value
def test_get_key_exception() -> None:
"""
must raise KeyError in case if key was not found
"""
ctx = _Context()
with pytest.raises(KeyError):
ctx.get(ContextKey("key", int))
def test_get_value_exception() -> None:
"""
must raise ValueError in case if key type differs from existing value
"""
key, value = ContextKey("key", int), 42
ctx = _Context()
ctx.set(key, value)
with pytest.raises(ValueError):
ctx.get(ContextKey("key", str))
def test_set_key_exception() -> None:
"""
must raise KeyError in case if key already exists
"""
key, value = ContextKey("key", int), 42
ctx = _Context()
ctx.set(key, value)
with pytest.raises(KeyError):
ctx.set(key, value)
def test_set_value_exception() -> None:
"""
must raise ValueError in case if key type differs from new value
"""
ctx = _Context()
with pytest.raises(ValueError):
ctx.set(ContextKey("key", str), 42)
def test_contains() -> None:
"""
must correctly check if element is in list
"""
key, value = ContextKey("key", int), 42
ctx = _Context()
ctx.set(key, value)
assert key not in ctx
assert key.key in ctx
assert "random-key" not in ctx
def test_iter() -> None:
"""
must return keys iterator
"""
key, value = ContextKey("key", int), 42
ctx = _Context()
ctx.set(key, value)
assert set(iter(ctx)) == set(iter({key.key: value}))
def test_len() -> None:
"""
must correctly define collection length
"""
ctx = _Context()
ctx.set(ContextKey("str", str), "str")
ctx.set(ContextKey("int", int), 42)
assert len(ctx) == 2

View File

@ -5,6 +5,7 @@ from pytest_mock import MockerFixture
from ahriman.core.database import SQLite
from ahriman.core.tree import Leaf, Tree
from ahriman.models.package import Package
from ahriman.models.package_description import PackageDescription
from ahriman.models.repository_paths import RepositoryPaths
@ -53,6 +54,18 @@ def test_leaf_load(package_ahriman: Package, repository_paths: RepositoryPaths,
dependencies_mock.assert_called_once_with(pytest.helpers.anyvar(int))
def test_tree_resolve(package_ahriman: Package, package_python_schedule: Package, repository_paths: RepositoryPaths,
database: SQLite, mocker: MockerFixture) -> None:
"""
must resolve denendecnies tree
"""
mocker.patch("ahriman.core.tree.Leaf.load", side_effect=lambda package, p, d: Leaf(package, set(package.depends)))
tree = Tree.resolve([package_ahriman, package_python_schedule], repository_paths, database)
assert len(tree) == 1
assert len(tree[0]) == 2
def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> None:
"""
must generate correct levels in the simples case
@ -60,21 +73,54 @@ def test_tree_levels(leaf_ahriman: Leaf, leaf_python_schedule: Leaf) -> None:
leaf_ahriman.dependencies = set(leaf_python_schedule.package.packages.keys())
tree = Tree([leaf_ahriman, leaf_python_schedule])
assert len(tree.levels()) == 2
first, second = tree.levels()
assert first == [leaf_python_schedule.package]
assert second == [leaf_ahriman.package]
def test_tree_load(package_ahriman: Package, package_python_schedule: Package, repository_paths: RepositoryPaths,
database: SQLite, mocker: MockerFixture) -> None:
def test_tree_levels_sorted() -> None:
"""
must package list
must reorder tree, moving packages which are not required for the next level further
"""
mocker.patch("tempfile.mkdtemp")
mocker.patch("ahriman.core.build_tools.sources.Sources.load")
mocker.patch("ahriman.models.package.Package.dependencies")
mocker.patch("shutil.rmtree")
leaf1 = Leaf(
Package(
base="package1",
version="1.0.0",
remote=None,
packages={"package1": PackageDescription()}
),
dependencies=set()
)
leaf2 = Leaf(
Package(
base="package2",
version="1.0.0",
remote=None,
packages={"package2": PackageDescription()}
),
dependencies={"package1"}
)
leaf3 = Leaf(
Package(
base="package3",
version="1.0.0",
remote=None,
packages={"package3": PackageDescription()}
),
dependencies={"package1"}
)
leaf4 = Leaf(
Package(
base="package4",
version="1.0.0",
remote=None,
packages={"package4": PackageDescription()}
),
dependencies={"package3"}
)
tree = Tree.load([package_ahriman, package_python_schedule], repository_paths, database)
assert len(tree.leaves) == 2
tree = Tree([leaf1, leaf2, leaf3, leaf4])
first, second, third = tree.levels()
assert first == [leaf1.package]
assert second == [leaf3.package]
assert third == [leaf2.package, leaf4.package]

View File

@ -11,8 +11,8 @@ from typing import Any
from unittest.mock import MagicMock
from ahriman.core.exceptions import BuildError, OptionError, UnsafeRunError
from ahriman.core.util import check_output, check_user, exception_response_text, filter_json, full_version, \
enum_values, package_like, pretty_datetime, pretty_size, safe_filename, walk
from ahriman.core.util import check_output, check_user, enum_values, exception_response_text, filter_json, \
full_version, package_like, pretty_datetime, pretty_size, safe_filename, utcnow, walk
from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
from ahriman.models.repository_paths import RepositoryPaths
@ -322,6 +322,15 @@ def test_safe_filename() -> None:
assert safe_filename("tolua++-1.0.93-4-x86_64.pkg.tar.zst") == "tolua---1.0.93-4-x86_64.pkg.tar.zst"
def test_utcnow() -> None:
"""
must generate correct timestamp
"""
ts1 = utcnow()
ts2 = utcnow()
assert 1 > (ts2 - ts1).total_seconds() > 0
def test_walk(resource_path_root: Path) -> None:
"""
must traverse directory recursively
@ -339,13 +348,12 @@ def test_walk(resource_path_root: Path) -> None:
resource_path_root / "models" / "package_gcc10_srcinfo",
resource_path_root / "models" / "package_tpacpi-bat-git_srcinfo",
resource_path_root / "models" / "package_yay_srcinfo",
resource_path_root / "web" / "templates" / "build-status" / "failed-modal.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "alerts.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "key-import-modal.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "login-modal.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "package-add-modal.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "package-info-modal.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "package-rebuild-modal.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "success-modal.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "table.jinja2",
resource_path_root / "web" / "templates" / "static" / "favicon.ico",
resource_path_root / "web" / "templates" / "utils" / "bootstrap-scripts.jinja2",

View File

View File

@ -265,11 +265,31 @@ def test_full_depends(package_ahriman: Package, package_python_schedule: Package
assert package_python_schedule.full_depends(pyalpm_handle, [package_python_schedule]) == expected
def test_is_newer_than(package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must correctly check if package is newer than specified timestamp
"""
# base checks, true/false
assert package_ahriman.is_newer_than(package_ahriman.packages[package_ahriman.base].build_date - 1)
assert not package_ahriman.is_newer_than(package_ahriman.packages[package_ahriman.base].build_date + 1)
# list check
min_date = min(package.build_date for package in package_python_schedule.packages.values())
assert package_python_schedule.is_newer_than(min_date)
# null list check
package_python_schedule.packages["python-schedule"].build_date = None
assert package_python_schedule.is_newer_than(min_date)
package_python_schedule.packages["python2-schedule"].build_date = None
assert not package_python_schedule.is_newer_than(min_date)
def test_is_outdated_false(package_ahriman: Package, repository_paths: RepositoryPaths) -> None:
"""
must be not outdated for the same package
"""
assert not package_ahriman.is_outdated(package_ahriman, repository_paths)
assert not package_ahriman.is_outdated(package_ahriman, repository_paths, calculate_version=True)
def test_is_outdated_true(package_ahriman: Package, repository_paths: RepositoryPaths) -> None:
@ -279,7 +299,7 @@ def test_is_outdated_true(package_ahriman: Package, repository_paths: Repository
other = Package.from_json(package_ahriman.view())
other.version = other.version.replace("-1", "-2")
assert package_ahriman.is_outdated(other, repository_paths)
assert package_ahriman.is_outdated(other, repository_paths, calculate_version=True)
def test_build_status_pretty_print(package_ahriman: Package) -> None:

View File

@ -12,6 +12,7 @@ import ahriman.core.auth.helpers
from ahriman.core.auth import OAuth
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
from ahriman.core.spawn import Spawn
from ahriman.models.user import User
from ahriman.web.web import setup_service
@ -48,7 +49,7 @@ def request(app: web.Application, path: str, method: str, json: Any = None, data
@pytest.fixture
def application(configuration: Configuration, spawner: Spawn, database: SQLite,
def application(configuration: Configuration, spawner: Spawn, database: SQLite, repository: Repository,
mocker: MockerFixture) -> web.Application:
"""
application fixture
@ -57,20 +58,21 @@ def application(configuration: Configuration, spawner: Spawn, database: SQLite,
configuration(Configuration): configuration fixture
spawner(Spawn): spawner fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
web.Application: application test instance
"""
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", False)
return setup_service("x86_64", configuration, spawner)
@pytest.fixture
def application_with_auth(configuration: Configuration, user: User, spawner: Spawn, database: SQLite,
mocker: MockerFixture) -> web.Application:
repository: Repository, mocker: MockerFixture) -> web.Application:
"""
application fixture with auth enabled
@ -79,6 +81,7 @@ def application_with_auth(configuration: Configuration, user: User, spawner: Spa
user(User): user descriptor fixture
spawner(Spawn): spawner fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
@ -86,7 +89,7 @@ def application_with_auth(configuration: Configuration, user: User, spawner: Spa
"""
configuration.set_option("auth", "target", "configuration")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", True)
application = setup_service("x86_64", configuration, spawner)
@ -98,7 +101,7 @@ def application_with_auth(configuration: Configuration, user: User, spawner: Spa
@pytest.fixture
def application_with_debug(configuration: Configuration, user: User, spawner: Spawn, database: SQLite,
mocker: MockerFixture) -> web.Application:
repository: Repository, mocker: MockerFixture) -> web.Application:
"""
application fixture with debug enabled
@ -107,6 +110,7 @@ def application_with_debug(configuration: Configuration, user: User, spawner: Sp
user(User): user descriptor fixture
spawner(Spawn): spawner fixture
database(SQLite): database fixture
repository(Repository): repository fixture
mocker(MockerFixture): mocker object
Returns:
@ -114,7 +118,7 @@ def application_with_debug(configuration: Configuration, user: User, spawner: Sp
"""
configuration.set_option("web", "debug", "yes")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", False)
return setup_service("x86_64", configuration, spawner)

View File

@ -32,9 +32,13 @@ deps =
{[tox]dependencies}
-e .[docs]
changedir = src
allowlist_externals =
/bin/bash
setenv =
SPHINX_APIDOC_OPTIONS=members,no-undoc-members,show-inheritance
commands =
/bin/bash -c 'shtab --shell bash --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../docs/completions/bash/_ahriman'
/bin/bash -c 'shtab --shell zsh --prefix ahriman --prog ahriman ahriman.application.ahriman._parser > ../docs/completions/zsh/_ahriman'
argparse-manpage --module ahriman.application.ahriman --function _parser --author "ahriman team" --project-name ahriman --author-email "" --url https://github.com/arcan1s/ahriman --output ../docs/ahriman.1
pydeps ahriman -o ../docs/ahriman-architecture.svg --no-config --no-show --cluster
sphinx-apidoc -o ../docs .