Compare commits

..

12 Commits

Author SHA1 Message Date
b412966f86 Release 2.15.1 2024-09-24 11:18:38 +03:00
1119a8d04e feat: enable changes calculation in unit 2024-09-24 11:01:37 +03:00
9b3d294992 refactor: rename Handler.check_if_empty to check_status 2024-09-24 01:36:33 +03:00
517337144c fix: bump pkgrel if the local version is newer than remote
In case of VCS packages, if PKGBUILD contains older version, the pkgrel
remains the same during the rebuild process. This fix bumps pkgrel in
any case if the local version is newer than the remote
2024-09-23 16:30:33 +03:00
db89ad46b7 build: reduce docker image size 2024-09-23 14:37:36 +03:00
9fc1c53c3d docs: update web preview picture 2024-09-23 14:03:12 +03:00
cd61ab3e5b fix: allow colon in options interpolation 2024-09-23 13:52:49 +03:00
b319e89e41 Release 2.15.0 2024-09-23 03:32:01 +03:00
b3d32c6fc4 build: speedup pacman 2024-09-23 03:30:17 +03:00
91c2aef931 website: add logo to api docs 2024-09-23 02:39:54 +03:00
3ad0037fa9 docs: update configuration references in docs 2024-09-22 15:16:34 +03:00
f5d415ab4f docs: add logo 2024-09-22 14:21:01 +03:00
48 changed files with 8056 additions and 7172 deletions

View File

@ -25,13 +25,13 @@ RUN echo "[multilib]" >> "/etc/pacman.conf" && \
echo "Include = /etc/pacman.d/mirrorlist" >> "/etc/pacman.conf"
## refresh packages, install sudo and install packages for building
RUN pacman -Syu --noconfirm sudo && \
pacman -Sy --noconfirm --asdeps fakeroot python-tox
pacman -S --noconfirm --asdeps fakeroot python-tox
## create build user
RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build && \
echo "build ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/build"
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies
RUN pacman -Sy --noconfirm --asdeps \
RUN pacman -S --noconfirm --asdeps \
devtools \
git \
pyalpm \
@ -40,21 +40,20 @@ RUN pacman -Sy --noconfirm --asdeps \
python-pyelftools \
python-requests \
&& \
pacman -Sy --noconfirm --asdeps \
pacman -S --noconfirm --asdeps \
base-devel \
python-build \
python-flit \
python-installer \
python-wheel \
&& \
pacman -Sy --noconfirm --asdeps \
pacman -S --noconfirm --asdeps \
git \
python-aiohttp \
python-boto3 \
python-cerberus \
python-cryptography \
python-jinja \
python-matplotlib \
python-systemd \
rsync \
&& \

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@ -25,7 +25,7 @@ Configuration allows string interpolation from the same configuration file, e.g.
key = ${anoher_key}
another_key = value
will read value for the ``section.key`` option from ``section.another_key``. In case if the cross-section reference is required, the ``${section:another_key}`` notation must be used. It also allows string interpolation from environment variables, e.g.:
will read value for the ``key`` option from ``another_key`` in the same section. In case if the cross-section reference is required, the ``${section:another_key}`` notation must be used. It also allows string interpolation from environment variables, e.g.:
.. code-block:: ini
@ -43,7 +43,7 @@ will try to read value from ``SECRET`` environment variable. In case if the requ
key = ${home}
home = $HOME
will eventually lead ``section1.key`` option to be set to the value of ``HOME`` environment variable (if available).
will eventually lead ``key`` option in section ``section1`` to be set to the value of ``HOME`` environment variable (if available).
There is also additional subcommand which will allow to validate configuration and print found errors. In order to do so, run ``service-config-validate`` subcommand, e.g.:
@ -379,7 +379,7 @@ Requires ``rsync`` package to be installed. Do not forget to configure ssh for u
* ``type`` - type of the upload, string, optional, must be set to ``rsync`` if exists.
* ``command`` - rsync command to run, space separated list of string, required.
* ``remote`` - remote server to rsync (e.g. ``1.2.3.4:path/to/sync``), string, required.
* ``remote`` - remote server to rsync (e.g. ``ahriman@10.0.0.1:/srv/repo``), string, required.
``s3`` type
^^^^^^^^^^^

View File

@ -8,8 +8,8 @@ Remote synchronization and remote server call
This setup requires at least two instances of the service:
#. Web service (with opt-in authorization enabled), later will be referenced as ``master`` node.
#. Application instances responsible for build, later will be referenced as ``worker`` nodes.
#. Web service (with opt-in authorization enabled), later will be referenced as **master** node.
#. Application instances responsible for build, later will be referenced as **worker** nodes.
In this example the following settings are assumed:
@ -70,7 +70,7 @@ Worker nodes configuration
username = worker-user
password = very-secure-password
As it has been mentioned above, ``status.address`` must be available for workers. In case if unix socket is used, it can be passed in the same option as usual. Optional ``status.username``/``status.password`` can be supplied in case if authentication was enabled on master node.
As it has been mentioned above, ``${status:address}`` must be available for workers. In case if unix socket is used, it can be passed in the same option as usual. Optional ``${status:username}``/``${status:password}`` can be supplied in case if authentication was enabled on master node.
#.
Each worker must call master node on success:
@ -83,7 +83,7 @@ Worker nodes configuration
[remote-call]
manual = yes
After success synchronization (see above), the built packages will be put into directory, from which they will be read during manual update, thus ``remote-call.manual`` flag is required.
After success synchronization (see above), the built packages will be put into directory, from which they will be read during manual update, thus ``${remote-call:manual}`` flag is required.
#.
Change order of trigger runs. This step is required, because by default the report trigger is called before the upload trigger and we would like to achieve the opposite:
@ -202,12 +202,12 @@ This action must be done in two steps:
Delegate builds to remote workers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This setup heavily uses upload feature described above and, in addition, also delegates build process automatically to build machines. Same as above, there must be at least two instances available (``master`` and ``worker``), however, all ``worker`` nodes must be run in the web service mode.
This setup heavily uses upload feature described above and, in addition, also delegates build process automatically to build machines. Same as above, there must be at least two instances available (**master** and **worker**), however, all **worker** nodes must be run in the web service mode.
Master node configuration
"""""""""""""""""""""""""
In addition to the configuration above, the worker list must be defined in configuration file (``build.workers`` option), i.e.:
In addition to the configuration above, the worker list must be defined in configuration file (``${build:workers}`` option), i.e.:
.. code-block:: ini
@ -218,7 +218,7 @@ In addition to the configuration above, the worker list must be defined in confi
enable_archive_upload = yes
wait_timeout = 0
In the example above, ``https://worker1.example.com`` and ``https://worker2.example.com`` are remote ``worker`` node addresses available for ``master`` node.
In the example above, ``https://worker1.example.com`` and ``https://worker2.example.com`` are remote **worker** node addresses available for **master** node.
In case if authentication is required (which is recommended way to setup it), it can be set by using ``status`` section as usual.
@ -229,7 +229,7 @@ It is required to point to the master node repository, otherwise internal depend
Also, in case if authentication is enabled, the same user with the same password must be created for all workers.
It is also recommended to set ``web.wait_timeout`` to infinite in case of multiple conflicting runs and ``service_only`` to ``yes`` in order to disable status endpoints.
It is also recommended to set ``${web:wait_timeout}`` to infinite in case of multiple conflicting runs and ``${web:service_only}`` to ``yes`` in order to disable status endpoints.
Other settings are the same as mentioned above.
@ -297,19 +297,19 @@ Command to run worker nodes (considering there will be two workers, one is on ``
docker run --privileged -p 8081:8081 -e AHRIMAN_PORT=8081 -v worker.ini:/etc/ahriman.ini.d/overrides.ini arcan1s/ahriman:latest web
docker run --privileged -p 8082:8082 -e AHRIMAN_PORT=8082 -v worker.ini:/etc/ahriman.ini.d/overrides.ini arcan1s/ahriman:latest web
Unlike the previous setup, it doesn't require to mount repository root for ``worker`` nodes, because they don't use it anyway.
Unlike the previous setup, it doesn't require to mount repository root for **worker** nodes, because they don't use it anyway.
Check proof-of-concept setup `here <https://github.com/arcan1s/ahriman/tree/master/recipes/distributed>`__.
Addition of new package, package removal, repository update
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
In all scenarios, update process must be run only on ``master`` node. Unlike the manually distributed packages described above, automatic update must be enabled only for ``master`` node.
In all scenarios, update process must be run only on **master** node. Unlike the manually distributed packages described above, automatic update must be enabled only for **master** node.
Automatic worker nodes discovery
""""""""""""""""""""""""""""""""
Instead of setting ``build.workers`` option it is also possible to configure services to load worker list dynamically. To do so, the ``ahriman.core.distributed.WorkerLoaderTrigger`` and ``ahriman.core.distributed.WorkerTrigger`` must be used for ``master`` and ``worker`` nodes repsectively. See recipes for more details.
Instead of setting ``${build:workers}`` option explicitly it is also possible to configure services to load worker list dynamically. To do so, the ``ahriman.core.distributed.WorkerLoaderTrigger`` and ``ahriman.core.distributed.WorkerTrigger`` must be used for **master** and **worker** nodes respectively. See recipes for more details.
Known limitations
"""""""""""""""""
@ -317,4 +317,4 @@ Known limitations
* Workers don't support local packages. However, it is possible to build custom packages by providing sources by using ``ahriman.core.gitremote.RemotePullTrigger`` trigger.
* No dynamic nodes discovery. In case if one of worker nodes is unavailable, the build process will fail.
* No pkgrel bump on conflicts.
* The identical user must be created for all workers. However, the ``master`` node user can be different from this one.
* The identical user must be created for all workers. However, the **master** node user can be different from this one.

View File

@ -375,7 +375,7 @@ After the success build the application extracts all linked libraries and used d
In order to disable this check completely, the ``--no-check-files`` flag can be used.
In addition, there is possibility to control paths which will be used for checking, by using option ``build.scan_paths``, which supports regular expressions. Leaving ``build.scan_paths`` blank will effectively disable any check too.
In addition, there is possibility to control paths which will be used for checking, by using option ``${build:scan_paths}``, which supports regular expressions. Leaving ``${build:scan_paths}`` blank will effectively disable any check too.
How to install built packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -14,7 +14,7 @@ The application provides special plugin which generates keyring package. This pl
[keyring]
target = keyring-generator
By default it will use ``sign.key`` as trusted key and all other keys as packagers ones. For all available options refer to :doc:`configuration </configuration>`.
By default it will use ``${sign:key}`` as trusted key and all other keys as packagers ones. For all available options refer to :doc:`configuration </configuration>`.
#.
Create package source files:
@ -52,7 +52,7 @@ The application provides special plugin which generates mirrorlist package also.
[mirrorlist-generator]
servers = https://repo.example.com/$arch
The ``mirrorlist-generator.servers`` must contain list of available mirrors, the ``$arch`` and ``$repo`` variables are supported. For more options kindly refer to :doc:`configuration </configuration>`.
The ``${mirrorlist-generator:servers}`` must contain list of available mirrors, the ``$arch`` and ``$repo`` variables are supported. For more options kindly refer to :doc:`configuration </configuration>`.
#.
Create package source files:

View File

@ -108,12 +108,12 @@ How to post build report to telegram
chat_id = @ahriman
link_path = http://example.com/aur-clone/x86_64
``api_key`` is the one sent by `@BotFather <https://t.me/botfather>`__, ``chat_id`` is the value retrieved from previous step.
``${api_key}`` is the one sent by `@BotFather <https://t.me/botfather>`__, ``${chat_id}`` is the value retrieved from previous step.
If you did everything fine you should receive the message with the next update. Quick credentials check can be done by using the following command:
.. code-block:: shell
curl 'https://api.telegram.org/bot{api_key}/sendMessage?chat_id={chat_id}&text=hello'
curl 'https://api.telegram.org/bot${api_key}/sendMessage?chat_id=${chat_id}&text=hello'
(replace ``{chat_id}`` and ``{api_key}`` with the values from configuration).
(replace ``${chat_id}`` and ``${api_key}`` with the values from configuration).

View File

@ -41,7 +41,7 @@ How to enable basic authorization
target = configuration
salt = somerandomstring
The ``salt`` parameter is optional, but recommended, and can be set to any (random) string.
The ``${auth:salt}`` parameter is optional, but recommended, and can be set to any (random) string.
#.
In order to provide access for reporting from application instances you can (the recommended way) use unix sockets by the following configuration (note, that it requires ``python-requests-unixsocket2`` package to be installed):
@ -53,7 +53,7 @@ How to enable basic authorization
This socket path must be available for web service instance and must be available for all application instances (e.g. in case if you are using docker container - see above - you need to make sure that the socket is passed to the root filesystem).
By the way, unix socket variable will be automatically set in case if ``--web-unix-socket`` argument is supplied to the ``setup`` subcommand.
By the way, unix socket variable will be automatically set in case if ``--web-unix-socket`` argument is supplied to the ``service-setup`` subcommand.
Alternatively, you need to create user for the service:
@ -96,7 +96,7 @@ How to enable OAuth authorization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#.
Create OAuth web application, download its ``client_id`` and ``client_secret``.
Create OAuth web application, download its ``${auth:client_id}`` and ``${auth:client_secret}``.
#.
Guess what? Install dependencies:
@ -118,10 +118,10 @@ How to enable OAuth authorization
[web]
address = https://example.com
Configure ``oauth_provider`` and ``oauth_scopes`` in case if you would like to use different from Google provider. Scope must grant access to user email. ``web.address`` is required to make callback URL available from internet.
Configure ``${auth:oauth_provider}`` and ``${auth:oauth_scopes}`` in case if you would like to use different from Google provider. Scope must grant access to user email. ``${web:address}`` is required to make callback URL available from internet.
#.
If you are not going to use unix socket, you also need to create service user (remember to set ``auth.salt`` option before if required):
If you are not going to use unix socket, you also need to create service user (remember to set ``${auth:salt}`` option before if required):
.. code-block:: shell

View File

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

View File

@ -3,7 +3,7 @@ Description=ArcH linux ReposItory MANager (%i)
[Service]
Type=simple
ExecStart=/usr/bin/ahriman --repository-id "%I" repo-daemon --no-changes --refresh
ExecStart=/usr/bin/ahriman --repository-id "%I" repo-daemon --refresh
User=ahriman
Group=ahriman

View File

@ -2,6 +2,6 @@
Description=ArcH linux ReposItory MANager (%i)
[Service]
ExecStart=/usr/bin/ahriman --repository-id "%I" repo-update --no-changes --refresh
ExecStart=/usr/bin/ahriman --repository-id "%I" repo-update --refresh
User=ahriman
Group=ahriman

View File

@ -3,7 +3,7 @@
include = ahriman.ini.d
; Relative path to configuration used by logging package.
logging = ahriman.ini.d/logging.ini
; Perform database migrations on the application start. Do not touch this option unless you know what are you doing.
; Perform database migrations on the application start. Do not touch this option unless you know what you are doing.
;apply_migrations = yes
; Path to the application SQLite database.
database = ${repository:root}/ahriman.db
@ -24,7 +24,7 @@ sync_files_database = yes
use_ahriman_cache = yes
[auth]
; Authentication provider, must be one of disabled, configuration, oauth.
; Authentication provider, must be one of disabled, configuration, pam, oauth.
target = disabled
; Allow read-only endpoint to be called without authentication.
allow_read_only = yes
@ -34,7 +34,7 @@ allow_read_only = yes
; Cookie secret key to be used for cookies encryption. Must be valid 32 bytes URL-safe base64-encoded string.
; If not set, it will be generated automatically.
;cookie_secret_key =
; Name of the secondary group to be used as admin group in the service.
; Name of the secondary group to be used as admin group in the service. Required if pam is used.
;full_access_group = wheel
; Authentication cookie expiration in seconds.
;max_age = 604800
@ -44,7 +44,7 @@ allow_read_only = yes
;oauth_provider = GoogleClient
; Scopes list for OAuth2 provider. Required if oauth is used.
;oauth_scopes = https://www.googleapis.com/auth/userinfo.email
; Allow login as root user (only if PAM is used).
; Allow login as root user (only applicable if PAM is used).
;permit_root_login = no
; Optional password salt.
;salt =
@ -89,10 +89,10 @@ target =
; Global switch to enable or disable status reporting.
enabled = yes
; Address of the remote service, e.g.:
; address = http://1.0.0.1:8080
; address = http://127.0.0.1:8080
; In case if unix sockets are used, it might point to the valid socket with encoded path, e.g.:
; address = http+unix://%2Fvar%2Flib%2Fahriman%2Fsocket
;address =
;address = http://${web:host}:${web:port}
; Optional password for authentication (if enabled).
;password =
; Do not log HTTP errors if occurs.
@ -104,15 +104,15 @@ suppress_http_log_errors = yes
[web]
; External address of the web service. Will be used for some features like OAuth. If none set will be generated as
; address = http://web.host:web.port
;address =
; address = http://${web:host}:${web:port}
;address = http://${web:host}:${web:port}
; Enable file upload endpoint used by some triggers.
;enable_archive_upload = no
; Address to bind the server.
host = 127.0.0.1
; Full URL to the repository index page used by templates.
;index_url =
; Max file size in bytes which can be uploaded to the server.
; Max file size in bytes which can be uploaded to the server. Requires ${web:enable_archive_upload} to be enabled.
;max_body_size =
; Port to listen. Must be set, if the web service is enabled.
;port =

View File

@ -15,6 +15,7 @@
apiDescriptionUrl="/api-docs/swagger.json"
router="hash"
layout="sidebar"
logo="/static/logo.svg"
/>
</body>

View File

@ -1,6 +1,6 @@
# AUTOMATICALLY GENERATED by `shtab`
_shtab_ahriman_subparsers=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' '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-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_subparsers=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' '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-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-statistics' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--log-handler' '-q' '--quiet' '--report' '--no-report' '-r' '--repository' '--unsafe' '-V' '--version' '--wait-timeout')
_shtab_ahriman_aur_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
@ -10,9 +10,9 @@ _shtab_ahriman_help_option_strings=('-h' '--help')
_shtab_ahriman_help_updates_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_help_version_option_strings=('-h' '--help')
_shtab_ahriman_version_option_strings=('-h' '--help')
_shtab_ahriman_package_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_package_add_option_strings=('-h' '--help' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_add_option_strings=('-h' '--help' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_package_changes_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_package_changes_remove_option_strings=('-h' '--help')
_shtab_ahriman_package_remove_option_strings=('-h' '--help')
@ -42,6 +42,7 @@ _shtab_ahriman_report_option_strings=('-h' '--help')
_shtab_ahriman_repo_restore_option_strings=('-h' '--help' '-o' '--output')
_shtab_ahriman_repo_sign_option_strings=('-h' '--help')
_shtab_ahriman_sign_option_strings=('-h' '--help')
_shtab_ahriman_repo_statistics_option_strings=('-h' '--help' '--chart' '-e' '--event' '--from-date' '--limit' '--offset' '--to-date')
_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')
@ -78,7 +79,7 @@ _shtab_ahriman_web_option_strings=('-h' '--help')
_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' '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-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' '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-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-statistics' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman___log_handler_choices=('console' 'syslog' 'journald')
_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' 'submitter' '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' 'submitter' 'url' 'url_path' 'version')
@ -100,6 +101,8 @@ _shtab_ahriman_repo_rebuild__s_choices=('unknown' 'pending' 'building' 'failed'
_shtab_ahriman_repo_rebuild___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_rebuild__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_rebuild___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_statistics__e_choices=('package-outdated' 'package-removed' 'package-update-failed' 'package-updated')
_shtab_ahriman_repo_statistics___event_choices=('package-outdated' 'package-removed' 'package-update-failed' 'package-updated')
_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_service_setup___sign_target_choices=('disabled' 'packages' 'repository')
@ -153,6 +156,8 @@ _shtab_ahriman_version___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___changes_nargs=0
_shtab_ahriman_package_add___no_changes_nargs=0
_shtab_ahriman_package_add___dependencies_nargs=0
_shtab_ahriman_package_add___no_dependencies_nargs=0
_shtab_ahriman_package_add__e_nargs=0
@ -166,6 +171,8 @@ _shtab_ahriman_package_add___refresh_nargs=0
_shtab_ahriman_add_pos_0_nargs=+
_shtab_ahriman_add__h_nargs=0
_shtab_ahriman_add___help_nargs=0
_shtab_ahriman_add___changes_nargs=0
_shtab_ahriman_add___no_changes_nargs=0
_shtab_ahriman_add___dependencies_nargs=0
_shtab_ahriman_add___no_dependencies_nargs=0
_shtab_ahriman_add__e_nargs=0
@ -179,6 +186,8 @@ _shtab_ahriman_add___refresh_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___changes_nargs=0
_shtab_ahriman_package_update___no_changes_nargs=0
_shtab_ahriman_package_update___dependencies_nargs=0
_shtab_ahriman_package_update___no_dependencies_nargs=0
_shtab_ahriman_package_update__e_nargs=0
@ -348,6 +357,8 @@ _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_statistics__h_nargs=0
_shtab_ahriman_repo_statistics___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

View File

@ -1,9 +1,9 @@
.TH AHRIMAN "1" "2024\-09\-04" "ahriman" "Generated Python Manual"
.TH AHRIMAN "1" "2024\-09\-24" "ahriman" "Generated Python Manual"
.SH NAME
ahriman
.SH SYNOPSIS
.B ahriman
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {aur-search,search,help-commands-unsafe,help,help-updates,help-version,version,package-add,add,package-update,package-changes,package-changes-remove,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-create-keyring,repo-create-mirrorlist,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-repositories,service-run,run,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,service-tree-migrate,user-add,user-list,user-remove,web} ...
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {aur-search,search,help-commands-unsafe,help,help-updates,help-version,version,package-add,add,package-update,package-changes,package-changes-remove,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-create-keyring,repo-create-mirrorlist,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-statistics,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-repositories,service-run,run,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,service-tree-migrate,user-add,user-list,user-remove,web} ...
.SH DESCRIPTION
ArcH linux ReposItory MANager
@ -134,6 +134,9 @@ restore repository data
\fBahriman\fR \fI\,repo\-sign\/\fR
sign packages
.TP
\fBahriman\fR \fI\,repo\-statistics\/\fR
repository statistics
.TP
\fBahriman\fR \fI\,repo\-status\-update\/\fR
update repository status
.TP
@ -248,7 +251,8 @@ usage: ahriman help\-version [\-h]
print application and its dependencies versions
.SH COMMAND \fI\,'ahriman package\-add'\/\fR
usage: ahriman package\-add [\-h] [\-\-dependencies | \-\-no\-dependencies] [\-e] [\-\-increment | \-\-no\-increment] [\-n] [\-y]
usage: ahriman package\-add [\-h] [\-\-changes | \-\-no\-changes] [\-\-dependencies | \-\-no\-dependencies] [\-e]
[\-\-increment | \-\-no\-increment] [\-n] [\-y]
[\-s {auto,archive,aur,directory,local,remote,repository}] [\-u USERNAME] [\-v VARIABLE]
package [package ...]
@ -259,6 +263,10 @@ add existing or new package to the build queue
package source (base name, path to local files, remote URL)
.SH OPTIONS \fI\,'ahriman package\-add'\/\fR
.TP
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
calculate changes from the latest known commit if available
.TP
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
process missing package dependencies
@ -460,7 +468,7 @@ filter check by package base
.SH OPTIONS \fI\,'ahriman repo\-check'\/\fR
.TP
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
calculate changes from the latest known commit if available. Only applicable in dry run mode
calculate changes from the latest known commit if available
.TP
\fB\-\-check\-files\fR, \fB\-\-no\-check\-files\fR
@ -624,6 +632,43 @@ usage: ahriman repo\-sign [\-h] [package ...]
\fBpackage\fR
sign only specified packages
.SH COMMAND \fI\,'ahriman repo\-statistics'\/\fR
usage: ahriman repo\-statistics [\-h] [\-\-chart CHART]
[\-e {package\-outdated,package\-removed,package\-update\-failed,package\-updated}]
[\-\-from\-date FROM_DATE] [\-\-limit LIMIT] [\-\-offset OFFSET] [\-\-to\-date TO_DATE]
[package]
fetch repository statistics
.TP
\fBpackage\fR
fetch only events for the specified package
.SH OPTIONS \fI\,'ahriman repo\-statistics'\/\fR
.TP
\fB\-\-chart\fR \fI\,CHART\/\fR
create updates chart and save it to the specified path
.TP
\fB\-e\fR \fI\,{package\-outdated,package\-removed,package\-update\-failed,package\-updated}\/\fR, \fB\-\-event\fR \fI\,{package\-outdated,package\-removed,package\-update\-failed,package\-updated}\/\fR
event type filter
.TP
\fB\-\-from\-date\fR \fI\,FROM_DATE\/\fR
only fetch events which are newer than the date
.TP
\fB\-\-limit\fR \fI\,LIMIT\/\fR
limit response by specified amount of events
.TP
\fB\-\-offset\fR \fI\,OFFSET\/\fR
skip specified amount of events
.TP
\fB\-\-to\-date\fR \fI\,TO_DATE\/\fR
only fetch events which are older than the date
.SH COMMAND \fI\,'ahriman repo\-status\-update'\/\fR
usage: ahriman repo\-status\-update [\-h] [\-s {unknown,pending,building,failed,success}]

View File

@ -48,6 +48,7 @@ _shtab_ahriman_commands() {
"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-statistics:fetch repository statistics"
"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"
@ -98,6 +99,7 @@ _shtab_ahriman_options=(
_shtab_ahriman_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--increment,--no-increment}"[increment package release (pkgrel) version on duplicate (default\: True)]:increment:"
@ -119,7 +121,7 @@ _shtab_ahriman_aur_search_options=(
_shtab_ahriman_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
{--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
@ -209,6 +211,7 @@ _shtab_ahriman_key_import_options=(
_shtab_ahriman_package_add_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--increment,--no-increment}"[increment package release (pkgrel) version on duplicate (default\: True)]:increment:"
@ -258,6 +261,7 @@ _shtab_ahriman_package_status_update_options=(
_shtab_ahriman_package_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--increment,--no-increment}"[increment package release (pkgrel) version on duplicate (default\: True)]:increment:"
@ -323,7 +327,7 @@ _shtab_ahriman_repo_backup_options=(
_shtab_ahriman_repo_check_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
{--changes,--no-changes}"[calculate changes from the latest known commit if available (default\: True)]:changes:"
{--check-files,--no-check-files}"[enable or disable checking of broken dependencies (e.g. dynamically linked libraries or modules directories) (default\: True)]:check_files:"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
@ -441,6 +445,17 @@ _shtab_ahriman_repo_sign_options=(
"(*)::sign only specified packages (default\: None):"
)
_shtab_ahriman_repo_statistics_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"--chart[create updates chart and save it to the specified path (default\: None)]:chart:"
{-e,--event}"[event type filter (default\: package-updated)]:event:(package-outdated package-removed package-update-failed package-updated)"
"--from-date[only fetch events which are newer than the date (default\: None)]:from_date:"
"--limit[limit response by specified amount of events (default\: -1)]:limit:"
"--offset[skip specified amount of events (default\: 0)]:offset:"
"--to-date[only fetch events which are older than the date (default\: None)]:to_date:"
":fetch only events for the specified package (default\: None):"
)
_shtab_ahriman_repo_status_update_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-s,--status}"[new status (default\: success)]:status:(unknown pending building failed success)"
@ -706,6 +721,7 @@ _shtab_ahriman() {
repo-restore) _arguments -C -s $_shtab_ahriman_repo_restore_options ;;
repo-setup) _arguments -C -s $_shtab_ahriman_repo_setup_options ;;
repo-sign) _arguments -C -s $_shtab_ahriman_repo_sign_options ;;
repo-statistics) _arguments -C -s $_shtab_ahriman_repo_statistics_options ;;
repo-status-update) _arguments -C -s $_shtab_ahriman_repo_status_update_options ;;
repo-sync) _arguments -C -s $_shtab_ahriman_repo_sync_options ;;
repo-tree) _arguments -C -s $_shtab_ahriman_repo_tree_options ;;

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.14.1"
__version__ = "2.15.1"

View File

@ -65,4 +65,4 @@ class Add(Handler):
application.print_updates(packages, log_fn=application.logger.info)
result = application.update(packages, packagers, bump_pkgrel=args.increment)
Add.check_if_empty(args.exit_code, result.is_empty)
Add.check_status(args.exit_code, not result.is_empty)

View File

@ -54,6 +54,6 @@ class Change(Handler):
case Action.List:
changes = client.package_changes_get(args.package)
ChangesPrinter(changes)(verbose=True, separator="")
Change.check_if_empty(args.exit_code, changes.is_empty)
Change.check_status(args.exit_code, not changes.is_empty)
case Action.Remove:
client.package_changes_update(args.package, Changes())

View File

@ -20,7 +20,7 @@
import argparse
import logging
from collections.abc import Iterable
from collections.abc import Callable, Iterable
from multiprocessing import Pool
from ahriman.application.lock import Lock
@ -124,19 +124,26 @@ class Handler:
raise NotImplementedError
@staticmethod
def check_if_empty(enabled: bool, predicate: bool) -> None:
def check_status(enabled: bool, status: bool | Callable[[], bool]) -> None:
"""
check condition and flag and raise ExitCode exception in case if it is enabled and condition match
Args:
enabled(bool): if ``False`` no check will be performed
predicate(bool): indicates condition on which exception should be thrown
status(bool | Callable[[], bool]): return status or function to check. ``True`` means success and vice versa
Raises:
ExitCode: if result is empty and check is enabled
"""
if enabled and predicate:
raise ExitCode
if not enabled:
return
match status:
case False:
raise ExitCode
# https://github.com/python/mypy/issues/14014
case Callable() if not status(): # type: ignore[misc]
raise ExitCode
@staticmethod
def repositories_extract(args: argparse.Namespace) -> list[RepositoryId]:

View File

@ -136,7 +136,7 @@ class Patch(Handler):
for patch in application.reporter.package_patches_get(package_base, None)
if variables is None or patch.key in variables
]
Patch.check_if_empty(exit_code, not patches)
Patch.check_status(exit_code, bool(patches))
PatchPrinter(package_base, patches)(verbose=True, separator=" = ")

View File

@ -49,15 +49,15 @@ class Rebuild(Handler):
application.on_start()
packages = Rebuild.extract_packages(application, args.status, from_database=args.from_database)
updates = application.repository.packages_depend_on(packages, args.depends_on)
packages = application.repository.packages_depend_on(packages, args.depends_on)
Rebuild.check_if_empty(args.exit_code, not updates)
Rebuild.check_status(args.exit_code, bool(packages))
if args.dry_run:
application.print_updates(updates, log_fn=print)
application.print_updates(packages, log_fn=print)
return
result = application.update(updates, Packagers(args.username), bump_pkgrel=args.increment)
Rebuild.check_if_empty(args.exit_code, result.is_empty)
result = application.update(packages, Packagers(args.username), bump_pkgrel=args.increment)
Rebuild.check_status(args.exit_code, not result.is_empty)
@staticmethod
def extract_packages(application: Application, status: BuildStatusEnum | None, *,

View File

@ -47,7 +47,7 @@ class Run(Handler):
parser = args.parser()
for command in args.command:
status = Run.run_command(shlex.split(command), parser)
Run.check_if_empty(True, not status)
Run.check_status(True, status)
@staticmethod
def run_command(command: list[str], parser: argparse.ArgumentParser) -> bool:

View File

@ -60,7 +60,8 @@ class Search(Handler):
"""
official_packages_list = Official.multisearch(*args.search)
aur_packages_list = AUR.multisearch(*args.search)
Search.check_if_empty(args.exit_code, not official_packages_list and not aur_packages_list)
non_empty = bool(official_packages_list or aur_packages_list)
Search.check_status(args.exit_code, non_empty)
for packages_list in (official_packages_list, aur_packages_list):
# keep sorting by packages source

View File

@ -57,4 +57,4 @@ class ServiceUpdates(Handler):
return
UpdatePrinter(remote, local_version)(verbose=True, separator=" -> ")
ServiceUpdates.check_if_empty(args.exit_code, not same_version)
ServiceUpdates.check_status(args.exit_code, same_version)

View File

@ -61,7 +61,7 @@ class Status(Handler):
else:
packages = client.package_get(None)
Status.check_if_empty(args.exit_code, not packages)
Status.check_status(args.exit_code, bool(packages))
comparator: Callable[[tuple[Package, BuildStatus]], str] = lambda item: item[0].base
filter_fn: Callable[[tuple[Package, BuildStatus]], bool] =\

View File

@ -63,7 +63,7 @@ class UnsafeCommands(Handler):
parser(argparse.ArgumentParser): generated argument parser
"""
args = parser.parse_args(command)
UnsafeCommands.check_if_empty(True, args.command in unsafe_commands)
UnsafeCommands.check_status(True, args.command not in unsafe_commands)
@staticmethod
def get_unsafe_commands(parser: argparse.ArgumentParser) -> list[str]:

View File

@ -54,7 +54,7 @@ class Update(Handler):
application.changes(packages)
if args.dry_run: # exit from application if no build requested
Update.check_if_empty(args.exit_code, not packages) # status code check
Update.check_status(args.exit_code, bool(packages)) # status code check
return
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
@ -62,7 +62,7 @@ class Update(Handler):
application.print_updates(packages, log_fn=application.logger.info)
result = application.update(packages, packagers, bump_pkgrel=args.increment)
Update.check_if_empty(args.exit_code, result.is_empty)
Update.check_status(args.exit_code, not result.is_empty)
@staticmethod
def log_fn(application: Application, dry_run: bool) -> Callable[[str], None]:

View File

@ -59,9 +59,9 @@ class Users(Handler):
database.user_update(user.hash_password(salt))
case Action.List:
users = database.user_list(args.username, args.role)
Users.check_if_empty(args.exit_code, not users)
for user in users:
UserPrinter(user)(verbose=True)
Users.check_status(args.exit_code, bool(users))
case Action.Remove:
database.user_remove(args.username)

View File

@ -61,7 +61,7 @@ class Validate(Handler):
ValidationPrinter(node, errors)(verbose=True)
# as we reach this part it means that we always have errors
Validate.check_if_empty(args.exit_code, True)
Validate.check_status(args.exit_code, False)
@staticmethod
def schema(repository_id: RepositoryId, configuration: Configuration) -> ConfigurationSchema:

View File

@ -52,7 +52,7 @@ class ShellInterpolator(configparser.Interpolation):
def identifiers() -> Generator[tuple[str | None, str], None, None]:
# extract all found identifiers and parse them
for identifier in ShellTemplate(value).get_identifiers():
match identifier.split(":"):
match identifier.rsplit(":", maxsplit=1):
case [lookup_option]: # single option from the same section
yield None, lookup_option
case [lookup_section, lookup_option]: # reference to another section

View File

@ -538,14 +538,10 @@ class Package(LazyLogging):
if local_version is None:
return None # local version not found, keep upstream pkgrel
epoch, pkgver, _ = parse_version(self.version)
local_epoch, local_pkgver, local_pkgrel = parse_version(local_version)
if epoch != local_epoch or pkgver != local_pkgver:
return None # epoch or pkgver are different, keep upstream pkgrel
if vercmp(self.version, local_version) > 0:
return None # upstream version is newer than local one, keep upstream pkgrel
*_, local_pkgrel = parse_version(local_version)
if "." in local_pkgrel:
major, minor = local_pkgrel.rsplit(".", maxsplit=1)
else:

View File

@ -120,15 +120,21 @@ def test_run(args: argparse.Namespace, configuration: Configuration) -> None:
Handler.run(args, repository_id, configuration, report=True)
def test_check_if_empty() -> None:
def test_check_status() -> None:
"""
must raise exception in case if predicate is True and enabled
"""
Handler.check_if_empty(False, False)
Handler.check_if_empty(True, False)
Handler.check_if_empty(False, True)
Handler.check_status(False, True)
Handler.check_status(False, False)
Handler.check_status(False, lambda: True)
Handler.check_status(False, lambda: False)
Handler.check_status(True, True)
with pytest.raises(ExitCode):
Handler.check_if_empty(True, True)
Handler.check_status(True, False)
Handler.check_status(True, lambda: True)
with pytest.raises(ExitCode):
Handler.check_status(True, lambda: False)
def test_repositories_extract(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:

View File

@ -82,7 +82,7 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration
mocker.patch("ahriman.application.application.Application.add")
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")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies",
@ -98,7 +98,7 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration
Packagers(args.username, {package_ahriman.base: "packager"}),
bump_pkgrel=args.increment)
dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies)
check_mock.assert_called_once_with(False, False)
check_mock.assert_called_once_with(False, True)
print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int))
@ -113,7 +113,7 @@ def test_run_no_changes(args: argparse.Namespace, configuration: Configuration,
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
mocker.patch("ahriman.application.handlers.Handler.check_status")
mocker.patch("ahriman.application.application.Application.updates")
mocker.patch("ahriman.application.application.Application.with_dependencies")
mocker.patch("ahriman.application.application.Application.print_updates")
@ -138,8 +138,8 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
mocker.patch("ahriman.application.application.Application.with_dependencies")
mocker.patch("ahriman.application.application.Application.updates")
mocker.patch("ahriman.application.application.Application.print_updates")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
Add.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)

View File

@ -36,13 +36,13 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get",
return_value=Changes("sha", "change"))
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with(args.package)
check_mock.assert_called_once_with(False, False)
check_mock.assert_called_once_with(False, True)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator="")
@ -55,11 +55,11 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get", return_value=Changes())
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,

View File

@ -163,12 +163,12 @@ def test_patch_set_list(application: Application, mocker: MockerFixture) -> None
get_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get",
return_value=[PkgbuildPatch(None, "patch"), PkgbuildPatch("version", "value")])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
Patch.patch_set_list(application, "ahriman", ["version"], False)
get_mock.assert_called_once_with("ahriman", None)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ")
check_mock.assert_called_once_with(False, False)
check_mock.assert_called_once_with(False, True)
def test_patch_set_list_all(application: Application, mocker: MockerFixture) -> None:
@ -178,12 +178,12 @@ def test_patch_set_list_all(application: Application, mocker: MockerFixture) ->
get_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get",
return_value=[PkgbuildPatch(None, "patch")])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
Patch.patch_set_list(application, "ahriman", None, False)
get_mock.assert_called_once_with("ahriman", None)
print_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" = ")
check_mock.assert_called_once_with(False, False)
check_mock.assert_called_once_with(False, True)
def test_patch_set_list_empty_exception(application: Application, mocker: MockerFixture) -> None:
@ -191,10 +191,10 @@ def test_patch_set_list_empty_exception(application: Application, mocker: Mocker
must raise ExitCode exception on empty patch list
"""
mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get", return_value={})
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
Patch.patch_set_list(application, "ahriman", [], True)
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)
def test_patch_set_create(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -47,7 +47,7 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on",
return_value=[package_ahriman])
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
_, repository_id = configuration.check_loaded()
@ -55,7 +55,7 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
extract_mock.assert_called_once_with(pytest.helpers.anyvar(int), args.status, from_database=args.from_database)
application_packages_mock.assert_called_once_with([package_ahriman], None)
application_mock.assert_called_once_with([package_ahriman], Packagers(args.username), bump_pkgrel=args.increment)
check_mock.assert_has_calls([MockCall(False, False), MockCall(False, False)])
check_mock.assert_has_calls([MockCall(False, True), MockCall(False, True)])
on_start_mock.assert_called_once_with()
@ -87,13 +87,13 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, rep
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")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
print_mock = mocker.patch("ahriman.application.application.Application.print_updates")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
application_mock.assert_not_called()
check_mock.assert_called_once_with(False, False)
check_mock.assert_called_once_with(False, True)
print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int))
@ -142,11 +142,11 @@ def test_run_update_empty_exception(args: argparse.Namespace, configuration: Con
mocker.patch("ahriman.application.handlers.Rebuild.extract_packages")
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[])
mocker.patch("ahriman.application.application.Application.print_updates")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)
def test_run_build_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
@ -160,11 +160,11 @@ def test_run_build_empty_exception(args: argparse.Namespace, configuration: Conf
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())
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
check_mock.assert_has_calls([MockCall(True, False), MockCall(True, True)])
check_mock.assert_has_calls([MockCall(True, True), MockCall(True, False)])
def test_extract_packages(application: Application, mocker: MockerFixture) -> None:

View File

@ -39,14 +39,14 @@ def test_run(args: argparse.Namespace, configuration: Configuration, 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])
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
aur_search_mock.assert_called_once_with("ahriman")
official_search_mock.assert_called_once_with("ahriman")
check_mock.assert_called_once_with(False, False)
check_mock.assert_called_once_with(False, True)
print_mock.assert_has_calls([
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": "),
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": "),
@ -64,11 +64,11 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.core.formatters.Printer.print")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)
def test_run_sort(args: argparse.Namespace, configuration: Configuration, repository: Repository,

View File

@ -34,13 +34,13 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
package_mock = mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
application_mock = mocker.patch("ahriman.core.formatters.Printer.print")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
ServiceUpdates.run(args, repository_id, configuration, report=False)
package_mock.assert_called_once_with(package_ahriman.base, None)
application_mock.assert_called_once_with(verbose=True, log_fn=pytest.helpers.anyvar(int), separator=" -> ")
check_mock.assert_called_once_with(args.exit_code, True)
check_mock.assert_called_once_with(args.exit_code, False)
def test_run_skip(args: argparse.Namespace, configuration: Configuration, repository: Repository,
@ -53,7 +53,7 @@ def test_run_skip(args: argparse.Namespace, configuration: Configuration, reposi
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
application_mock = mocker.patch("ahriman.core.formatters.Printer.print")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
ServiceUpdates.run(args, repository_id, configuration, report=False)

View File

@ -41,14 +41,14 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
packages_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get",
return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
(package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False)
application_mock.assert_called_once_with()
packages_mock.assert_called_once_with(None)
check_mock.assert_called_once_with(False, False)
check_mock.assert_called_once_with(False, True)
print_mock.assert_has_calls([
MockCall(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
for _ in range(3)
@ -65,11 +65,11 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.Client.status_get")
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)
def test_run_verbose(args: argparse.Namespace, configuration: Configuration, repository: Repository,

View File

@ -58,18 +58,18 @@ def test_check_unsafe(mocker: MockerFixture) -> None:
"""
must check if command is unsafe
"""
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
UnsafeCommands.check_unsafe(["service-clean"], ["service-clean"], _parser())
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)
def test_check_unsafe_safe(mocker: MockerFixture) -> None:
"""
must check if command is safe
"""
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
UnsafeCommands.check_unsafe(["package-status"], ["service-clean"], _parser())
check_mock.assert_called_once_with(True, False)
check_mock.assert_called_once_with(True, True)
def test_get_unsafe_commands() -> None:

View File

@ -49,7 +49,7 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
result.add_updated(package_ahriman)
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")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies",
return_value=[package_ahriman])
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
@ -66,7 +66,7 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files)
changes_mock.assert_called_once_with([package_ahriman])
dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies)
check_mock.assert_called_once_with(False, False)
check_mock.assert_called_once_with(False, True)
on_start_mock.assert_called_once_with()
print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int))
@ -81,11 +81,11 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
args.dry_run = True
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")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)
def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
@ -101,11 +101,11 @@ def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: P
mocker.patch("ahriman.application.application.Application.with_dependencies", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.Application.print_updates")
mocker.patch("ahriman.application.application.Application.changes")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)
def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configuration: Configuration,
@ -117,7 +117,7 @@ def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configu
args.dry_run = True
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")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
@ -127,7 +127,7 @@ def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configu
args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, check_files=args.check_files)
application_mock.assert_not_called()
changes_mock.assert_called_once_with([package_ahriman])
check_mock.assert_called_once_with(False, pytest.helpers.anyvar(int))
check_mock.assert_called_once_with(False, True)
def test_run_no_changes(args: argparse.Namespace, configuration: Configuration, repository: Repository,
@ -140,7 +140,7 @@ def test_run_no_changes(args: argparse.Namespace, configuration: Configuration,
args.changes = False
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
mocker.patch("ahriman.application.handlers.Handler.check_status")
mocker.patch("ahriman.application.application.Application.updates")
changes_mock = mocker.patch("ahriman.application.application.Application.changes")

View File

@ -97,13 +97,13 @@ def test_run_list(args: argparse.Namespace, configuration: Configuration, databa
args = _default_args(args)
args.action = Action.List
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
list_mock = mocker.patch("ahriman.core.database.SQLite.user_list", return_value=[user])
_, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False)
list_mock.assert_called_once_with("user", args.role)
check_mock.assert_called_once_with(False, False)
check_mock.assert_called_once_with(False, True)
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, database: SQLite,
@ -116,11 +116,11 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
args.exit_code = True
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.database.SQLite.user_list", return_value=[])
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
_, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False)
check_mock.assert_called_once_with(True, True)
check_mock.assert_called_once_with(True, False)
def test_run_remove(args: argparse.Namespace, configuration: Configuration, database: SQLite,

View File

@ -134,9 +134,10 @@ def test_init_bump_pkgrel_skip(task_ahriman: Task, mocker: MockerFixture) -> Non
"""
must keep pkgrel if version is different from provided
"""
task_ahriman.package.version = "2.0.0-1"
mocker.patch("ahriman.models.package.Package.from_build", return_value=task_ahriman.package)
mocker.patch("ahriman.core.build_tools.sources.Sources.load", return_value="sha")
write_mock = mocker.patch("ahriman.models.pkgbuild_patch.PkgbuildPatch.write")
assert task_ahriman.init(Path("ahriman"), [], f"2:{task_ahriman.package.version}") == "sha"
assert task_ahriman.init(Path("ahriman"), [], "1.0.0-1") == "sha"
write_mock.assert_not_called()

View File

@ -24,6 +24,9 @@ def _parser() -> dict[str, dict[str, str]]:
"key3": "${section1:home}",
"key5": "${section1:key4}",
},
"section4:suffix": {
"key6": "value6"
},
}
@ -45,6 +48,10 @@ def test_extract_variables() -> None:
assert not dict(ShellInterpolator._extract_variables(parser, "${section4:key1}", parser["section1"]))
assert dict(ShellInterpolator._extract_variables(parser, "${section4:suffix:key6}", parser["section1"])) == {
"section4:suffix:key6": "value6",
}
def test_environment() -> None:
"""

View File

@ -474,11 +474,11 @@ def test_next_pkgrel(package_ahriman: Package) -> None:
assert package_ahriman.next_pkgrel(package_ahriman.version) == "1.2.2"
package_ahriman.version = "1:1.0.0-1"
assert package_ahriman.next_pkgrel("1:1.0.1-1") is None
assert package_ahriman.next_pkgrel("2:1.0.0-1") is None
assert package_ahriman.next_pkgrel("1:1.0.1-1") == "1.1"
assert package_ahriman.next_pkgrel("2:1.0.0-1") == "1.1"
package_ahriman.version = "1.0.0-1.1"
assert package_ahriman.next_pkgrel("1.0.1-2") is None
assert package_ahriman.next_pkgrel("1.0.1-2") == "2.1"
assert package_ahriman.next_pkgrel("1.0.0-2") == "2.1"
package_ahriman.version = "1.0.0-2"

View File

@ -78,7 +78,7 @@ allowlist_externals =
passenv =
SSH_AUTH_SOCK
commands =
git add package/archlinux/PKGBUILD src/ahriman/__init__.py docs/ahriman-architecture.svg package/share/man/man1/ahriman.1 package/share/bash-completion/completions/_ahriman package/share/zsh/site-functions/_ahriman
git add package/archlinux/PKGBUILD src/ahriman/__init__.py docs/_static/architecture.svg package/share/man/man1/ahriman.1 package/share/bash-completion/completions/_ahriman package/share/zsh/site-functions/_ahriman
git commit -m "Release {posargs}"
git tag "{posargs}"
git push

BIN
web.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 370 KiB