mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-11-03 23:33:41 +00:00 
			
		
		
		
	Compare commits
	
		
			12 Commits
		
	
	
		
			2.18.3
			...
			7885df0dae
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7885df0dae | |||
| 7abfa261c5 | |||
| 694aeecfa9 | |||
| b2e0ccfd3d | |||
| 2376603de9 | |||
| e88d35cf24 | |||
| a1281989f3 | |||
| 440d7785d2 | |||
| 3d97490c11 | |||
| 89d8227ba6 | |||
| fca9239cd2 | |||
| be57045972 | 
							
								
								
									
										11
									
								
								.github/workflows/setup.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/workflows/setup.sh
									
									
									
									
										vendored
									
									
								
							@ -26,11 +26,16 @@ cp "docker/systemd-nspawn.sh" "/usr/local/bin/systemd-nspawn"
 | 
			
		||||
# create fresh tarball
 | 
			
		||||
tox -e archive
 | 
			
		||||
# run makepkg
 | 
			
		||||
mv dist/ahriman-*.tar.gz package/archlinux
 | 
			
		||||
PKGVER=$(python -c "from src.ahriman import __version__; print(__version__)")
 | 
			
		||||
mv "dist/ahriman-$PKGVER.tar.gz" package/archlinux
 | 
			
		||||
chmod +777 package/archlinux  # because fuck you that's why
 | 
			
		||||
cd package/archlinux
 | 
			
		||||
sudo -u nobody -- makepkg -cf --skipchecksums --noconfirm
 | 
			
		||||
sudo -u nobody -- makepkg --packagelist | grep -v -- -debug- | pacman -U --noconfirm -
 | 
			
		||||
sudo -u nobody -- makepkg --packagelist | grep "ahriman-core-$PKGVER" | pacman -U --noconfirm --nodeps -
 | 
			
		||||
if [[ -z $MINIMAL_INSTALL ]]; then
 | 
			
		||||
    sudo -u nobody -- makepkg --packagelist | grep "ahriman-triggers-$PKGVER" | pacman -U --noconfirm --nodeps -
 | 
			
		||||
    sudo -u nobody -- makepkg --packagelist | grep "ahriman-web-$PKGVER" | pacman -U --noconfirm --nodeps -
 | 
			
		||||
fi
 | 
			
		||||
# create machine-id which is required by build tools
 | 
			
		||||
systemd-machine-id-setup
 | 
			
		||||
 | 
			
		||||
@ -41,12 +46,12 @@ pacman -Qdtq | pacman -Rscn --noconfirm -
 | 
			
		||||
[[ -z $MINIMAL_INSTALL ]] && WEB_ARGS=("--web-port" "8080")
 | 
			
		||||
ahriman -a x86_64 -r "github" service-setup --packager "ahriman bot <ahriman@example.com>" "${WEB_ARGS[@]}"
 | 
			
		||||
# enable services
 | 
			
		||||
systemctl enable ahriman-web
 | 
			
		||||
systemctl enable ahriman@x86_64-github.timer
 | 
			
		||||
if [[ -z $MINIMAL_INSTALL ]]; then
 | 
			
		||||
    # validate configuration
 | 
			
		||||
    ahriman service-config-validate --exit-code
 | 
			
		||||
    # run web service (detached)
 | 
			
		||||
    systemctl enable ahriman-web
 | 
			
		||||
    sudo -u ahriman -- ahriman web &
 | 
			
		||||
    WEB_PID=$!
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,8 @@ RUN cd "/home/build/ahriman" && \
 | 
			
		||||
    tox -e archive && \
 | 
			
		||||
    cp ./dist/*.tar.gz "package/archlinux" && \
 | 
			
		||||
    cd "package/archlinux" && \
 | 
			
		||||
    runuser -u build -- makepkg --noconfirm --install --skipchecksums && \
 | 
			
		||||
    runuser -u build -- makepkg --noconfirm --skipchecksums && \
 | 
			
		||||
    runuser -u build -- makepkg --packagelist | grep -v -- -debug- | pacman -U --noconfirm --nodeps - && \
 | 
			
		||||
    cd / && rm -r "/home/build/ahriman"
 | 
			
		||||
 | 
			
		||||
# cleanup unused
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										51
									
								
								docs/advanced-usage/handlers.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								docs/advanced-usage/handlers.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
Writing own handler
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
It is possible to extend the application by adding own custom commands. To do so it is required to implement class, which derives from ``ahriman.application.handlers.handler.Handler`` and put it to the ``ahriman.application.handlers`` package. The class later will be loaded automatically and included to each command run.
 | 
			
		||||
 | 
			
		||||
Let's imagine, that the new class implements ``help-web``, which prints server information to the stdout. To do so, we need to implement base ``ahriman.application.handlers.handler.Handler.run`` method which is entry point for all subcommands:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   from ahriman.application.application import Application
 | 
			
		||||
   from ahriman.application.handlers.handler import Handler
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
   class HelpWeb(Handler):
 | 
			
		||||
 | 
			
		||||
      @classmethod
 | 
			
		||||
      def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
 | 
			
		||||
              report: bool) -> None:
 | 
			
		||||
        # load application instance
 | 
			
		||||
        # report is set to True to make sure that web client is loaded
 | 
			
		||||
        application = Application(repository_id, configuration, report=True)
 | 
			
		||||
        # extract web client
 | 
			
		||||
        client = application.repository.reporter
 | 
			
		||||
 | 
			
		||||
        # send request to the server
 | 
			
		||||
        response = client.make_request("GET", f"{client.address}/api/v1/info")
 | 
			
		||||
        result = response.json()
 | 
			
		||||
        print(result)
 | 
			
		||||
 | 
			
		||||
The main functionality of the class is already described, but command is still not available yet. To do so, it is required to set ``arguments`` property, which is list of functions, which takes argument parser object, creates new subcommand and returns the modified parser, e.g.:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   import argparse
 | 
			
		||||
 | 
			
		||||
   from ahriman.application.handlers.handler import SubParserAction
 | 
			
		||||
 | 
			
		||||
   ...
 | 
			
		||||
 | 
			
		||||
       @staticmethod
 | 
			
		||||
       def set_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
           parser = root.add_parser("help-web", help="get web server status",
 | 
			
		||||
                                    description="request server info and print it to stdout")
 | 
			
		||||
 | 
			
		||||
       arguments = set_parser
 | 
			
		||||
 | 
			
		||||
In addition, ``ahriman.application.handlers.handler.Handler.ALLOW_MULTI_ARCHITECTURE_RUN`` can be set to ``False`` in order to disable multiprocess run (e.g. in case if there are conflicting operations, like writting to stdout).
 | 
			
		||||
 | 
			
		||||
Save the file above as ``/usr/lib/python3.12/site-packages/ahriman/application/handlers/help_web.py`` (replace ``python3.12`` with actual python version) and you are set.
 | 
			
		||||
 | 
			
		||||
For more examples and details, please check builtin handlers and classes documentations.
 | 
			
		||||
@ -1,6 +1,12 @@
 | 
			
		||||
Advanced usage
 | 
			
		||||
==============
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 2
 | 
			
		||||
 | 
			
		||||
   handlers
 | 
			
		||||
   views
 | 
			
		||||
 | 
			
		||||
Depending on the goal the package can be used in different ways. Nevertheless, in the most cases you will need some basic classes
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
							
								
								
									
										41
									
								
								docs/advanced-usage/views.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								docs/advanced-usage/views.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
			
		||||
Writing own API endpoint
 | 
			
		||||
========================
 | 
			
		||||
 | 
			
		||||
The web service loads views dynamically, thus it is possible to add custom API endpoint or even web page. The views must be derived from ``ahriman.web.views.base.BaseView`` and implements HTTP methods. The API specification will be also loaded (if available), but optional. The implementation must be saved into the ``ahriman.web.views`` package
 | 
			
		||||
 | 
			
		||||
Let's consider example for API endpoint which always returns 204 with no response:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   from aiohttp.web import Response, HTTPNoContent
 | 
			
		||||
 | 
			
		||||
   from ahriman.web.views.base import BaseView
 | 
			
		||||
 | 
			
		||||
   class PingView(BaseView):
 | 
			
		||||
 | 
			
		||||
       async def get(self) -> Response:
 | 
			
		||||
           # do nothing, just raise 204 response
 | 
			
		||||
           # check public methods of the BaseView class for all available controls
 | 
			
		||||
           raise HTTPNoContent
 | 
			
		||||
 | 
			
		||||
The ``get()`` method can be decorated by ``aiohttp_apispec`` methods, but we will leave it for self-study. Consider checking examples of usages in the main package.
 | 
			
		||||
 | 
			
		||||
In order to view to be set correctly to routes list, few more options are required to be set. First of all, it is required to specify ``ROUTES`` (list of strings), which contains list of all available routes, e.g.:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   ...
 | 
			
		||||
 | 
			
		||||
       ROUTES = ["/api/v1/ping"]
 | 
			
		||||
 | 
			
		||||
In addition, it is also recommended to specify permission level for using this endpoint. Since this endpoint neither does anything nor returns sensitive information, it can be set to ``UserAccess.Unauthorized``:
 | 
			
		||||
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
   ...
 | 
			
		||||
 | 
			
		||||
       GET_PERMISSION = UserAccess.Unauthorized
 | 
			
		||||
 | 
			
		||||
That's all. Just save the file as ``/usr/lib/python3.12/site-packages/ahriman/web/views/ping.py`` (replace ``python3.12`` with actual python version) and restart web server.
 | 
			
		||||
 | 
			
		||||
For more examples and details, please check builtin handlers and classes documentations.
 | 
			
		||||
@ -228,6 +228,14 @@ ahriman.application.handlers.triggers module
 | 
			
		||||
   :no-undoc-members:
 | 
			
		||||
   :show-inheritance:
 | 
			
		||||
 | 
			
		||||
ahriman.application.handlers.triggers\_support module
 | 
			
		||||
-----------------------------------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: ahriman.application.handlers.triggers_support
 | 
			
		||||
   :members:
 | 
			
		||||
   :no-undoc-members:
 | 
			
		||||
   :show-inheritance:
 | 
			
		||||
 | 
			
		||||
ahriman.application.handlers.unsafe\_commands module
 | 
			
		||||
----------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,14 @@ ahriman.core.exceptions module
 | 
			
		||||
   :no-undoc-members:
 | 
			
		||||
   :show-inheritance:
 | 
			
		||||
 | 
			
		||||
ahriman.core.module\_loader module
 | 
			
		||||
----------------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: ahriman.core.module_loader
 | 
			
		||||
   :members:
 | 
			
		||||
   :no-undoc-members:
 | 
			
		||||
   :show-inheritance:
 | 
			
		||||
 | 
			
		||||
ahriman.core.spawn module
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ Full dependency diagram:
 | 
			
		||||
``ahriman.application`` package
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
This package contains application (aka executable) related classes and everything for it. It also contains package called ``ahriman.application.handlers`` in which all available subcommands are described as separated classes derived from the base ``ahriman.application.handlers.Handler`` class.
 | 
			
		||||
This package contains application (aka executable) related classes and everything for it. It also contains package called ``ahriman.application.handlers`` in which all available subcommands are described as separated classes derived from the base ``ahriman.application.handlers.handler.Handler`` class.
 | 
			
		||||
 | 
			
		||||
``ahriman.application.application.Application`` (god class) is used for any interaction from parsers with repository. It is divided into multiple traits by functions (package related and repository related) in the same package.
 | 
			
		||||
 | 
			
		||||
@ -52,8 +52,10 @@ This package contains everything required for the most of application actions an
 | 
			
		||||
This package also provides some generic functions and classes which may be used by other packages:
 | 
			
		||||
 | 
			
		||||
* ``ahriman.core.exceptions`` provides custom exceptions.
 | 
			
		||||
* ``ahriman.core.module_loader`` provides ``implementations`` method which can be used for dynamic classes load. In particular, this method is used for web views and application handlers loading.
 | 
			
		||||
* ``ahriman.core.spawn.Spawn`` is a tool which can spawn another ``ahriman`` process. This feature is used by web application.
 | 
			
		||||
* ``ahriman.core.tree`` is a dependency tree implementation.
 | 
			
		||||
* ``ahriman.core.types`` are an additional global types for mypy checks.
 | 
			
		||||
 | 
			
		||||
``ahriman.models`` package
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
@ -436,6 +438,9 @@ REST API supports only JSON data.
 | 
			
		||||
Different APIs are separated into different packages:
 | 
			
		||||
 | 
			
		||||
* ``ahriman.web.views.api`` not a real API, but some views which provide OpenAPI support.
 | 
			
		||||
* ``ahriman.web.views.*.auditlog`` provides event log API.
 | 
			
		||||
* ``ahriman.web.views.*.distributed`` is an API for builders interaction for multi-node setup.
 | 
			
		||||
* ``ahriman.web.views.*.pacakges`` contains views which provide information about existing packages.
 | 
			
		||||
* ``ahriman.web.views.*.service`` provides views for application controls.
 | 
			
		||||
* ``ahriman.web.views.*.status`` package provides REST API for application reporting.
 | 
			
		||||
* ``ahriman.web.views.*.user`` package provides login and logout methods which can be called without authorization.
 | 
			
		||||
 | 
			
		||||
@ -311,6 +311,12 @@ Automatic worker nodes discovery
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
Those triggers have to be installed as a separate package:
 | 
			
		||||
 | 
			
		||||
.. code-block:: shell
 | 
			
		||||
 | 
			
		||||
   yay -S ahriman-triggers
 | 
			
		||||
 | 
			
		||||
Known limitations
 | 
			
		||||
"""""""""""""""""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,12 @@
 | 
			
		||||
Maintenance packages
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
Those features require extensions package to be installed before, e.g.:
 | 
			
		||||
 | 
			
		||||
.. code-block:: shell
 | 
			
		||||
 | 
			
		||||
   yay -S ahriman-triggers
 | 
			
		||||
 | 
			
		||||
Generate keyring package
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,11 +5,11 @@ How to setup web service
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
#. 
 | 
			
		||||
   Install dependencies:
 | 
			
		||||
   Install web service:
 | 
			
		||||
 | 
			
		||||
   .. code-block:: shell
 | 
			
		||||
 | 
			
		||||
      yay -S --asdeps python-aiohttp python-aiohttp-jinja2 python-aiohttp-apispec>=3.0.0 python-aiohttp-cors
 | 
			
		||||
      yay -S -ahriman-web
 | 
			
		||||
 | 
			
		||||
#. 
 | 
			
		||||
   Configure service:
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,6 @@ Contents
 | 
			
		||||
   faq/index
 | 
			
		||||
   migrations/index
 | 
			
		||||
   architecture
 | 
			
		||||
   advanced-usage
 | 
			
		||||
   advanced-usage/index
 | 
			
		||||
   triggers
 | 
			
		||||
   modules
 | 
			
		||||
 | 
			
		||||
@ -1,51 +1,87 @@
 | 
			
		||||
# Maintainer: Evgeniy Alekseev
 | 
			
		||||
 | 
			
		||||
pkgname='ahriman'
 | 
			
		||||
pkgbase='ahriman'
 | 
			
		||||
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
 | 
			
		||||
pkgver=2.15.2
 | 
			
		||||
pkgrel=1
 | 
			
		||||
pkgdesc="ArcH linux ReposItory MANager"
 | 
			
		||||
arch=('any')
 | 
			
		||||
url="https://github.com/arcan1s/ahriman"
 | 
			
		||||
license=('GPL3')
 | 
			
		||||
url="https://ahriman.readthedocs.io/"
 | 
			
		||||
license=('GPL-3.0-or-later')
 | 
			
		||||
depends=('devtools>=1:1.0.0' 'git' 'pyalpm' 'python-bcrypt' 'python-inflection' 'python-pyelftools' 'python-requests')
 | 
			
		||||
makedepends=('python-build' 'python-flit' 'python-installer' 'python-wheel')
 | 
			
		||||
optdepends=('python-aioauth-client: web server with OAuth2 authorization'
 | 
			
		||||
            'python-aiohttp: web server'
 | 
			
		||||
            'python-aiohttp-apispec>=3.0.0: web server'
 | 
			
		||||
            'python-aiohttp-cors: web server'
 | 
			
		||||
            'python-aiohttp-jinja2: web server'
 | 
			
		||||
            'python-aiohttp-security: web server with authorization'
 | 
			
		||||
            'python-aiohttp-session: web server with authorization'
 | 
			
		||||
            'python-boto3: sync to s3'
 | 
			
		||||
            'python-cerberus: configuration validator'
 | 
			
		||||
            'python-cryptography: web server with authorization'
 | 
			
		||||
            'python-matplotlib: usage statistics chart'
 | 
			
		||||
            'python-requests-unixsocket2: client report to web server by unix socket'
 | 
			
		||||
            'python-jinja: html report generation'
 | 
			
		||||
            'python-systemd: journal support'
 | 
			
		||||
            'rsync: sync by using rsync')
 | 
			
		||||
source=("https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgname-$pkgver.tar.gz"
 | 
			
		||||
        'ahriman.sysusers'
 | 
			
		||||
        'ahriman.tmpfiles')
 | 
			
		||||
install="$pkgname.install"
 | 
			
		||||
backup=('etc/ahriman.ini'
 | 
			
		||||
        'etc/ahriman.ini.d/logging.ini')
 | 
			
		||||
source=("https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgbase-$pkgver.tar.gz"
 | 
			
		||||
        "$pkgbase.sysusers"
 | 
			
		||||
        "$pkgbase.tmpfiles")
 | 
			
		||||
 | 
			
		||||
build() {
 | 
			
		||||
  cd "$pkgname-$pkgver"
 | 
			
		||||
    cd "$pkgbase-$pkgver"
 | 
			
		||||
 | 
			
		||||
  python -m build --wheel --no-isolation
 | 
			
		||||
    python -m build --wheel --no-isolation
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
package() {
 | 
			
		||||
  cd "$pkgname-$pkgver"
 | 
			
		||||
 | 
			
		||||
  python -m installer --destdir="$pkgdir" "dist/$pkgname-$pkgver-py3-none-any.whl"
 | 
			
		||||
 | 
			
		||||
  # keep usr/share configs as reference and copy them to /etc
 | 
			
		||||
  install -Dm644 "$pkgdir/usr/share/$pkgname/settings/ahriman.ini" "$pkgdir/etc/ahriman.ini"
 | 
			
		||||
  install -Dm644 "$pkgdir/usr/share/$pkgname/settings/ahriman.ini.d/logging.ini" "$pkgdir/etc/ahriman.ini.d/logging.ini"
 | 
			
		||||
 | 
			
		||||
  install -Dm644 "$srcdir/$pkgname.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgname.conf"
 | 
			
		||||
  install -Dm644 "$srcdir/$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf"
 | 
			
		||||
package_ahriman() {
 | 
			
		||||
    pkgname='ahriman'
 | 
			
		||||
    pkgdesc="ArcH linux ReposItory MANager (meta package)"
 | 
			
		||||
    depends=("$pkgbase-core=$pkgver" "$pkgbase-triggers=$pkgver" "$pkgbase-web=$pkgver")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
package_ahriman-core() {
 | 
			
		||||
    pkgname='ahriman-core'
 | 
			
		||||
    optdepends=('ahriman-triggers: additional extensions for the application'
 | 
			
		||||
                'ahriman-web: web server'
 | 
			
		||||
                'python-boto3: sync to s3'
 | 
			
		||||
                'python-cerberus: configuration validator'
 | 
			
		||||
                'python-matplotlib: usage statistics chart'
 | 
			
		||||
                'python-requests-unixsocket2: client report to web server by unix socket'
 | 
			
		||||
                'python-jinja: html report generation'
 | 
			
		||||
                'python-systemd: journal support'
 | 
			
		||||
                'rsync: sync by using rsync')
 | 
			
		||||
    install="$pkgbase.install"
 | 
			
		||||
    backup=('etc/ahriman.ini'
 | 
			
		||||
            'etc/ahriman.ini.d/logging.ini')
 | 
			
		||||
 | 
			
		||||
    cd "$pkgbase-$pkgver"
 | 
			
		||||
 | 
			
		||||
    python -m installer --destdir="$pkgdir" "dist/$pkgbase-$pkgver-py3-none-any.whl"
 | 
			
		||||
    python subpackages.py "$pkgdir" "$pkgname"
 | 
			
		||||
 | 
			
		||||
    # keep usr/share configs as reference and copy them to /etc
 | 
			
		||||
    install -Dm644 "$pkgdir/usr/share/$pkgbase/settings/ahriman.ini" "$pkgdir/etc/ahriman.ini"
 | 
			
		||||
    install -Dm644 "$pkgdir/usr/share/$pkgbase/settings/ahriman.ini.d/logging.ini" "$pkgdir/etc/ahriman.ini.d/logging.ini"
 | 
			
		||||
 | 
			
		||||
    install -Dm644 "$srcdir/$pkgbase.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgbase.conf"
 | 
			
		||||
    install -Dm644 "$srcdir/$pkgbase.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgbase.conf"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
package_ahriman-triggers() {
 | 
			
		||||
    pkgname='ahriman-triggers'
 | 
			
		||||
    pkgdesc="ArcH linux ReposItory MANager, additional extensions"
 | 
			
		||||
    depends=("$pkgbase-core=$pkgver")
 | 
			
		||||
    backup=('etc/ahriman.ini.d/00-triggers.ini')
 | 
			
		||||
 | 
			
		||||
    cd "$pkgbase-$pkgver"
 | 
			
		||||
 | 
			
		||||
    python -m installer --destdir="$pkgdir" "dist/$pkgbase-$pkgver-py3-none-any.whl"
 | 
			
		||||
    python subpackages.py "$pkgdir" "$pkgname"
 | 
			
		||||
 | 
			
		||||
    install -Dm644 "$pkgdir/usr/share/$pkgbase/settings/ahriman.ini.d/00-triggers.ini" "$pkgdir/etc/ahriman.ini.d/00-triggers.ini"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
package_ahriman-web() {
 | 
			
		||||
    pkgname='ahriman-web'
 | 
			
		||||
    pkgdesc="ArcH linux ReposItory MANager, web server"
 | 
			
		||||
    depends=("$pkgbase-core=$pkgver" 'python-aiohttp-apispec>=3.0.0' 'python-aiohttp-cors' 'python-aiohttp-jinja2')
 | 
			
		||||
    optdepends=('python-aioauth-client: OAuth2 authorization support'
 | 
			
		||||
                'python-aiohttp-security: authorization support'
 | 
			
		||||
                'python-aiohttp-session: authorization support'
 | 
			
		||||
                'python-cryptography: authorization support')
 | 
			
		||||
    backup=('etc/ahriman.ini.d/00-web.ini')
 | 
			
		||||
 | 
			
		||||
    cd "$pkgbase-$pkgver"
 | 
			
		||||
 | 
			
		||||
    python -m installer --destdir="$pkgdir" "dist/$pkgbase-$pkgver-py3-none-any.whl"
 | 
			
		||||
    python subpackages.py "$pkgdir" "$pkgname"
 | 
			
		||||
 | 
			
		||||
    install -Dm644 "$pkgdir/usr/share/$pkgbase/settings/ahriman.ini.d/00-web.ini" "$pkgdir/etc/ahriman.ini.d/00-web.ini"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ post_upgrade() {
 | 
			
		||||
    local breakpoints=(
 | 
			
		||||
        2.9.0-1
 | 
			
		||||
        2.12.0-1
 | 
			
		||||
        2.16.0-1
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    for v in "${breakpoints[@]}"; do
 | 
			
		||||
 | 
			
		||||
@ -23,32 +23,6 @@ sync_files_database = yes
 | 
			
		||||
; as additional option for some subcommands). If set to no, databases must be synchronized manually.
 | 
			
		||||
use_ahriman_cache = yes
 | 
			
		||||
 | 
			
		||||
[auth]
 | 
			
		||||
; 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
 | 
			
		||||
; OAuth2 application client ID and secret. Required if oauth is used.
 | 
			
		||||
;client_id =
 | 
			
		||||
;client_secret =
 | 
			
		||||
; 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. Required if pam is used.
 | 
			
		||||
;full_access_group = wheel
 | 
			
		||||
; Authentication cookie expiration in seconds.
 | 
			
		||||
;max_age = 604800
 | 
			
		||||
; OAuth2 provider icon for the web interface.
 | 
			
		||||
;oauth_icon = google
 | 
			
		||||
; OAuth2 provider class name, one of provided by aioauth-client. Required if oauth is used.
 | 
			
		||||
;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 applicable if PAM is used).
 | 
			
		||||
;permit_root_login = no
 | 
			
		||||
; Optional password salt.
 | 
			
		||||
;salt =
 | 
			
		||||
 | 
			
		||||
[build]
 | 
			
		||||
; List of additional flags passed to archbuild command.
 | 
			
		||||
;archbuild_flags =
 | 
			
		||||
@ -70,20 +44,12 @@ triggers[] = ahriman.core.report.ReportTrigger
 | 
			
		||||
triggers[] = ahriman.core.upload.UploadTrigger
 | 
			
		||||
triggers[] = ahriman.core.gitremote.RemotePushTrigger
 | 
			
		||||
; List of well-known triggers. Used only for configuration purposes.
 | 
			
		||||
triggers_known[] = ahriman.core.distributed.WorkerLoaderTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.distributed.WorkerTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.gitremote.RemotePullTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.gitremote.RemotePushTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.report.ReportTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.support.KeyringTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.support.MirrorlistTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.upload.UploadTrigger
 | 
			
		||||
; Maximal age in seconds of the VCS packages before their version will be updated with its remote source.
 | 
			
		||||
;vcs_allowed_age = 604800
 | 
			
		||||
; List of worker nodes addresses used for build process, e.g.:
 | 
			
		||||
;     workers = http://10.0.0.1:8080 http://10.0.0.3:8080
 | 
			
		||||
; Empty list means run on the local instance.
 | 
			
		||||
;workers =
 | 
			
		||||
 | 
			
		||||
[repository]
 | 
			
		||||
; Application root.
 | 
			
		||||
@ -112,79 +78,6 @@ suppress_http_log_errors = yes
 | 
			
		||||
; Optional username for authentication (if enabled).
 | 
			
		||||
;username =
 | 
			
		||||
 | 
			
		||||
[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 = 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. Requires ${web:enable_archive_upload} to be enabled.
 | 
			
		||||
;max_body_size =
 | 
			
		||||
; Port to listen. Must be set, if the web service is enabled.
 | 
			
		||||
;port =
 | 
			
		||||
; Disable status (e.g. package status, logs, etc) endpoints. Useful for build only modes.
 | 
			
		||||
;service_only = no
 | 
			
		||||
; Path to directory with static files.
 | 
			
		||||
static_path = ${templates}/static
 | 
			
		||||
; List of directories with templates.
 | 
			
		||||
templates[] = ${prefix}/share/ahriman/templates
 | 
			
		||||
; Path to unix socket. If none set, unix socket will be disabled.
 | 
			
		||||
;unix_socket =
 | 
			
		||||
; Allow unix socket to be world readable.
 | 
			
		||||
;unix_socket_unsafe = yes
 | 
			
		||||
; Maximum amount of time in seconds to be waited before lock will be free, used by spawned processes (0 is infinite).
 | 
			
		||||
;wait_timeout =
 | 
			
		||||
 | 
			
		||||
[keyring]
 | 
			
		||||
; List of configuration section names for keyring generator plugin, e.g.:
 | 
			
		||||
;     target = keyring-trigger
 | 
			
		||||
target =
 | 
			
		||||
 | 
			
		||||
; Keyring generator trigger sample.
 | 
			
		||||
;[keyring-trigger]
 | 
			
		||||
; Generator type name.
 | 
			
		||||
;type = keyring-generator
 | 
			
		||||
; Optional keyring package description.
 | 
			
		||||
;description=
 | 
			
		||||
; Optional URL to the repository homepage.
 | 
			
		||||
;homepage=
 | 
			
		||||
; Keyring package licenses list.
 | 
			
		||||
;license = Unlicense
 | 
			
		||||
; Optional keyring package name.
 | 
			
		||||
;package =
 | 
			
		||||
; Optional packager PGP keys list. If none set, it will read from database.
 | 
			
		||||
;packagers =
 | 
			
		||||
; List of revoked PGP keys.
 | 
			
		||||
;revoked =
 | 
			
		||||
; List of master PGP keys. If none set, the sign.key value will be used.
 | 
			
		||||
;trusted =
 | 
			
		||||
 | 
			
		||||
[mirrorlist]
 | 
			
		||||
; List of configuration section names for mirrorlist generator plugin, e.g.:
 | 
			
		||||
;     target = mirrorlist-trigger
 | 
			
		||||
target =
 | 
			
		||||
 | 
			
		||||
; Mirror list generator trigger sample.
 | 
			
		||||
;[mirrorlist-trigger]
 | 
			
		||||
; Generator type name.
 | 
			
		||||
;type = mirrorlist-generator
 | 
			
		||||
; Optional mirrorlist package description.
 | 
			
		||||
;description=
 | 
			
		||||
; Optional URL to the repository homepage.
 | 
			
		||||
;homepage=
 | 
			
		||||
; Mirrorlist package licenses list.
 | 
			
		||||
;license = Unlicense
 | 
			
		||||
; Optional mirrorlist package name.
 | 
			
		||||
;package =
 | 
			
		||||
; Absolute path to generated mirrorlist file, usually path inside /etc/pacman.d directory.
 | 
			
		||||
;path =
 | 
			
		||||
; List of repository mirrors.
 | 
			
		||||
;servers =
 | 
			
		||||
 | 
			
		||||
[remote-pull]
 | 
			
		||||
; List of configuration section names for git remote pull plugin, e.g.:
 | 
			
		||||
;     target = remote-pull-trigger
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										56
									
								
								package/share/ahriman/settings/ahriman.ini.d/00-triggers.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								package/share/ahriman/settings/ahriman.ini.d/00-triggers.ini
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
			
		||||
[build]
 | 
			
		||||
; List of well-known triggers. Used only for configuration purposes.
 | 
			
		||||
triggers_known[] = ahriman.core.distributed.WorkerLoaderTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.distributed.WorkerTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.support.KeyringTrigger
 | 
			
		||||
triggers_known[] = ahriman.core.support.MirrorlistTrigger
 | 
			
		||||
; List of worker nodes addresses used for build process, e.g.:
 | 
			
		||||
;     workers = http://10.0.0.1:8080 http://10.0.0.3:8080
 | 
			
		||||
; Empty list means run on the local instance.
 | 
			
		||||
;workers =
 | 
			
		||||
 | 
			
		||||
[keyring]
 | 
			
		||||
; List of configuration section names for keyring generator plugin, e.g.:
 | 
			
		||||
;     target = keyring-trigger
 | 
			
		||||
target =
 | 
			
		||||
 | 
			
		||||
; Keyring generator trigger sample.
 | 
			
		||||
;[keyring-trigger]
 | 
			
		||||
; Generator type name.
 | 
			
		||||
;type = keyring-generator
 | 
			
		||||
; Optional keyring package description.
 | 
			
		||||
;description=
 | 
			
		||||
; Optional URL to the repository homepage.
 | 
			
		||||
;homepage=
 | 
			
		||||
; Keyring package licenses list.
 | 
			
		||||
;license = Unlicense
 | 
			
		||||
; Optional keyring package name.
 | 
			
		||||
;package =
 | 
			
		||||
; Optional packager PGP keys list. If none set, it will read from database.
 | 
			
		||||
;packagers =
 | 
			
		||||
; List of revoked PGP keys.
 | 
			
		||||
;revoked =
 | 
			
		||||
; List of master PGP keys. If none set, the sign.key value will be used.
 | 
			
		||||
;trusted =
 | 
			
		||||
 | 
			
		||||
[mirrorlist]
 | 
			
		||||
; List of configuration section names for mirrorlist generator plugin, e.g.:
 | 
			
		||||
;     target = mirrorlist-trigger
 | 
			
		||||
target =
 | 
			
		||||
 | 
			
		||||
; Mirror list generator trigger sample.
 | 
			
		||||
;[mirrorlist-trigger]
 | 
			
		||||
; Generator type name.
 | 
			
		||||
;type = mirrorlist-generator
 | 
			
		||||
; Optional mirrorlist package description.
 | 
			
		||||
;description=
 | 
			
		||||
; Optional URL to the repository homepage.
 | 
			
		||||
;homepage=
 | 
			
		||||
; Mirrorlist package licenses list.
 | 
			
		||||
;license = Unlicense
 | 
			
		||||
; Optional mirrorlist package name.
 | 
			
		||||
;package =
 | 
			
		||||
; Absolute path to generated mirrorlist file, usually path inside /etc/pacman.d directory.
 | 
			
		||||
;path =
 | 
			
		||||
; List of repository mirrors.
 | 
			
		||||
;servers =
 | 
			
		||||
							
								
								
									
										52
									
								
								package/share/ahriman/settings/ahriman.ini.d/00-web.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								package/share/ahriman/settings/ahriman.ini.d/00-web.ini
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
[auth]
 | 
			
		||||
; 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
 | 
			
		||||
; OAuth2 application client ID and secret. Required if oauth is used.
 | 
			
		||||
;client_id =
 | 
			
		||||
;client_secret =
 | 
			
		||||
; 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. Required if pam is used.
 | 
			
		||||
;full_access_group = wheel
 | 
			
		||||
; Authentication cookie expiration in seconds.
 | 
			
		||||
;max_age = 604800
 | 
			
		||||
; OAuth2 provider icon for the web interface.
 | 
			
		||||
;oauth_icon = google
 | 
			
		||||
; OAuth2 provider class name, one of provided by aioauth-client. Required if oauth is used.
 | 
			
		||||
;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 applicable if PAM is used).
 | 
			
		||||
;permit_root_login = no
 | 
			
		||||
; Optional password salt.
 | 
			
		||||
;salt =
 | 
			
		||||
 | 
			
		||||
[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 = 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. Requires ${web:enable_archive_upload} to be enabled.
 | 
			
		||||
;max_body_size =
 | 
			
		||||
; Port to listen. Must be set, if the web service is enabled.
 | 
			
		||||
;port =
 | 
			
		||||
; Disable status (e.g. package status, logs, etc) endpoints. Useful for build only modes.
 | 
			
		||||
;service_only = no
 | 
			
		||||
; Path to directory with static files.
 | 
			
		||||
static_path = ${templates}/static
 | 
			
		||||
; List of directories with templates.
 | 
			
		||||
templates[] = ${prefix}/share/ahriman/templates
 | 
			
		||||
; Path to unix socket. If none set, unix socket will be disabled.
 | 
			
		||||
;unix_socket =
 | 
			
		||||
; Allow unix socket to be world readable.
 | 
			
		||||
;unix_socket_unsafe = yes
 | 
			
		||||
; Maximum amount of time in seconds to be waited before lock will be free, used by spawned processes (0 is infinite).
 | 
			
		||||
;wait_timeout =
 | 
			
		||||
@ -30,12 +30,14 @@ class ImportType(StrEnum):
 | 
			
		||||
    import type enumeration
 | 
			
		||||
 | 
			
		||||
    Attributes:
 | 
			
		||||
        Future(MethodTypeOrder): (class attribute) from __future__ import
 | 
			
		||||
        Package(MethodTypeOrder): (class attribute) package import
 | 
			
		||||
        PackageFrom(MethodTypeOrder): (class attribute) package import, from clause
 | 
			
		||||
        System(ImportType): (class attribute) system installed packages
 | 
			
		||||
        SystemFrom(MethodTypeOrder): (class attribute) system installed packages, from clause
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    Future = "future"
 | 
			
		||||
    Package = "package"
 | 
			
		||||
    PackageFrom = "package-from"
 | 
			
		||||
    System = "system"
 | 
			
		||||
@ -70,6 +72,7 @@ class ImportOrder(BaseRawFileChecker):
 | 
			
		||||
            "import-type-order",
 | 
			
		||||
            {
 | 
			
		||||
                "default": [
 | 
			
		||||
                    "future",
 | 
			
		||||
                    "system",
 | 
			
		||||
                    "system-from",
 | 
			
		||||
                    "package",
 | 
			
		||||
@ -91,7 +94,7 @@ class ImportOrder(BaseRawFileChecker):
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def imports(source: Iterable[Any], start_lineno: int = 0) -> list[nodes.Import | nodes.ImportFrom]:
 | 
			
		||||
    def imports(source: Iterable[Any], start_lineno: int = 0) -> Iterable[nodes.Import | nodes.ImportFrom]:
 | 
			
		||||
        """
 | 
			
		||||
        extract import nodes from list of raw nodes
 | 
			
		||||
 | 
			
		||||
@ -100,7 +103,7 @@ class ImportOrder(BaseRawFileChecker):
 | 
			
		||||
            start_lineno(int, optional): minimal allowed line number (Default value = 0)
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            list[nodes.Import | nodes.ImportFrom]: list of import nodes
 | 
			
		||||
            Iterable[nodes.Import | nodes.ImportFrom]: list of import nodes
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        def is_defined_import(imports: Any) -> bool:
 | 
			
		||||
@ -108,7 +111,7 @@ class ImportOrder(BaseRawFileChecker):
 | 
			
		||||
                and imports.lineno is not None \
 | 
			
		||||
                and imports.lineno >= start_lineno
 | 
			
		||||
 | 
			
		||||
        return list(filter(is_defined_import, source))
 | 
			
		||||
        return sorted(filter(is_defined_import, source), key=lambda imports: imports.lineno)
 | 
			
		||||
 | 
			
		||||
    def check_from_imports(self, imports: nodes.ImportFrom) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@ -124,30 +127,36 @@ class ImportOrder(BaseRawFileChecker):
 | 
			
		||||
            self.add_message("from-imports-out-of-order", line=imports.lineno, args=(real, expected))
 | 
			
		||||
            break
 | 
			
		||||
 | 
			
		||||
    def check_imports(self, imports: list[nodes.Import | nodes.ImportFrom], root_package: str) -> None:
 | 
			
		||||
    def check_imports(self, imports: Iterable[nodes.Import | nodes.ImportFrom], root_package: str) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        check imports
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            imports(list[nodes.Import | nodes.ImportFrom]): list of imports in their defined order
 | 
			
		||||
            imports(Iterable[nodes.Import | nodes.ImportFrom]): list of imports in their defined order
 | 
			
		||||
            root_package(str): root package name
 | 
			
		||||
        """
 | 
			
		||||
        last_statement: tuple[int, str] | None = None
 | 
			
		||||
 | 
			
		||||
        for statement in imports:
 | 
			
		||||
            # define types and perform specific checks
 | 
			
		||||
            if isinstance(statement, nodes.ImportFrom):
 | 
			
		||||
                import_name = statement.modname
 | 
			
		||||
                root, *_ = import_name.split(".", maxsplit=1)
 | 
			
		||||
                import_type = ImportType.PackageFrom if root_package == root else ImportType.SystemFrom
 | 
			
		||||
                # check from import itself
 | 
			
		||||
                self.check_from_imports(statement)
 | 
			
		||||
            else:
 | 
			
		||||
                import_name = next(name for name, _ in statement.names)
 | 
			
		||||
                root, *_ = import_name.split(".", maxsplit=1)[0]
 | 
			
		||||
                import_type = ImportType.Package if root_package == root else ImportType.System
 | 
			
		||||
                # check import itself
 | 
			
		||||
                self.check_package_imports(statement)
 | 
			
		||||
            match statement:
 | 
			
		||||
                case nodes.ImportFrom() if statement.modname == "__future__":
 | 
			
		||||
                    import_name = statement.modname
 | 
			
		||||
                    import_type = ImportType.Future
 | 
			
		||||
                case nodes.ImportFrom():
 | 
			
		||||
                    import_name = statement.modname
 | 
			
		||||
                    root, *_ = import_name.split(".", maxsplit=1)
 | 
			
		||||
                    import_type = ImportType.PackageFrom if root_package == root else ImportType.SystemFrom
 | 
			
		||||
                    # check from import itself
 | 
			
		||||
                    self.check_from_imports(statement)
 | 
			
		||||
                case nodes.Import():
 | 
			
		||||
                    import_name = next(name for name, _ in statement.names)
 | 
			
		||||
                    root, *_ = import_name.split(".", maxsplit=1)
 | 
			
		||||
                    import_type = ImportType.Package if root_package == root else ImportType.System
 | 
			
		||||
                    # check import itself
 | 
			
		||||
                    self.check_package_imports(statement)
 | 
			
		||||
                case _:
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
            # extract index
 | 
			
		||||
            try:
 | 
			
		||||
 | 
			
		||||
@ -95,6 +95,7 @@ include = [
 | 
			
		||||
    "CONTRIBUTING.md",
 | 
			
		||||
    "SECURITY.md",
 | 
			
		||||
    "package",
 | 
			
		||||
    "subpackages.py",
 | 
			
		||||
    "web.png",
 | 
			
		||||
]
 | 
			
		||||
exclude = [
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -17,37 +17,3 @@
 | 
			
		||||
# 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 ahriman.application.handlers.add import Add
 | 
			
		||||
from ahriman.application.handlers.backup import Backup
 | 
			
		||||
from ahriman.application.handlers.change import Change
 | 
			
		||||
from ahriman.application.handlers.clean import Clean
 | 
			
		||||
from ahriman.application.handlers.copy import Copy
 | 
			
		||||
from ahriman.application.handlers.daemon import Daemon
 | 
			
		||||
from ahriman.application.handlers.dump import Dump
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.help import Help
 | 
			
		||||
from ahriman.application.handlers.key_import import KeyImport
 | 
			
		||||
from ahriman.application.handlers.patch import Patch
 | 
			
		||||
from ahriman.application.handlers.rebuild import Rebuild
 | 
			
		||||
from ahriman.application.handlers.remove import Remove
 | 
			
		||||
from ahriman.application.handlers.remove_unknown import RemoveUnknown
 | 
			
		||||
from ahriman.application.handlers.repositories import Repositories
 | 
			
		||||
from ahriman.application.handlers.restore import Restore
 | 
			
		||||
from ahriman.application.handlers.run import Run
 | 
			
		||||
from ahriman.application.handlers.search import Search
 | 
			
		||||
from ahriman.application.handlers.service_updates import ServiceUpdates
 | 
			
		||||
from ahriman.application.handlers.setup import Setup
 | 
			
		||||
from ahriman.application.handlers.shell import Shell
 | 
			
		||||
from ahriman.application.handlers.sign import Sign
 | 
			
		||||
from ahriman.application.handlers.statistics import Statistics
 | 
			
		||||
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.tree_migrate import TreeMigrate
 | 
			
		||||
from ahriman.application.handlers.triggers import Triggers
 | 
			
		||||
from ahriman.application.handlers.unsafe_commands import UnsafeCommands
 | 
			
		||||
from ahriman.application.handlers.update import Update
 | 
			
		||||
from ahriman.application.handlers.users import Users
 | 
			
		||||
from ahriman.application.handlers.validate import Validate
 | 
			
		||||
from ahriman.application.handlers.versions import Versions
 | 
			
		||||
from ahriman.application.handlers.web import Web
 | 
			
		||||
 | 
			
		||||
@ -20,8 +20,10 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.utils import enum_values, extract_user
 | 
			
		||||
from ahriman.models.package_source import PackageSource
 | 
			
		||||
from ahriman.models.packagers import Packagers
 | 
			
		||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
@ -66,3 +68,49 @@ class Add(Handler):
 | 
			
		||||
        application.print_updates(packages, log_fn=application.logger.info)
 | 
			
		||||
        result = application.update(packages, packagers, bump_pkgrel=args.increment)
 | 
			
		||||
        Add.check_status(args.exit_code, not result.is_empty)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for package addition subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("package-add", aliases=["add", "package-update"], help="add package",
 | 
			
		||||
                                 description="add existing or new package to the build queue",
 | 
			
		||||
                                 epilog="This subcommand should be used for new package addition. "
 | 
			
		||||
                                        "It also supports flag --now in case if you would like to build "
 | 
			
		||||
                                        "the package immediately. You can add new package from one of "
 | 
			
		||||
                                        "supported sources:\n\n"
 | 
			
		||||
                                        "1. If it is already built package you can specify the path to the archive.\n"
 | 
			
		||||
                                        "2. You can also add built packages from the directory (e.g. during the "
 | 
			
		||||
                                        "migration from another repository source).\n"
 | 
			
		||||
                                        "3. It is also possible to add package from local PKGBUILD, but in this case "
 | 
			
		||||
                                        "it will be ignored during the next automatic updates.\n"
 | 
			
		||||
                                        "4. Ahriman supports downloading archives from remote (e.g. HTTP) sources.\n"
 | 
			
		||||
                                        "5. Finally you can add package from AUR.")
 | 
			
		||||
        parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
 | 
			
		||||
        parser.add_argument("--changes", help="calculate changes from the latest known commit if available",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--dependencies", help="process missing package dependencies",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("--increment", help="increment package release (pkgrel) version on duplicate",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("-n", "--now", help="run update function after", action="store_true")
 | 
			
		||||
        parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
 | 
			
		||||
                                                    "-yy to force refresh even if up to date",
 | 
			
		||||
                            action="count", default=False)
 | 
			
		||||
        parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
 | 
			
		||||
                            type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
 | 
			
		||||
        parser.add_argument("-u", "--username", help="build as user", default=extract_user())
 | 
			
		||||
        parser.add_argument("-v", "--variable", help="apply specified makepkg variables to the next build",
 | 
			
		||||
                            action="append")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_package_add_parser]
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ import tarfile
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from pwd import getpwuid
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.database import SQLite
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
@ -53,6 +53,23 @@ class Backup(Handler):
 | 
			
		||||
            for backup_path in backup_paths:
 | 
			
		||||
                archive.add(backup_path)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_backup_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository backup subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-backup", help="backup repository data",
 | 
			
		||||
                                 description="backup repository settings and database")
 | 
			
		||||
        parser.add_argument("path", help="path of the output archive", type=Path)
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_paths(configuration: Configuration) -> set[Path]:
 | 
			
		||||
        """
 | 
			
		||||
@ -83,3 +100,5 @@ class Backup(Handler):
 | 
			
		||||
            paths.add(gnupg_home)
 | 
			
		||||
 | 
			
		||||
        return paths
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_repo_backup_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import ChangesPrinter
 | 
			
		||||
from ahriman.models.action import Action
 | 
			
		||||
@ -57,3 +57,43 @@ class Change(Handler):
 | 
			
		||||
                Change.check_status(args.exit_code, not changes.is_empty)
 | 
			
		||||
            case Action.Remove:
 | 
			
		||||
                client.package_changes_update(args.package, Changes())
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_package_changes_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for package changes subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("package-changes", help="get package changes",
 | 
			
		||||
                                 description="retrieve package changes stored in database",
 | 
			
		||||
                                 epilog="This command requests package status from the web interface "
 | 
			
		||||
                                        "if it is available.")
 | 
			
		||||
        parser.add_argument("package", help="package base")
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.set_defaults(action=Action.List, lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_package_changes_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for package change remove subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("package-changes-remove", help="remove package changes",
 | 
			
		||||
                                 description="remove the package changes stored remotely")
 | 
			
		||||
        parser.add_argument("package", help="package base")
 | 
			
		||||
        parser.set_defaults(action=Action.Remove, exit_code=False, lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_package_changes_parser, _set_package_changes_remove_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
@ -46,3 +46,33 @@ class Clean(Handler):
 | 
			
		||||
        application.on_start()
 | 
			
		||||
        application.clean(cache=args.cache, chroot=args.chroot, manual=args.manual, packages=args.packages,
 | 
			
		||||
                          pacman=args.pacman)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_service_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository clean subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("service-clean", aliases=["clean", "repo-clean"], help="clean local caches",
 | 
			
		||||
                                 description="remove local caches",
 | 
			
		||||
                                 epilog="The subcommand clears every temporary directories (builds, caches etc). "
 | 
			
		||||
                                        "Normally you should not run this command manually. Also in case if "
 | 
			
		||||
                                        "you are going to clear the chroot directories you will need root privileges.")
 | 
			
		||||
        parser.add_argument("--cache", help="clear directory with package caches",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=False)
 | 
			
		||||
        parser.add_argument("--chroot", help="clear build chroot", action=argparse.BooleanOptionalAction, default=False)
 | 
			
		||||
        parser.add_argument("--manual", help="clear manually added packages queue",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=False)
 | 
			
		||||
        parser.add_argument("--packages", help="clear directory with built packages",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=False)
 | 
			
		||||
        parser.add_argument("--pacman", help="clear directory with pacman local database cache",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=False)
 | 
			
		||||
        parser.set_defaults(lock=None, quiet=True, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_service_clean_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.build_status import BuildStatusEnum
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
@ -67,6 +67,26 @@ class Copy(Handler):
 | 
			
		||||
        if args.remove:
 | 
			
		||||
            source_application.remove(args.package)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_package_copy_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for package copy subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("package-copy", aliases=["copy"], help="copy package from another repository",
 | 
			
		||||
                                 description="copy package and its metadata from another repository")
 | 
			
		||||
        parser.add_argument("source", help="source repository name")
 | 
			
		||||
        parser.add_argument("package", help="package base", nargs="+")
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("--remove", help="remove package from the source repository after", action="store_true")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def copy_package(package: Package, application: Application, source_application: Application) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@ -93,3 +113,5 @@ class Copy(Handler):
 | 
			
		||||
            package.base, source_application.reporter.package_dependencies_get(package.base)
 | 
			
		||||
        )
 | 
			
		||||
        application.reporter.package_update(package, BuildStatusEnum.Pending)
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_package_copy_parser]
 | 
			
		||||
 | 
			
		||||
@ -19,11 +19,14 @@
 | 
			
		||||
#
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.application.updates_iterator import FixedUpdatesIterator, UpdatesIterator
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.application.handlers.update import Update
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.utils import extract_user
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -56,3 +59,48 @@ class Daemon(Handler):
 | 
			
		||||
 | 
			
		||||
            args.package = packages
 | 
			
		||||
            Update.run(args, repository_id, configuration, report=report)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for daemon subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-daemon", aliases=["daemon"], help="run application as daemon",
 | 
			
		||||
                                 description="start process which periodically will run update process")
 | 
			
		||||
        parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12)
 | 
			
		||||
        parser.add_argument("--aur", help="enable or disable checking for AUR updates",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
 | 
			
		||||
                                              "Only applicable in dry run mode",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
 | 
			
		||||
                                                  "(e.g. dynamically linked libraries or modules directories)",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--dependencies", help="process missing package dependencies",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--dry-run", help="just perform check for updates, same as check command",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("--increment", help="increment package release (pkgrel) on duplicate",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--local", help="enable or disable checking of local packages for updates",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--manual", help="include or exclude manual updates",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--partitions", help="instead of updating whole repository, split updates into chunks",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("-u", "--username", help="build as user", default=extract_user())
 | 
			
		||||
        parser.add_argument("--vcs", help="fetch actual version of VCS packages",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
 | 
			
		||||
                                                    "-yy to force refresh even if up to date",
 | 
			
		||||
                            action="count", default=False)
 | 
			
		||||
        parser.set_defaults(exit_code=False, lock=Path("ahriman-daemon.pid"), package=[])
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_repo_daemon_parser]
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@
 | 
			
		||||
#
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import ConfigurationPathsPrinter, ConfigurationPrinter, StringPrinter
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
@ -59,3 +59,27 @@ class Dump(Handler):
 | 
			
		||||
            case section, key:  # key only
 | 
			
		||||
                value = configuration.get(section, key, fallback="")
 | 
			
		||||
                StringPrinter(value)(verbose=False)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_service_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for config subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("service-config", aliases=["config", "repo-config"], help="dump configuration",
 | 
			
		||||
                                 description="dump configuration for the specified architecture")
 | 
			
		||||
        parser.add_argument("section", help="filter settings by section", nargs="?")
 | 
			
		||||
        parser.add_argument("key", help="filter settings by key", nargs="?")
 | 
			
		||||
        parser.add_argument("--info", help="show additional information, e.g. configuration files",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--secure", help="hide passwords and secrets from output",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_service_config_parser]
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@ import logging
 | 
			
		||||
 | 
			
		||||
from collections.abc import Callable, Iterable
 | 
			
		||||
from multiprocessing import Pool
 | 
			
		||||
from typing import TypeVar
 | 
			
		||||
 | 
			
		||||
from ahriman.application.lock import Lock
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
@ -32,12 +33,21 @@ from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
from ahriman.models.repository_paths import RepositoryPaths
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# this workaround is for several things
 | 
			
		||||
# firstly python devs don't think that is it error and asking you for workarounds https://bugs.python.org/issue41592
 | 
			
		||||
# secondly linters don't like when you are importing private members
 | 
			
		||||
# thirdly new mypy doesn't like _SubParsersAction and thinks it is a template
 | 
			
		||||
SubParserAction = TypeVar("SubParserAction", bound="argparse._SubParsersAction[argparse.ArgumentParser]")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Handler:
 | 
			
		||||
    """
 | 
			
		||||
    base handler class for command callbacks
 | 
			
		||||
 | 
			
		||||
    Attributes:
 | 
			
		||||
        ALLOW_MULTI_ARCHITECTURE_RUN(bool): (class attribute) allow running with multiple architectures
 | 
			
		||||
        arguments(list[Callable[[SubParserAction], argparse.ArgumentParser]]): (class attribute) argument parser
 | 
			
		||||
            methods, which will be called to create command line parsers
 | 
			
		||||
 | 
			
		||||
    Examples:
 | 
			
		||||
        Wrapper for all command line actions, though each derived class implements :func:`run()` method, it usually
 | 
			
		||||
@ -49,6 +59,7 @@ class Handler:
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    ALLOW_MULTI_ARCHITECTURE_RUN = True
 | 
			
		||||
    arguments: list[Callable[[SubParserAction], argparse.ArgumentParser]]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def call(cls, args: argparse.Namespace, repository_id: RepositoryId) -> bool:
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@
 | 
			
		||||
#
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
@ -48,3 +48,22 @@ class Help(Handler):
 | 
			
		||||
            parser.parse_args(["--help"])
 | 
			
		||||
        else:
 | 
			
		||||
            parser.parse_args([args.subcommand, "--help"])
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for listing help subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("help", help="show help message",
 | 
			
		||||
                                 description="show help message for application or command and exit")
 | 
			
		||||
        parser.add_argument("subcommand", help="show help message for specific command", nargs="?")
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_help_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
@ -46,3 +46,27 @@ class KeyImport(Handler):
 | 
			
		||||
        """
 | 
			
		||||
        application = Application(repository_id, configuration, report=report)
 | 
			
		||||
        application.repository.sign.key_import(args.key_server, args.key)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_service_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for key import subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("service-key-import", aliases=["key-import"], help="import PGP key",
 | 
			
		||||
                                 description="import PGP key from public sources to the repository user",
 | 
			
		||||
                                 epilog="By default ahriman runs build process with package sources validation "
 | 
			
		||||
                                        "(in case if signature and keys are available in PKGBUILD). This process will "
 | 
			
		||||
                                        "fail in case if key is not known for build user. This subcommand can be used "
 | 
			
		||||
                                        "in order to import the PGP key to user keychain.")
 | 
			
		||||
        parser.add_argument("--key-server", help="key server for key import", default="keyserver.ubuntu.com")
 | 
			
		||||
        parser.add_argument("key", help="PGP key to import from public server")
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, report=False, repository="")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_service_key_import_parser]
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ import sys
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.build_tools.sources import Sources
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import PatchPrinter
 | 
			
		||||
@ -67,6 +67,100 @@ class Patch(Handler):
 | 
			
		||||
            case Action.Remove:
 | 
			
		||||
                Patch.patch_set_remove(application, args.package, args.variable)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for new single-function patch subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("patch-add", help="add patch for PKGBUILD function",
 | 
			
		||||
                                 description="create or update patched PKGBUILD function or variable",
 | 
			
		||||
                                 epilog="Unlike ``patch-set-add``, this function allows to patch only one PKGBUILD "
 | 
			
		||||
                                        "function, e.g. typing ``ahriman patch-add ahriman pkgver`` it will change the "
 | 
			
		||||
                                        "``pkgver`` inside PKGBUILD, typing ``ahriman patch-add ahriman build()`` "
 | 
			
		||||
                                        "it will change ``build()`` function inside PKGBUILD.")
 | 
			
		||||
        parser.add_argument("package", help="package base")
 | 
			
		||||
        parser.add_argument("variable", help="PKGBUILD variable or function name. If variable is a function, "
 | 
			
		||||
                                             "it must end with ()")
 | 
			
		||||
        parser.add_argument("patch", help="path to file which contains function or variable value. If not set, "
 | 
			
		||||
                                          "the value will be read from stdin", type=Path, nargs="?")
 | 
			
		||||
        parser.set_defaults(action=Action.Update, architecture="", exit_code=False, lock=None, report=False,
 | 
			
		||||
                            repository="")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for list patches subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("patch-list", help="list patch sets",
 | 
			
		||||
                                 description="list available patches for the package")
 | 
			
		||||
        parser.add_argument("package", help="package base")
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables",
 | 
			
		||||
                            action="append")
 | 
			
		||||
        parser.set_defaults(action=Action.List, architecture="", lock=None, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_patch_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for remove patches subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("patch-remove", help="remove patch set", description="remove patches for the package")
 | 
			
		||||
        parser.add_argument("package", help="package base")
 | 
			
		||||
        parser.add_argument("-v", "--variable",
 | 
			
		||||
                            help="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",
 | 
			
		||||
                            action="append")
 | 
			
		||||
        parser.set_defaults(action=Action.Remove, architecture="", exit_code=False, lock=None, report=False,
 | 
			
		||||
                            repository="")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_patch_set_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for new full-diff patch subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("patch-set-add", help="add patch set", description="create or update source patches",
 | 
			
		||||
                                 epilog="In order to add a patch set for the package you will need to:\n\n"
 | 
			
		||||
                                        "1. Clone the AUR package manually.\n"
 | 
			
		||||
                                        "2. Add required changes (e.g. external patches, edit PKGBUILD).\n"
 | 
			
		||||
                                        "3. Run command, e.g. ``ahriman patch-set-add path/to/directory``.\n\n"
 | 
			
		||||
                                        "By default it tracks ``*.patch`` and ``*.diff`` files, but this behavior "
 | 
			
		||||
                                        "can be changed by using ``--track`` option.")
 | 
			
		||||
        parser.add_argument("package", help="path to directory with changed files for patch addition/update", type=Path)
 | 
			
		||||
        parser.add_argument("-t", "--track", help="files which has to be tracked", action="append",
 | 
			
		||||
                            default=["*.diff", "*.patch"])
 | 
			
		||||
        parser.set_defaults(action=Action.Update, architecture="", exit_code=False, lock=None, report=False,
 | 
			
		||||
                            repository="", variable=None)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def patch_create_from_diff(sources_dir: Path, architecture: str, track: list[str]) -> tuple[str, PkgbuildPatch]:
 | 
			
		||||
        """
 | 
			
		||||
@ -155,3 +249,10 @@ class Patch(Handler):
 | 
			
		||||
                application.reporter.package_patches_remove(package_base, variable)
 | 
			
		||||
        else:
 | 
			
		||||
            application.reporter.package_patches_remove(package_base, None)  # just pass as is
 | 
			
		||||
 | 
			
		||||
    arguments = [
 | 
			
		||||
        _set_patch_add_parser,
 | 
			
		||||
        _set_patch_list_parser,
 | 
			
		||||
        _set_patch_remove_parser,
 | 
			
		||||
        _set_patch_set_add_parser,
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -20,8 +20,9 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.utils import enum_values, extract_user
 | 
			
		||||
from ahriman.models.build_status import BuildStatusEnum
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
from ahriman.models.packagers import Packagers
 | 
			
		||||
@ -59,6 +60,38 @@ class Rebuild(Handler):
 | 
			
		||||
        result = application.update(packages, Packagers(args.username), bump_pkgrel=args.increment)
 | 
			
		||||
        Rebuild.check_status(args.exit_code, not result.is_empty)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository rebuild subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-rebuild", aliases=["rebuild"], help="rebuild repository",
 | 
			
		||||
                                 description="force rebuild whole repository")
 | 
			
		||||
        parser.add_argument("--depends-on", help="only rebuild packages that depend on specified packages",
 | 
			
		||||
                            action="append")
 | 
			
		||||
        parser.add_argument("--dry-run", help="just perform check for packages without rebuild process itself",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("--from-database",
 | 
			
		||||
                            help="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.",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("--increment", help="increment package release (pkgrel) on duplicate",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("-s", "--status", help="filter packages by status. Requires --from-database to be set",
 | 
			
		||||
                            type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
 | 
			
		||||
        parser.add_argument("-u", "--username", help="build as user", default=extract_user())
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def extract_packages(application: Application, status: BuildStatusEnum | None, *,
 | 
			
		||||
                         from_database: bool) -> list[Package]:
 | 
			
		||||
@ -81,3 +114,5 @@ class Rebuild(Handler):
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        return application.repository.packages()
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_repo_rebuild_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
@ -45,3 +45,21 @@ class Remove(Handler):
 | 
			
		||||
        application = Application(repository_id, configuration, report=report)
 | 
			
		||||
        application.on_start()
 | 
			
		||||
        application.remove(args.package)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_package_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for package removal subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("package-remove", aliases=["remove"], help="remove package",
 | 
			
		||||
                                 description="remove package from the repository")
 | 
			
		||||
        parser.add_argument("package", help="package name or base", nargs="+")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_package_remove_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import StringPrinter
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
@ -53,3 +53,21 @@ class RemoveUnknown(Handler):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        application.remove(unknown_packages)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_remove_unknown_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for remove unknown packages subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-remove-unknown", aliases=["remove-unknown"], help="remove unknown packages",
 | 
			
		||||
                                 description="remove packages which are missing in AUR and do not have local PKGBUILDs")
 | 
			
		||||
        parser.add_argument("--dry-run", help="just perform check for packages without removal", action="store_true")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_repo_remove_unknown_parser]
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@
 | 
			
		||||
#
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import RepositoryPrinter
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
@ -52,3 +52,23 @@ class Repositories(Handler):
 | 
			
		||||
        )
 | 
			
		||||
        for repository in cls.repositories_extract(dummy_args):
 | 
			
		||||
            RepositoryPrinter(repository)(verbose=not args.id_only)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_service_repositories(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repositories listing
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("service-repositories", help="show repositories",
 | 
			
		||||
                                 description="list all available repositories")
 | 
			
		||||
        parser.add_argument("--id-only", help="show machine readable identifier instead",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=False)
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_service_repositories]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,9 @@
 | 
			
		||||
import argparse
 | 
			
		||||
import tarfile
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
@ -46,3 +48,23 @@ class Restore(Handler):
 | 
			
		||||
        """
 | 
			
		||||
        with tarfile.open(args.path) as archive:
 | 
			
		||||
            archive.extractall(path=args.output)  # nosec
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_restore_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository restore subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-restore", help="restore repository data",
 | 
			
		||||
                                 description="restore settings and database")
 | 
			
		||||
        parser.add_argument("path", help="path of the input archive", type=Path)
 | 
			
		||||
        parser.add_argument("-o", "--output", help="root path of the extracted files", type=Path, default=Path("/"))
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_repo_restore_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
import shlex
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,25 @@ class Run(Handler):
 | 
			
		||||
            status = Run.run_command(shlex.split(command), parser)
 | 
			
		||||
            Run.check_status(True, status)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_service_run(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for multicommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("service-run", aliases=["run"], help="run multiple commands",
 | 
			
		||||
                                 description="run multiple commands on success run of the previous command",
 | 
			
		||||
                                 epilog="Commands must be quoted by using usual bash rules. Processes will be spawned "
 | 
			
		||||
                                        "under the same user as this command.")
 | 
			
		||||
        parser.add_argument("command", help="command to be run (quoted) without ``ahriman``", nargs="+")
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def run_command(command: list[str], parser: argparse.ArgumentParser) -> bool:
 | 
			
		||||
        """
 | 
			
		||||
@ -64,3 +83,5 @@ class Run(Handler):
 | 
			
		||||
        args = parser.parse_args(command)
 | 
			
		||||
        handler: Handler = args.handler
 | 
			
		||||
        return handler.execute(args) == 0
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_service_run]
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ import argparse
 | 
			
		||||
from collections.abc import Callable, Iterable
 | 
			
		||||
from dataclasses import fields
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.alpm.remote import AUR, Official
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.exceptions import OptionError
 | 
			
		||||
@ -68,6 +68,33 @@ class Search(Handler):
 | 
			
		||||
            for package in Search.sort(packages_list, args.sort_by):
 | 
			
		||||
                AurPrinter(package)(verbose=args.info)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for AUR search subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("aur-search", aliases=["search"], help="search for package",
 | 
			
		||||
                                 description="search for package in AUR using API")
 | 
			
		||||
        parser.add_argument("search",
 | 
			
		||||
                            help="search terms, can be specified multiple times, the result will match all terms",
 | 
			
		||||
                            nargs="+")
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("--info", help="show additional package information",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=False)
 | 
			
		||||
        parser.add_argument("--sort-by",
 | 
			
		||||
                            help="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",
 | 
			
		||||
                            default="name", choices=sorted(Search.SORT_FIELDS))
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def sort(packages: Iterable[AURPackage], sort_by: str) -> list[AURPackage]:
 | 
			
		||||
        """
 | 
			
		||||
@ -90,3 +117,5 @@ class Search(Handler):
 | 
			
		||||
        comparator: Callable[[AURPackage], tuple[str, str]] =\
 | 
			
		||||
            lambda package: (getattr(package, sort_by), package.name)
 | 
			
		||||
        return sorted(packages, key=comparator)
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_aur_search_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman import __version__
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import UpdatePrinter
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
@ -58,3 +58,23 @@ class ServiceUpdates(Handler):
 | 
			
		||||
 | 
			
		||||
        UpdatePrinter(remote, local_version)(verbose=True, separator=" -> ")
 | 
			
		||||
        ServiceUpdates.check_status(args.exit_code, same_version)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_help_updates_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for service update check subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("help-updates", help="check for service updates",
 | 
			
		||||
                                 description="request AUR for current version and compare with current service version")
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit code if updates available",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_help_updates_parser]
 | 
			
		||||
 | 
			
		||||
@ -24,11 +24,13 @@ from pwd import getpwuid
 | 
			
		||||
from urllib.parse import quote_plus as urlencode
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.exceptions import MissingArchitectureError
 | 
			
		||||
from ahriman.core.utils import enum_values
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
from ahriman.models.repository_paths import RepositoryPaths
 | 
			
		||||
from ahriman.models.sign_settings import SignSettings
 | 
			
		||||
from ahriman.models.user import User
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -80,6 +82,44 @@ class Setup(Handler):
 | 
			
		||||
        # lazy database sync
 | 
			
		||||
        application.repository.pacman.handle  # pylint: disable=pointless-statement
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_service_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for setup subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("service-setup", aliases=["init", "repo-init", "repo-setup", "setup"],
 | 
			
		||||
                                 help="initial service configuration",
 | 
			
		||||
                                 description="create initial service configuration, requires root",
 | 
			
		||||
                                 epilog="Create minimal configuration for the service according to provided options.")
 | 
			
		||||
        parser.add_argument("--build-as-user", help="force makepkg user to the specific one")
 | 
			
		||||
        parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
 | 
			
		||||
                            type=Path,
 | 
			
		||||
                            default=Path("/") / "usr" / "share" / "devtools" / "pacman.conf.d" / "extra.conf")
 | 
			
		||||
        parser.add_argument("--generate-salt", help="generate salt for user passwords",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=False)
 | 
			
		||||
        parser.add_argument("--makeflags-jobs",
 | 
			
		||||
                            help="append MAKEFLAGS variable with parallelism set to number of cores",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--mirror", help="use the specified explicitly mirror instead of including mirrorlist")
 | 
			
		||||
        parser.add_argument("--multilib", help="add or do not multilib repository",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--packager", help="packager name and email", required=True)
 | 
			
		||||
        parser.add_argument("--server", help="server to be used for devtools. If none set, local files will be used")
 | 
			
		||||
        parser.add_argument("--sign-key", help="sign key id")
 | 
			
		||||
        parser.add_argument("--sign-target", help="sign options", action="append",
 | 
			
		||||
                            type=SignSettings.from_option, choices=enum_values(SignSettings))
 | 
			
		||||
        parser.add_argument("--web-port", help="port of the web service", type=int)
 | 
			
		||||
        parser.add_argument("--web-unix-socket", help="path to unix socket used for interprocess communications",
 | 
			
		||||
                            type=Path)
 | 
			
		||||
        parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def build_command(root: Path, repository_id: RepositoryId) -> Path:
 | 
			
		||||
        """
 | 
			
		||||
@ -240,3 +280,5 @@ class Setup(Handler):
 | 
			
		||||
        command.unlink(missing_ok=True)
 | 
			
		||||
        command.symlink_to(Setup.ARCHBUILD_COMMAND_PATH)
 | 
			
		||||
        paths.chown(command)  # we would like to keep owner inside ahriman's home
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_service_setup_parser]
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ import sys
 | 
			
		||||
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import StringPrinter
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
@ -63,3 +63,23 @@ class Shell(Handler):
 | 
			
		||||
            code.interact(local=local_variables)
 | 
			
		||||
        else:
 | 
			
		||||
            code.InteractiveConsole(locals=local_variables).runcode(args.code)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_service_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for shell subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("service-shell", aliases=["shell"], help="invoke python shell",
 | 
			
		||||
                                 description="drop into python shell")
 | 
			
		||||
        parser.add_argument("code", help="instead of dropping into shell, just execute the specified code", nargs="?")
 | 
			
		||||
        parser.add_argument("-v", "--verbose", help=argparse.SUPPRESS, action="store_true")
 | 
			
		||||
        parser.set_defaults(lock=None, report=False)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_service_shell_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
@ -43,3 +43,22 @@ class Sign(Handler):
 | 
			
		||||
            report(bool): force enable or disable reporting
 | 
			
		||||
        """
 | 
			
		||||
        Application(repository_id, configuration, report=report).sign(args.package)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_sign_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for sign subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-sign", aliases=["sign"], help="sign packages",
 | 
			
		||||
                                 description="(re-)sign packages and repository database according to current settings",
 | 
			
		||||
                                 epilog="Sign repository and/or packages as configured.")
 | 
			
		||||
        parser.add_argument("package", help="sign only specified packages", nargs="*")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_repo_sign_parser]
 | 
			
		||||
 | 
			
		||||
@ -25,11 +25,11 @@ from collections.abc import Callable
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import EventStatsPrinter, PackageStatsPrinter
 | 
			
		||||
from ahriman.core.utils import pretty_datetime
 | 
			
		||||
from ahriman.models.event import Event
 | 
			
		||||
from ahriman.core.utils import enum_values, pretty_datetime
 | 
			
		||||
from ahriman.models.event import Event, EventType
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -68,6 +68,30 @@ class Statistics(Handler):
 | 
			
		||||
            case _:
 | 
			
		||||
                Statistics.stats_for_package(args.event, events, args.chart)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_statistics_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository statistics subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-statistics", help="repository statistics",
 | 
			
		||||
                                 description="fetch repository statistics")
 | 
			
		||||
        parser.add_argument("package", help="fetch only events for the specified package", nargs="?")
 | 
			
		||||
        parser.add_argument("--chart", help="create updates chart and save it to the specified path", type=Path)
 | 
			
		||||
        parser.add_argument("-e", "--event", help="event type filter",
 | 
			
		||||
                            type=EventType, choices=enum_values(EventType), default=EventType.PackageUpdated)
 | 
			
		||||
        parser.add_argument("--from-date", help="only fetch events which are newer than the date")
 | 
			
		||||
        parser.add_argument("--limit", help="limit response by specified amount of events", type=int, default=-1)
 | 
			
		||||
        parser.add_argument("--offset", help="skip specified amount of events", type=int, default=0)
 | 
			
		||||
        parser.add_argument("--to-date", help="only fetch events which are older than the date")
 | 
			
		||||
        parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def event_stats(event_type: str, events: list[Event]) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@ -168,3 +192,5 @@ class Statistics(Handler):
 | 
			
		||||
        # chart if enabled
 | 
			
		||||
        if chart_path is not None:
 | 
			
		||||
            Statistics.plot_packages(event_type, by_object_id, chart_path)
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_repo_statistics_parser]
 | 
			
		||||
 | 
			
		||||
@ -22,10 +22,11 @@ import argparse
 | 
			
		||||
from collections.abc import Callable
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import PackagePrinter, StatusPrinter
 | 
			
		||||
from ahriman.models.build_status import BuildStatus
 | 
			
		||||
from ahriman.core.utils import enum_values
 | 
			
		||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
@ -68,3 +69,31 @@ class Status(Handler):
 | 
			
		||||
            lambda item: args.status is None or item[1].status == args.status
 | 
			
		||||
        for package, package_status in sorted(filter(filter_fn, packages), key=comparator):
 | 
			
		||||
            PackagePrinter(package, package_status)(verbose=args.info)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_package_status_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for package status subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("package-status", aliases=["status"], help="get package status",
 | 
			
		||||
                                 description="request status of the package",
 | 
			
		||||
                                 epilog="This command requests package status from the web interface "
 | 
			
		||||
                                        "if it is available.")
 | 
			
		||||
        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("--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(lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_package_status_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,9 +20,11 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.utils import enum_values
 | 
			
		||||
from ahriman.models.action import Action
 | 
			
		||||
from ahriman.models.build_status import BuildStatusEnum
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -59,3 +61,67 @@ class StatusUpdate(Handler):
 | 
			
		||||
            case Action.Remove:
 | 
			
		||||
                for package in args.package:
 | 
			
		||||
                    client.package_remove(package)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_package_status_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for package status remove subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("package-status-remove", help="remove package status",
 | 
			
		||||
                                 description="remove the package from the status page",
 | 
			
		||||
                                 epilog="Please note that this subcommand does not remove the package itself, it just "
 | 
			
		||||
                                        "clears the status page.")
 | 
			
		||||
        parser.add_argument("package", help="remove specified packages from status page", nargs="+")
 | 
			
		||||
        parser.set_defaults(action=Action.Remove, lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_package_status_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for package status update subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("package-status-update", aliases=["status-update"], help="update package status",
 | 
			
		||||
                                 description="update package status on the status page")
 | 
			
		||||
        parser.add_argument("package", help="set status for specified packages. "
 | 
			
		||||
                                            "If no packages supplied, service status will be updated",
 | 
			
		||||
                            nargs="*")
 | 
			
		||||
        parser.add_argument("-s", "--status", help="new package build status",
 | 
			
		||||
                            type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
 | 
			
		||||
        parser.set_defaults(action=Action.Update, lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_status_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository status update subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-status-update", help="update repository status",
 | 
			
		||||
                                 description="update repository status on the status page")
 | 
			
		||||
        parser.add_argument("-s", "--status", help="new status",
 | 
			
		||||
                            type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
 | 
			
		||||
        parser.set_defaults(action=Action.Update, lock=None, package=[], quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [
 | 
			
		||||
        _set_package_status_remove_parser,
 | 
			
		||||
        _set_package_status_update_parser,
 | 
			
		||||
        _set_repo_status_update_parser,
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import StringPrinter, TreePrinter
 | 
			
		||||
from ahriman.core.tree import Tree
 | 
			
		||||
@ -58,3 +58,23 @@ class Structure(Handler):
 | 
			
		||||
 | 
			
		||||
            # empty line
 | 
			
		||||
            StringPrinter("")(verbose=False)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    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")
 | 
			
		||||
        parser.add_argument("-p", "--partitions", help="also divide packages by independent partitions",
 | 
			
		||||
                            type=int, default=1)
 | 
			
		||||
        parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_repo_tree_parser]
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@
 | 
			
		||||
#
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
from ahriman.models.repository_paths import RepositoryPaths
 | 
			
		||||
@ -50,6 +50,22 @@ class TreeMigrate(Handler):
 | 
			
		||||
        # perform migration
 | 
			
		||||
        TreeMigrate.tree_move(current_tree, target_tree)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_service_tree_migrate_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for tree migration subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("service-tree-migrate", help="migrate repository tree",
 | 
			
		||||
                                 description="migrate repository tree between versions")
 | 
			
		||||
        parser.set_defaults(lock=None, quiet=True, report=False)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def tree_move(from_tree: RepositoryPaths, to_tree: RepositoryPaths) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@ -66,3 +82,5 @@ class TreeMigrate(Handler):
 | 
			
		||||
            RepositoryPaths.repository,
 | 
			
		||||
        ):
 | 
			
		||||
            attribute.fget(from_tree).rename(attribute.fget(to_tree))  # type: ignore[attr-defined]
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_service_tree_migrate_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
from ahriman.models.result import Result
 | 
			
		||||
@ -49,3 +49,60 @@ class Triggers(Handler):
 | 
			
		||||
            loader.triggers = [loader.load_trigger(trigger, repository_id, configuration) for trigger in args.trigger]
 | 
			
		||||
        application.on_start()
 | 
			
		||||
        application.on_result(Result())
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_report_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for report subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-report", aliases=["report"], help="generate report",
 | 
			
		||||
                                 description="generate repository report according to current settings",
 | 
			
		||||
                                 epilog="Create and/or update repository report as configured.")
 | 
			
		||||
        parser.set_defaults(trigger=["ahriman.core.report.ReportTrigger"])
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_sync_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository sync subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-sync", aliases=["sync"], help="sync repository",
 | 
			
		||||
                                 description="sync repository files to remote server according to current settings",
 | 
			
		||||
                                 epilog="Synchronize the repository to remote services as configured.")
 | 
			
		||||
        parser.set_defaults(trigger=["ahriman.core.upload.UploadTrigger"])
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_triggers_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository triggers subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-triggers", help="run triggers",
 | 
			
		||||
                                 description="run triggers on empty build result as configured by settings")
 | 
			
		||||
        parser.add_argument("trigger", help="instead of running all triggers as set by configuration, just process "
 | 
			
		||||
                                            "specified ones in order of mention", nargs="*")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [
 | 
			
		||||
        _set_repo_report_parser,
 | 
			
		||||
        _set_repo_sync_parser,
 | 
			
		||||
        _set_repo_triggers_parser,
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										70
									
								
								src/ahriman/application/handlers/triggers_support.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/ahriman/application/handlers/triggers_support.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2021-2024 ahriman team.
 | 
			
		||||
#
 | 
			
		||||
# This file is part of ahriman
 | 
			
		||||
# (see https://github.com/arcan1s/ahriman).
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import SubParserAction
 | 
			
		||||
from ahriman.application.handlers.triggers import Triggers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TriggersSupport(Triggers):
 | 
			
		||||
    """
 | 
			
		||||
    additional triggers handlers for support commands
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_create_keyring_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for create-keyring subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-create-keyring", help="create keyring package",
 | 
			
		||||
                                 description="create package which contains list of trusted keys as set by "
 | 
			
		||||
                                             "configuration. Note, that this action will only create package, "
 | 
			
		||||
                                             "the package itself has to be built manually")
 | 
			
		||||
        parser.set_defaults(trigger=["ahriman.core.support.KeyringTrigger"])
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_create_mirrorlist_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for create-mirrorlist subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-create-mirrorlist", help="create mirrorlist package",
 | 
			
		||||
                                 description="create package which contains list of available mirrors as set by "
 | 
			
		||||
                                             "configuration. Note, that this action will only create package, "
 | 
			
		||||
                                             "the package itself has to be built manually")
 | 
			
		||||
        parser.set_defaults(trigger=["ahriman.core.support.MirrorlistTrigger"])
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    arguments = [
 | 
			
		||||
        _set_repo_create_keyring_parser,
 | 
			
		||||
        _set_repo_create_mirrorlist_parser,
 | 
			
		||||
    ]
 | 
			
		||||
@ -19,7 +19,7 @@
 | 
			
		||||
#
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import StringPrinter
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
@ -52,6 +52,25 @@ class UnsafeCommands(Handler):
 | 
			
		||||
            for command in unsafe_commands:
 | 
			
		||||
                StringPrinter(command)(verbose=True)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for listing unsafe commands
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("help-commands-unsafe", help="list unsafe commands",
 | 
			
		||||
                                 description="list unsafe commands as defined in default args")
 | 
			
		||||
        parser.add_argument("subcommand",
 | 
			
		||||
                            help="instead of showing commands, just test command line for unsafe subcommand "
 | 
			
		||||
                                 "and return 0 in case if command is safe and 1 otherwise", nargs="*")
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def check_unsafe(command: list[str], unsafe_commands: list[str], parser: argparse.ArgumentParser) -> None:
 | 
			
		||||
        """
 | 
			
		||||
@ -81,3 +100,5 @@ class UnsafeCommands(Handler):
 | 
			
		||||
        subparser = next((action for action in parser._actions if isinstance(action, argparse._SubParsersAction)), None)
 | 
			
		||||
        actions = subparser.choices if subparser is not None else {}
 | 
			
		||||
        return sorted(action_name for action_name, action in actions.items() if action.get_default("unsafe"))
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_help_commands_unsafe_parser]
 | 
			
		||||
 | 
			
		||||
@ -22,8 +22,9 @@ import argparse
 | 
			
		||||
from collections.abc import Callable
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.utils import extract_user
 | 
			
		||||
from ahriman.models.packagers import Packagers
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
 | 
			
		||||
@ -64,6 +65,78 @@ class Update(Handler):
 | 
			
		||||
        result = application.update(packages, packagers, bump_pkgrel=args.increment)
 | 
			
		||||
        Update.check_status(args.exit_code, not result.is_empty)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository check subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-check", aliases=["check"], help="check for updates",
 | 
			
		||||
                                 description="check for packages updates. Same as repo-update --dry-run --no-manual")
 | 
			
		||||
        parser.add_argument("package", help="filter check by package base", nargs="*")
 | 
			
		||||
        parser.add_argument("--changes", help="calculate changes from the latest known commit if available",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
 | 
			
		||||
                                                  "(e.g. dynamically linked libraries or modules directories)",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("--vcs", help="fetch actual version of VCS packages",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
 | 
			
		||||
                                                    "-yy to force refresh even if up to date",
 | 
			
		||||
                            action="count", default=False)
 | 
			
		||||
        parser.set_defaults(aur=True, dependencies=False, dry_run=True, increment=False, local=True, manual=False,
 | 
			
		||||
                            username=None)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for repository update subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("repo-update", aliases=["update"], help="update packages",
 | 
			
		||||
                                 description="check for packages updates and run build process if requested")
 | 
			
		||||
        parser.add_argument("package", help="filter check by package base", nargs="*")
 | 
			
		||||
        parser.add_argument("--aur", help="enable or disable checking for AUR updates",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
 | 
			
		||||
                                              "Only applicable in dry run mode",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
 | 
			
		||||
                                                  "(e.g. dynamically linked libraries or modules directories)",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--dependencies", help="process missing package dependencies",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--dry-run", help="just perform check for updates, same as check command",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("--increment", help="increment package release (pkgrel) on duplicate",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--local", help="enable or disable checking of local packages for updates",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("--manual", help="include or exclude manual updates",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("-u", "--username", help="build as user", default=extract_user())
 | 
			
		||||
        parser.add_argument("--vcs", help="fetch actual version of VCS packages",
 | 
			
		||||
                            action=argparse.BooleanOptionalAction, default=True)
 | 
			
		||||
        parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
 | 
			
		||||
                                                    "-yy to force refresh even if up to date",
 | 
			
		||||
                            action="count", default=False)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def log_fn(application: Application, dry_run: bool) -> Callable[[str], None]:
 | 
			
		||||
        """
 | 
			
		||||
@ -79,3 +152,8 @@ class Update(Handler):
 | 
			
		||||
        def inner(line: str) -> None:
 | 
			
		||||
            return print(line) if dry_run else application.logger.info(line)  # pylint: disable=bad-builtin
 | 
			
		||||
        return inner
 | 
			
		||||
 | 
			
		||||
    arguments = [
 | 
			
		||||
        _set_repo_check_parser,
 | 
			
		||||
        _set_repo_update_parser,
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -20,14 +20,16 @@
 | 
			
		||||
import argparse
 | 
			
		||||
import getpass
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.database import SQLite
 | 
			
		||||
from ahriman.core.exceptions import PasswordError
 | 
			
		||||
from ahriman.core.formatters import UserPrinter
 | 
			
		||||
from ahriman.core.utils import enum_values
 | 
			
		||||
from ahriman.models.action import Action
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
from ahriman.models.user import User
 | 
			
		||||
from ahriman.models.user_access import UserAccess
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Users(Handler):
 | 
			
		||||
@ -65,6 +67,73 @@ class Users(Handler):
 | 
			
		||||
            case Action.Remove:
 | 
			
		||||
                database.user_remove(args.username)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for create user subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("user-add", help="create or update user",
 | 
			
		||||
                                 description="update user for web services with the given password and role. "
 | 
			
		||||
                                             "In case if password was not entered it will be asked interactively")
 | 
			
		||||
        parser.add_argument("username", help="username for web service")
 | 
			
		||||
        parser.add_argument("--key", help="optional PGP key used by this user. The private key must be imported")
 | 
			
		||||
        parser.add_argument("--packager", help="optional packager id used for build process in form of "
 | 
			
		||||
                                               "`Name Surname <mail@example.com>`")
 | 
			
		||||
        parser.add_argument(
 | 
			
		||||
            "-p", "--password", help="user password. Blank password will be treated as empty password, "
 | 
			
		||||
            "which is in particular must be used for OAuth2 authorization type.")
 | 
			
		||||
        parser.add_argument("-R", "--role", help="user access level",
 | 
			
		||||
                            type=UserAccess, choices=enum_values(UserAccess), default=UserAccess.Read)
 | 
			
		||||
        parser.set_defaults(action=Action.Update, architecture="", exit_code=False, lock=None, quiet=True,
 | 
			
		||||
                            report=False, repository="")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for user list subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("user-list", help="user known users and their access",
 | 
			
		||||
                                 description="list users from the user mapping and their roles")
 | 
			
		||||
        parser.add_argument("username", help="filter users by username", nargs="?")
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.add_argument("-R", "--role", help="filter users by role", type=UserAccess,
 | 
			
		||||
                            choices=enum_values(UserAccess))
 | 
			
		||||
        parser.set_defaults(action=Action.List, architecture="", lock=None, quiet=True, report=False, repository="",
 | 
			
		||||
                            unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_user_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for user removal subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("user-remove", help="remove user",
 | 
			
		||||
                                 description="remove user from the user mapping and update the configuration")
 | 
			
		||||
        parser.add_argument("username", help="username for web service")
 | 
			
		||||
        parser.set_defaults(action=Action.Remove, architecture="", exit_code=False, lock=None, quiet=True,
 | 
			
		||||
                            report=False, repository="")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def user_create(args: argparse.Namespace) -> User:
 | 
			
		||||
        """
 | 
			
		||||
@ -92,3 +161,9 @@ class Users(Handler):
 | 
			
		||||
 | 
			
		||||
        return User(username=args.username, password=password, access=args.role,
 | 
			
		||||
                    packager_id=args.packager, key=args.key)
 | 
			
		||||
 | 
			
		||||
    arguments = [
 | 
			
		||||
        _set_user_add_parser,
 | 
			
		||||
        _set_user_list_parser,
 | 
			
		||||
        _set_user_remove_parser,
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ import copy
 | 
			
		||||
 | 
			
		||||
from typing import Any
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.configuration.schema import CONFIGURATION_SCHEMA, ConfigurationSchema
 | 
			
		||||
from ahriman.core.exceptions import ExtensionError
 | 
			
		||||
@ -63,6 +63,25 @@ class Validate(Handler):
 | 
			
		||||
        # as we reach this part it means that we always have errors
 | 
			
		||||
        Validate.check_status(args.exit_code, False)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_service_config_validate_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for config validation subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("service-config-validate", aliases=["config-validate", "repo-config-validate"],
 | 
			
		||||
                                 help="validate system configuration",
 | 
			
		||||
                                 description="validate configuration and print found errors")
 | 
			
		||||
        parser.add_argument("-e", "--exit-code", help="return non-zero exit status if configuration is invalid",
 | 
			
		||||
                            action="store_true")
 | 
			
		||||
        parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def schema(repository_id: RepositoryId, configuration: Configuration) -> ConfigurationSchema:
 | 
			
		||||
        """
 | 
			
		||||
@ -136,3 +155,5 @@ class Validate(Handler):
 | 
			
		||||
                Validate.schema_merge(value, schema[key])
 | 
			
		||||
 | 
			
		||||
        return schema
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_service_config_validate_parser]
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ from collections.abc import Generator
 | 
			
		||||
from importlib import metadata
 | 
			
		||||
 | 
			
		||||
from ahriman import __version__
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.formatters import VersionPrinter
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
@ -59,6 +59,22 @@ class Versions(Handler):
 | 
			
		||||
        packages = Versions.package_dependencies("ahriman")
 | 
			
		||||
        VersionPrinter("Installed packages", dict(packages))(verbose=False, separator=" ")
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_help_version_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for version subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("help-version", aliases=["version"], help="application version",
 | 
			
		||||
                                 description="print application and its dependencies versions")
 | 
			
		||||
        parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def package_dependencies(root: str) -> Generator[tuple[str, str], None, None]:
 | 
			
		||||
        """
 | 
			
		||||
@ -96,3 +112,5 @@ class Versions(Handler):
 | 
			
		||||
                yield distribution.name, distribution.version
 | 
			
		||||
            except metadata.PackageNotFoundError:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_help_version_parser]
 | 
			
		||||
 | 
			
		||||
@ -20,8 +20,9 @@
 | 
			
		||||
import argparse
 | 
			
		||||
 | 
			
		||||
from collections.abc import Generator
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler, SubParserAction
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.spawn import Spawn
 | 
			
		||||
from ahriman.core.triggers import TriggerLoader
 | 
			
		||||
@ -71,6 +72,21 @@ class Web(Handler):
 | 
			
		||||
        spawner.stop()
 | 
			
		||||
        spawner.join()
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
 | 
			
		||||
        """
 | 
			
		||||
        add parser for web subcommand
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            root(SubParserAction): subparsers for the commands
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            argparse.ArgumentParser: created argument parser
 | 
			
		||||
        """
 | 
			
		||||
        parser = root.add_parser("web", help="web server", description="start web server")
 | 
			
		||||
        parser.set_defaults(architecture="", lock=Path("ahriman-web.pid"), report=False, repository="")
 | 
			
		||||
        return parser
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def extract_arguments(args: argparse.Namespace, configuration: Configuration) -> Generator[str, None, None]:
 | 
			
		||||
        """
 | 
			
		||||
@ -100,3 +116,5 @@ class Web(Handler):
 | 
			
		||||
        # arguments from configuration
 | 
			
		||||
        if (wait_timeout := configuration.getint("web", "wait_timeout", fallback=None)) is not None:
 | 
			
		||||
            yield from ["--wait-timeout", str(wait_timeout)]
 | 
			
		||||
 | 
			
		||||
    arguments = [_set_web_parser]
 | 
			
		||||
 | 
			
		||||
@ -17,14 +17,14 @@
 | 
			
		||||
# 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 Any
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import aiohttp_security
 | 
			
		||||
    _has_aiohttp_security = True
 | 
			
		||||
except ImportError:
 | 
			
		||||
    _has_aiohttp_security = False
 | 
			
		||||
 | 
			
		||||
from typing import Any
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["authorized_userid", "check_authorized", "forget", "remember"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,8 @@
 | 
			
		||||
# 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 logging import NullHandler  # pylint: disable=imports-out-of-order
 | 
			
		||||
# pylint: disable=imports-out-of-order
 | 
			
		||||
from logging import NullHandler
 | 
			
		||||
from typing import Any
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										78
									
								
								src/ahriman/core/module_loader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/ahriman/core/module_loader.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,78 @@
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2021-2024 ahriman team.
 | 
			
		||||
#
 | 
			
		||||
# This file is part of ahriman
 | 
			
		||||
# (see https://github.com/arcan1s/ahriman).
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
import inspect
 | 
			
		||||
 | 
			
		||||
from collections.abc import Generator
 | 
			
		||||
from importlib import import_module
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from pkgutil import ModuleInfo, walk_packages
 | 
			
		||||
from types import ModuleType
 | 
			
		||||
from typing import Any, TypeGuard, TypeVar
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["implementations"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
T = TypeVar("T")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _modules(module_root: Path, prefix: str) -> Generator[ModuleInfo, None, None]:
 | 
			
		||||
    """
 | 
			
		||||
    extract available modules from package
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        module_root(Path): module root path
 | 
			
		||||
        prefix(str): modules package prefix
 | 
			
		||||
 | 
			
		||||
    Yields:
 | 
			
		||||
        ModuleInfo: module information each available module
 | 
			
		||||
    """
 | 
			
		||||
    prefix = f"{prefix}." if prefix else ""
 | 
			
		||||
    for module_info in walk_packages([str(module_root)], prefix):
 | 
			
		||||
        if module_info.ispkg:
 | 
			
		||||
            yield from _modules(module_root / module_info.name, prefix)
 | 
			
		||||
        else:
 | 
			
		||||
            yield module_info
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def implementations(root_module: ModuleType, base_class: type[T]) -> Generator[type[T], None, None]:
 | 
			
		||||
    """
 | 
			
		||||
    extract implementations of the ``base_class`` from the module
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        root_module(ModuleType): root module
 | 
			
		||||
        base_class(type[T]): base class type
 | 
			
		||||
 | 
			
		||||
    Yields:
 | 
			
		||||
        type[T]: found implementations
 | 
			
		||||
    """
 | 
			
		||||
    def is_base_class(clazz: Any) -> TypeGuard[type[T]]:
 | 
			
		||||
        return inspect.isclass(clazz) \
 | 
			
		||||
            and issubclass(clazz, base_class) and clazz != base_class \
 | 
			
		||||
            and clazz.__module__ == module.__name__
 | 
			
		||||
 | 
			
		||||
    prefix = root_module.__name__
 | 
			
		||||
 | 
			
		||||
    for module_root in root_module.__path__:
 | 
			
		||||
        for module_info in _modules(Path(module_root), prefix):
 | 
			
		||||
            module = import_module(module_info.name)
 | 
			
		||||
 | 
			
		||||
            for _, attribute in inspect.getmembers(module, is_base_class):
 | 
			
		||||
                yield attribute
 | 
			
		||||
@ -461,7 +461,7 @@ def trim_package(package_name: str) -> str:
 | 
			
		||||
        str: package name without description or version bound
 | 
			
		||||
    """
 | 
			
		||||
    for symbol in ("<", "=", ">", ":"):
 | 
			
		||||
        package_name = package_name.partition(symbol)[0]
 | 
			
		||||
        package_name, *_ = package_name.split(symbol, maxsplit=1)
 | 
			
		||||
    return package_name
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -478,7 +478,6 @@ def utcnow() -> datetime.datetime:
 | 
			
		||||
def walk(directory_path: Path) -> Generator[Path, None, None]:
 | 
			
		||||
    """
 | 
			
		||||
    list all file paths in given directory
 | 
			
		||||
    Credits to https://stackoverflow.com/a/64915960
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        directory_path(Path): root directory path
 | 
			
		||||
@ -487,18 +486,13 @@ def walk(directory_path: Path) -> Generator[Path, None, None]:
 | 
			
		||||
        Path: all found files in given directory with full path
 | 
			
		||||
 | 
			
		||||
    Examples:
 | 
			
		||||
        Since the :mod:`pathlib` module does not provide an alternative to :func:`os.walk()`, this wrapper
 | 
			
		||||
        can be used instead::
 | 
			
		||||
        Wrapper around :func:`pathlib.Path.walk`, which yields only files instead::
 | 
			
		||||
 | 
			
		||||
            >>> from pathlib import Path
 | 
			
		||||
            >>>
 | 
			
		||||
            >>> for file_path in walk(Path.cwd()):
 | 
			
		||||
            >>>     print(file_path)
 | 
			
		||||
 | 
			
		||||
        Note, however, that unlike the original method, it does not yield directories.
 | 
			
		||||
    """
 | 
			
		||||
    for element in directory_path.iterdir():
 | 
			
		||||
        if element.is_dir():
 | 
			
		||||
            yield from walk(element)
 | 
			
		||||
            continue
 | 
			
		||||
        yield element
 | 
			
		||||
    for root, _, files in directory_path.walk(follow_symlinks=True):
 | 
			
		||||
        for file in files:
 | 
			
		||||
            yield root / file
 | 
			
		||||
 | 
			
		||||
@ -18,93 +18,35 @@
 | 
			
		||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
from aiohttp.web import Application, View
 | 
			
		||||
from collections.abc import Generator
 | 
			
		||||
from importlib.machinery import SourceFileLoader
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from pkgutil import ModuleInfo, iter_modules
 | 
			
		||||
from types import ModuleType
 | 
			
		||||
from typing import Any, Type, TypeGuard
 | 
			
		||||
 | 
			
		||||
import ahriman.web.views
 | 
			
		||||
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.module_loader import implementations
 | 
			
		||||
from ahriman.web.views.base import BaseView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["setup_routes"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _dynamic_routes(module_root: Path, configuration: Configuration) -> dict[str, Type[View]]:
 | 
			
		||||
def _dynamic_routes(configuration: Configuration) -> dict[str, type[View]]:
 | 
			
		||||
    """
 | 
			
		||||
    extract dynamic routes based on views
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        module_root(Path): root module path with views
 | 
			
		||||
        configuration(Configuration): configuration instance
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        dict[str, Type[View]]: map of the route to its view
 | 
			
		||||
        dict[str, type[View]]: map of the route to its view
 | 
			
		||||
    """
 | 
			
		||||
    def is_base_view(clazz: Any) -> TypeGuard[Type[BaseView]]:
 | 
			
		||||
        return isinstance(clazz, type) and issubclass(clazz, BaseView)
 | 
			
		||||
 | 
			
		||||
    routes: dict[str, Type[View]] = {}
 | 
			
		||||
    for module_info in _modules(module_root):
 | 
			
		||||
        module = _module(module_info)
 | 
			
		||||
 | 
			
		||||
        for attribute_name in dir(module):
 | 
			
		||||
            view = getattr(module, attribute_name)
 | 
			
		||||
            if not is_base_view(view):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            view_routes = view.routes(configuration)
 | 
			
		||||
            routes.update([(route, view) for route in view_routes])
 | 
			
		||||
    routes: dict[str, type[View]] = {}
 | 
			
		||||
    for view in implementations(ahriman.web.views, BaseView):
 | 
			
		||||
        view_routes = view.routes(configuration)
 | 
			
		||||
        routes.update([(route, view) for route in view_routes])
 | 
			
		||||
 | 
			
		||||
    return routes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _module(module_info: ModuleInfo) -> ModuleType:
 | 
			
		||||
    """
 | 
			
		||||
    load module from its info
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        module_info(ModuleInfo): module info descriptor
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        ModuleType: loaded module
 | 
			
		||||
 | 
			
		||||
    Raises:
 | 
			
		||||
        ValueError: if loader is not an instance of :class:`importlib.machinery.SourceFileLoader`
 | 
			
		||||
    """
 | 
			
		||||
    module_spec = module_info.module_finder.find_spec(module_info.name, None)
 | 
			
		||||
    if module_spec is None:
 | 
			
		||||
        raise ValueError(f"Module specification of {module_info.name} is empty")
 | 
			
		||||
 | 
			
		||||
    loader = module_spec.loader
 | 
			
		||||
    if not isinstance(loader, SourceFileLoader):
 | 
			
		||||
        raise ValueError(f"Module {module_info.name} loader is not an instance of SourceFileLoader")
 | 
			
		||||
 | 
			
		||||
    module = ModuleType(loader.name)
 | 
			
		||||
    loader.exec_module(module)
 | 
			
		||||
 | 
			
		||||
    return module
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _modules(module_root: Path) -> Generator[ModuleInfo, None, None]:
 | 
			
		||||
    """
 | 
			
		||||
    extract available modules from package
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        module_root(Path): module root path
 | 
			
		||||
 | 
			
		||||
    Yields:
 | 
			
		||||
        ModuleInfo: module information each available module
 | 
			
		||||
    """
 | 
			
		||||
    for module_info in iter_modules([str(module_root)]):
 | 
			
		||||
        if module_info.ispkg:
 | 
			
		||||
            yield from _modules(module_root / module_info.name)
 | 
			
		||||
        else:
 | 
			
		||||
            yield module_info
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup_routes(application: Application, configuration: Configuration) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    setup all defined routes
 | 
			
		||||
@ -115,6 +57,5 @@ def setup_routes(application: Application, configuration: Configuration) -> None
 | 
			
		||||
    """
 | 
			
		||||
    application.router.add_static("/static", configuration.getpath("web", "static_path"), follow_symlinks=True)
 | 
			
		||||
 | 
			
		||||
    views_root = Path(__file__).parent / "views"
 | 
			
		||||
    for route, view in _dynamic_routes(views_root, configuration).items():
 | 
			
		||||
    for route, view in _dynamic_routes(configuration).items():
 | 
			
		||||
        application.router.add_view(route, view)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										133
									
								
								subpackages.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								subpackages.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,133 @@
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2021-2024 ahriman team.
 | 
			
		||||
#
 | 
			
		||||
# This file is part of ahriman
 | 
			
		||||
# (see https://github.com/arcan1s/ahriman).
 | 
			
		||||
#
 | 
			
		||||
# This program is free software: you can redistribute it and/or modify
 | 
			
		||||
# it under the terms of the GNU General Public License as published by
 | 
			
		||||
# the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
# (at your option) any later version.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful,
 | 
			
		||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
# GNU General Public License for more details.
 | 
			
		||||
#
 | 
			
		||||
# You should have received a copy of the GNU General Public License
 | 
			
		||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
#
 | 
			
		||||
import argparse
 | 
			
		||||
import shutil
 | 
			
		||||
import site
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
prefix = Path(sys.prefix).relative_to("/")
 | 
			
		||||
site_packages = Path(site.getsitepackages()[0]).relative_to("/")
 | 
			
		||||
SUBPACKAGES = {
 | 
			
		||||
    "ahriman-core": [
 | 
			
		||||
        prefix / "bin",
 | 
			
		||||
        prefix / "lib" / "systemd",
 | 
			
		||||
        prefix / "share",
 | 
			
		||||
        site_packages,
 | 
			
		||||
    ],
 | 
			
		||||
    "ahriman-triggers": [
 | 
			
		||||
        prefix / "share" / "ahriman" / "settings" / "ahriman.ini.d" / "00-triggers.ini",
 | 
			
		||||
        site_packages / "ahriman" / "application" / "handlers" / "triggers_support.py",
 | 
			
		||||
        site_packages / "ahriman" / "core" / "distributed",
 | 
			
		||||
        site_packages / "ahriman" / "core" / "support",
 | 
			
		||||
    ],
 | 
			
		||||
    "ahriman-web": [
 | 
			
		||||
        prefix / "lib" / "systemd" / "system" / "ahriman-web.service",
 | 
			
		||||
        prefix / "lib" / "systemd" / "system" / "ahriman-web@.service",
 | 
			
		||||
        prefix / "share" / "ahriman" / "settings" / "ahriman.ini.d" / "00-web.ini",
 | 
			
		||||
        prefix / "share" / "ahriman" / "templates" / "api.jinja2",
 | 
			
		||||
        prefix / "share" / "ahriman" / "templates" / "build-status",
 | 
			
		||||
        prefix / "share" / "ahriman" / "templates" / "build-status.jinja2",
 | 
			
		||||
        prefix / "share" / "ahriman" / "templates" / "error.jinja2",
 | 
			
		||||
        site_packages / "ahriman" / "application" / "handlers" / "web.py",
 | 
			
		||||
        site_packages / "ahriman" / "core" / "auth",
 | 
			
		||||
        site_packages / "ahriman" / "web",
 | 
			
		||||
    ],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def subpackages(root: Path) -> dict[str, list[Path]]:
 | 
			
		||||
    """
 | 
			
		||||
    extend list of subpackages
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        root(Path): root directory
 | 
			
		||||
 | 
			
		||||
    Returns:
 | 
			
		||||
        dict[str, list[Path]]: extended list of files which belong to a specific package
 | 
			
		||||
    """
 | 
			
		||||
    for package, paths in SUBPACKAGES.items():
 | 
			
		||||
        new_paths = []
 | 
			
		||||
        for path in paths:
 | 
			
		||||
            full_path = root / path
 | 
			
		||||
 | 
			
		||||
            match path.suffix:
 | 
			
		||||
                case ".py":
 | 
			
		||||
                    pycache_path = full_path.parent / "__pycache__"
 | 
			
		||||
                    new_paths.extend(
 | 
			
		||||
                        new_path.relative_to(root) for new_path in pycache_path.glob(f"{full_path.stem}.*.pyc")
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
        SUBPACKAGES[package].extend(new_paths)
 | 
			
		||||
 | 
			
		||||
    return SUBPACKAGES
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def process(root: Path, include: list[Path], exclude: list[Path]) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    remove files based on patterns
 | 
			
		||||
 | 
			
		||||
    Args:
 | 
			
		||||
        root(Path): root directory
 | 
			
		||||
        include(list[Path]): list of files to include to the subpackage
 | 
			
		||||
        exclude(list[Path]): list of files to exclude from the subpackage
 | 
			
		||||
    """
 | 
			
		||||
    for subdirectory, _, files in root.walk(top_down=False):
 | 
			
		||||
        for file in files:
 | 
			
		||||
            full_path = subdirectory / file
 | 
			
		||||
            relative_path = full_path.relative_to(root)
 | 
			
		||||
 | 
			
		||||
            if not any(relative_path.is_relative_to(path) for path in include):
 | 
			
		||||
                full_path.unlink()
 | 
			
		||||
            elif any(relative_path.is_relative_to(path) for path in exclude):
 | 
			
		||||
                full_path.unlink()
 | 
			
		||||
 | 
			
		||||
        content = list(subdirectory.iterdir())
 | 
			
		||||
        if not content:
 | 
			
		||||
            shutil.rmtree(subdirectory)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def run() -> None:
 | 
			
		||||
    """
 | 
			
		||||
    run application
 | 
			
		||||
    """
 | 
			
		||||
    parser = argparse.ArgumentParser(description="Split package into subpackages")
 | 
			
		||||
    parser.add_argument("root", help="package root", type=Path)
 | 
			
		||||
    parser.add_argument("subpackage", help="subpackage name", choices=SUBPACKAGES.keys())
 | 
			
		||||
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    full_subpackages = subpackages(args.root)
 | 
			
		||||
    include = full_subpackages[args.subpackage]
 | 
			
		||||
    exclude = [
 | 
			
		||||
        path
 | 
			
		||||
        for subpackage, portion in full_subpackages.items()
 | 
			
		||||
        if subpackage != args.subpackage
 | 
			
		||||
        for path in portion
 | 
			
		||||
        if not any(include_path.is_relative_to(path) for include_path in include)
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    process(args.root, include, exclude)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    run()
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError
 | 
			
		||||
from ahriman.models.log_handler import LogHandler
 | 
			
		||||
@ -19,7 +19,7 @@ def test_call(args: argparse.Namespace, configuration: Configuration, mocker: Mo
 | 
			
		||||
    args.log_handler = LogHandler.Console
 | 
			
		||||
    args.quiet = False
 | 
			
		||||
    args.report = False
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Handler.run")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.handler.Handler.run")
 | 
			
		||||
    configuration_mock = mocker.patch("ahriman.core.configuration.Configuration.from_path", return_value=configuration)
 | 
			
		||||
    log_handler_mock = mocker.patch("ahriman.core.log.log_loader.LogLoader.handler", return_value=args.log_handler)
 | 
			
		||||
    log_load_mock = mocker.patch("ahriman.core.log.log_loader.LogLoader.load")
 | 
			
		||||
@ -76,7 +76,7 @@ def test_execute(args: argparse.Namespace, mocker: MockerFixture) -> None:
 | 
			
		||||
        RepositoryId("i686", "aur"),
 | 
			
		||||
        RepositoryId("x86_64", "aur"),
 | 
			
		||||
    ]
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Handler.repositories_extract", return_value=ids)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=ids)
 | 
			
		||||
    starmap_mock = mocker.patch("multiprocessing.pool.Pool.starmap")
 | 
			
		||||
 | 
			
		||||
    Handler.execute(args)
 | 
			
		||||
@ -88,7 +88,7 @@ def test_execute_multiple_not_supported(args: argparse.Namespace, mocker: Mocker
 | 
			
		||||
    must raise an exception if multiple architectures are not supported by the handler
 | 
			
		||||
    """
 | 
			
		||||
    args.command = "web"
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Handler.repositories_extract", return_value=[
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=[
 | 
			
		||||
        RepositoryId("i686", "aur"),
 | 
			
		||||
        RepositoryId("x86_64", "aur"),
 | 
			
		||||
    ])
 | 
			
		||||
@ -102,7 +102,7 @@ def test_execute_single(args: argparse.Namespace, mocker: MockerFixture) -> None
 | 
			
		||||
    """
 | 
			
		||||
    must run execution in current process if only one architecture supplied
 | 
			
		||||
    """
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Handler.repositories_extract", return_value=[
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=[
 | 
			
		||||
        RepositoryId("x86_64", "aur"),
 | 
			
		||||
    ])
 | 
			
		||||
    starmap_mock = mocker.patch("multiprocessing.pool.Pool.starmap")
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import pytest
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Add
 | 
			
		||||
from ahriman.application.handlers.add import Add
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
@ -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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.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",
 | 
			
		||||
@ -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_status")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.handler.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,7 +138,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Add.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ from pathlib import Path
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import MagicMock
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Backup
 | 
			
		||||
from ahriman.application.handlers.backup import Backup
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_paths import RepositoryPaths
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
 | 
			
		||||
    must run command
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Backup.get_paths", return_value=[Path("path")])
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.backup.Backup.get_paths", return_value=[Path("path")])
 | 
			
		||||
    tarfile = MagicMock()
 | 
			
		||||
    add_mock = tarfile.__enter__.return_value = MagicMock()
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.backup.tarfile.open", return_value=tarfile)
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import pytest
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Change
 | 
			
		||||
from ahriman.application.handlers.change import Change
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.database import SQLite
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
@ -36,7 +36,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -55,7 +55,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Change.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import argparse
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Clean
 | 
			
		||||
from ahriman.application.handlers.clean import Clean
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers import Copy
 | 
			
		||||
from ahriman.application.handlers.copy import Copy
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.build_status import BuildStatusEnum
 | 
			
		||||
@ -37,7 +37,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    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.application.handlers.Copy.copy_package")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
 | 
			
		||||
    update_mock = mocker.patch("ahriman.application.application.Application.update")
 | 
			
		||||
    remove_mock = mocker.patch("ahriman.application.application.Application.remove")
 | 
			
		||||
    on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
 | 
			
		||||
@ -59,7 +59,7 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo
 | 
			
		||||
    args.remove = True
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Copy.copy_package")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
 | 
			
		||||
    mocker.patch("ahriman.application.application.Application.update")
 | 
			
		||||
    remove_mock = mocker.patch("ahriman.application.application.Application.remove")
 | 
			
		||||
 | 
			
		||||
@ -77,7 +77,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
 | 
			
		||||
    args.exit_code = True
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.packages", return_value=[])
 | 
			
		||||
    mocker.patch("ahriman.application.application.Application.update")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Copy.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import argparse
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Daemon
 | 
			
		||||
from ahriman.application.handlers.daemon import Daemon
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
@ -31,7 +31,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, package_ahr
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    run_mock = mocker.patch("ahriman.application.handlers.Update.run")
 | 
			
		||||
    run_mock = mocker.patch("ahriman.application.handlers.update.Update.run")
 | 
			
		||||
    iter_mock = mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.__iter__",
 | 
			
		||||
                             return_value=iter([[package_ahriman.base]]))
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,7 @@ def test_run_no_partitions(args: argparse.Namespace, configuration: Configuratio
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.partitions = False
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    run_mock = mocker.patch("ahriman.application.handlers.Update.run")
 | 
			
		||||
    run_mock = mocker.patch("ahriman.application.handlers.update.Update.run")
 | 
			
		||||
    iter_mock = mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.__iter__",
 | 
			
		||||
                             return_value=iter([[]]))
 | 
			
		||||
 | 
			
		||||
@ -67,7 +67,7 @@ def test_run_no_updates(args: argparse.Namespace, configuration: Configuration,
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    run_mock = mocker.patch("ahriman.application.handlers.Update.run")
 | 
			
		||||
    run_mock = mocker.patch("ahriman.application.handlers.update.Update.run")
 | 
			
		||||
    iter_mock = mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.__iter__",
 | 
			
		||||
                             return_value=iter([[package_ahriman.base], None]))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import pytest
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Dump
 | 
			
		||||
from ahriman.application.handlers.dump import Dump
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import argparse
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.ahriman import _parser
 | 
			
		||||
from ahriman.application.handlers import Help
 | 
			
		||||
from ahriman.application.handlers.help import Help
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import argparse
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import KeyImport
 | 
			
		||||
from ahriman.application.handlers.key_import import KeyImport
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ from pathlib import Path
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers import Patch
 | 
			
		||||
from ahriman.application.handlers.patch import Patch
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.action import Action
 | 
			
		||||
@ -40,9 +40,9 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.action = Action.Update
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    patch_mock = mocker.patch("ahriman.application.handlers.Patch.patch_create_from_diff",
 | 
			
		||||
    patch_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_create_from_diff",
 | 
			
		||||
                              return_value=(args.package, PkgbuildPatch(None, "patch")))
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_create")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_create")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Patch.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -61,8 +61,9 @@ def test_run_function(args: argparse.Namespace, configuration: Configuration, re
 | 
			
		||||
    args.variable = "version"
 | 
			
		||||
    patch = PkgbuildPatch(args.variable, args.patch)
 | 
			
		||||
    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")
 | 
			
		||||
    patch_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_create_from_function",
 | 
			
		||||
                              return_value=patch)
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_create")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Patch.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -79,7 +80,7 @@ def test_run_list(args: argparse.Namespace, configuration: Configuration, reposi
 | 
			
		||||
    args.action = Action.List
 | 
			
		||||
    args.variable = ["version"]
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_list")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_list")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Patch.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -95,7 +96,7 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo
 | 
			
		||||
    args.action = Action.Remove
 | 
			
		||||
    args.variable = ["version"]
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_remove")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_remove")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Patch.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -163,7 +164,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    Patch.patch_set_list(application, "ahriman", ["version"], False)
 | 
			
		||||
    get_mock.assert_called_once_with("ahriman", None)
 | 
			
		||||
@ -178,7 +179,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    Patch.patch_set_list(application, "ahriman", None, False)
 | 
			
		||||
    get_mock.assert_called_once_with("ahriman", None)
 | 
			
		||||
@ -191,7 +192,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    Patch.patch_set_list(application, "ahriman", [], True)
 | 
			
		||||
    check_mock.assert_called_once_with(True, [])
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers import Rebuild
 | 
			
		||||
from ahriman.application.handlers.rebuild import Rebuild
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
 | 
			
		||||
@ -43,11 +43,12 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
 | 
			
		||||
    result = Result()
 | 
			
		||||
    result.add_updated(package_ahriman)
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    extract_mock = mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[package_ahriman])
 | 
			
		||||
    extract_mock = mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages",
 | 
			
		||||
                                return_value=[package_ahriman])
 | 
			
		||||
    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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
    on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -70,7 +71,7 @@ def test_run_extract_packages(args: argparse.Namespace, configuration: Configura
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.application.Application.add")
 | 
			
		||||
    mocker.patch("ahriman.application.application.Application.print_updates")
 | 
			
		||||
    extract_mock = mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
 | 
			
		||||
    extract_mock = mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[])
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Rebuild.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -85,9 +86,9 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, rep
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.dry_run = True
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[package_ahriman])
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.rebuild.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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
    print_mock = mocker.patch("ahriman.application.application.Application.print_updates")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -106,7 +107,7 @@ def test_run_filter(args: argparse.Namespace, configuration: Configuration, repo
 | 
			
		||||
    args.depends_on = ["python-aur"]
 | 
			
		||||
    mocker.patch("ahriman.application.application.Application.update")
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[])
 | 
			
		||||
    application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -122,7 +123,7 @@ def test_run_without_filter(args: argparse.Namespace, configuration: Configurati
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    mocker.patch("ahriman.application.application.Application.update")
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[])
 | 
			
		||||
    application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -139,10 +140,10 @@ def test_run_update_empty_exception(args: argparse.Namespace, configuration: Con
 | 
			
		||||
    args.exit_code = True
 | 
			
		||||
    args.dry_run = True
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Rebuild.extract_packages")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.rebuild.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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Rebuild.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -157,10 +158,10 @@ def test_run_build_empty_exception(args: argparse.Namespace, configuration: Conf
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.exit_code = True
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Rebuild.extract_packages")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.rebuild.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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Rebuild.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import argparse
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Remove
 | 
			
		||||
from ahriman.application.handlers.remove import Remove
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import pytest
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import RemoveUnknown
 | 
			
		||||
from ahriman.application.handlers.remove_unknown import RemoveUnknown
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import pytest
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Repositories
 | 
			
		||||
from ahriman.application.handlers.repositories import Repositories
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.Handler.repositories_extract",
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract",
 | 
			
		||||
                                    return_value=[repository_id])
 | 
			
		||||
 | 
			
		||||
    Repositories.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ from pathlib import Path
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import MagicMock
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Restore
 | 
			
		||||
from ahriman.application.handlers.restore import Restore
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.ahriman import _parser
 | 
			
		||||
from ahriman.application.handlers import Run
 | 
			
		||||
from ahriman.application.handlers.run import Run
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.exceptions import ExitCode
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
 | 
			
		||||
    must run command
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.Run.run_command")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.run.Run.run_command")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Run.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -42,7 +42,7 @@ def test_run_failed(args: argparse.Namespace, configuration: Configuration, mock
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.command = ["help", "config"]
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.Run.run_command", return_value=False)
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.run.Run.run_command", return_value=False)
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    with pytest.raises(ExitCode):
 | 
			
		||||
@ -54,8 +54,13 @@ def test_run_command(mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must correctly run external command
 | 
			
		||||
    """
 | 
			
		||||
    execute_mock = mocker.patch("ahriman.application.handlers.Help.execute")
 | 
			
		||||
    Run.run_command(["help"], _parser())
 | 
			
		||||
    # because of dynamic load we need to patch exact instance of the object
 | 
			
		||||
    parser = _parser()
 | 
			
		||||
    subparser = next((action for action in parser._actions if isinstance(action, argparse._SubParsersAction)), None)
 | 
			
		||||
    action = subparser.choices["help"]
 | 
			
		||||
    execute_mock = mocker.patch.object(action.get_default("handler"), "execute")
 | 
			
		||||
 | 
			
		||||
    Run.run_command(["help"], parser)
 | 
			
		||||
    execute_mock.assert_called_once_with(pytest.helpers.anyvar(int))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ import pytest
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Search
 | 
			
		||||
from ahriman.application.handlers.search import Search
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.exceptions import OptionError
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
@ -39,7 +39,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -64,7 +64,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Search.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -80,7 +80,7 @@ def test_run_sort(args: argparse.Namespace, configuration: Configuration, reposi
 | 
			
		||||
    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.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    sort_mock = mocker.patch("ahriman.application.handlers.Search.sort")
 | 
			
		||||
    sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Search.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -100,7 +100,7 @@ def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, rep
 | 
			
		||||
    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.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    sort_mock = mocker.patch("ahriman.application.handlers.Search.sort")
 | 
			
		||||
    sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Search.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman import __version__
 | 
			
		||||
from ahriman.application.handlers import ServiceUpdates
 | 
			
		||||
from ahriman.application.handlers.service_updates import ServiceUpdates
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
@ -34,7 +34,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    ServiceUpdates.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    ServiceUpdates.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ from typing import Any
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
from urllib.parse import quote_plus as urlencode
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Setup
 | 
			
		||||
from ahriman.application.handlers.setup import Setup
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.exceptions import MissingArchitectureError
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
@ -50,11 +50,11 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    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")
 | 
			
		||||
    sudo_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_sudo")
 | 
			
		||||
    executable_mock = mocker.patch("ahriman.application.handlers.Setup.executable_create")
 | 
			
		||||
    ahriman_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
 | 
			
		||||
    devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools")
 | 
			
		||||
    makepkg_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
 | 
			
		||||
    sudo_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo")
 | 
			
		||||
    executable_mock = mocker.patch("ahriman.application.handlers.setup.Setup.executable_create")
 | 
			
		||||
    init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -95,12 +95,12 @@ def test_run_with_server(args: argparse.Namespace, configuration: Configuration,
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.server = "server"
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Setup.configuration_create_ahriman")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Setup.configuration_create_makepkg")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Setup.configuration_create_sudo")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Setup.executable_create")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.setup.Setup.executable_create")
 | 
			
		||||
    mocker.patch("ahriman.core.alpm.repo.Repo.init")
 | 
			
		||||
    devtools_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_devtools")
 | 
			
		||||
    devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Setup.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import pytest
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Shell
 | 
			
		||||
from ahriman.application.handlers.shell import Shell
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import argparse
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Sign
 | 
			
		||||
from ahriman.application.handlers.sign import Sign
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ from pathlib import Path
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Statistics
 | 
			
		||||
from ahriman.application.handlers.statistics import Statistics
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.core.utils import pretty_datetime, utcnow
 | 
			
		||||
@ -42,7 +42,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
 | 
			
		||||
    events = [Event("1", "1"), Event("2", "2")]
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=events)
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.Statistics.stats_per_package")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_per_package")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Statistics.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -60,7 +60,7 @@ def test_run_for_package(args: argparse.Namespace, configuration: Configuration,
 | 
			
		||||
    events = [Event("1", "1"), Event("2", "2")]
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=events)
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.Statistics.stats_for_package")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_for_package")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Statistics.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -77,7 +77,7 @@ def test_run_convert_from_date(args: argparse.Namespace, configuration: Configur
 | 
			
		||||
    date = utcnow()
 | 
			
		||||
    args.from_date = date.isoformat()
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Statistics.stats_per_package")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_per_package")
 | 
			
		||||
    events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=[])
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -94,7 +94,7 @@ def test_run_convert_to_date(args: argparse.Namespace, configuration: Configurat
 | 
			
		||||
    date = utcnow()
 | 
			
		||||
    args.to_date = date.isoformat()
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Statistics.stats_per_package")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_per_package")
 | 
			
		||||
    events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=[])
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -147,8 +147,8 @@ def test_stats_for_package(mocker: MockerFixture) -> None:
 | 
			
		||||
    must print statistics for the package
 | 
			
		||||
    """
 | 
			
		||||
    events = [Event("event", "1"), Event("event", "1")]
 | 
			
		||||
    events_mock = mocker.patch("ahriman.application.handlers.Statistics.event_stats")
 | 
			
		||||
    chart_plot = mocker.patch("ahriman.application.handlers.Statistics.plot_times")
 | 
			
		||||
    events_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
 | 
			
		||||
    chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_times")
 | 
			
		||||
 | 
			
		||||
    Statistics.stats_for_package("event", events, None)
 | 
			
		||||
    events_mock.assert_called_once_with("event", events)
 | 
			
		||||
@ -161,8 +161,8 @@ def test_stats_for_package_with_chart(mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    local = Path("local")
 | 
			
		||||
    events = [Event("event", "1"), Event("event", "1")]
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Statistics.event_stats")
 | 
			
		||||
    chart_plot = mocker.patch("ahriman.application.handlers.Statistics.plot_times")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
 | 
			
		||||
    chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_times")
 | 
			
		||||
 | 
			
		||||
    Statistics.stats_for_package("event", events, local)
 | 
			
		||||
    chart_plot.assert_called_once_with("event", events, local)
 | 
			
		||||
@ -174,8 +174,8 @@ def test_stats_per_package(mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    events = [Event("event", "1"), Event("event", "2"), Event("event", "1")]
 | 
			
		||||
    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
			
		||||
    events_mock = mocker.patch("ahriman.application.handlers.Statistics.event_stats")
 | 
			
		||||
    chart_plot = mocker.patch("ahriman.application.handlers.Statistics.plot_packages")
 | 
			
		||||
    events_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
 | 
			
		||||
    chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_packages")
 | 
			
		||||
 | 
			
		||||
    Statistics.stats_per_package("event", events, None)
 | 
			
		||||
    print_mock.assert_has_calls([
 | 
			
		||||
@ -192,8 +192,8 @@ def test_stats_per_package_with_chart(mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    local = Path("local")
 | 
			
		||||
    events = [Event("event", "1"), Event("event", "2"), Event("event", "1")]
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Statistics.event_stats")
 | 
			
		||||
    chart_plot = mocker.patch("ahriman.application.handlers.Statistics.plot_packages")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
 | 
			
		||||
    chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_packages")
 | 
			
		||||
 | 
			
		||||
    Statistics.stats_per_package("event", events, local)
 | 
			
		||||
    chart_plot.assert_called_once_with("event", {"1": 2, "2": 1}, local)
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Status
 | 
			
		||||
from ahriman.application.handlers.status import Status
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.database import SQLite
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
@ -43,7 +43,7 @@ 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.Client.status_get")
 | 
			
		||||
    packages_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=packages)
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -67,7 +67,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Status.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import argparse
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import StatusUpdate
 | 
			
		||||
from ahriman.application.handlers.status_update import StatusUpdate
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.database import SQLite
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Structure
 | 
			
		||||
from ahriman.application.handlers.structure import Structure
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ from pathlib import Path
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import TreeMigrate
 | 
			
		||||
from ahriman.application.handlers.tree_migrate import TreeMigrate
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_id import RepositoryId
 | 
			
		||||
from ahriman.models.repository_paths import RepositoryPaths
 | 
			
		||||
@ -15,7 +15,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
 | 
			
		||||
    must run command
 | 
			
		||||
    """
 | 
			
		||||
    tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.TreeMigrate.tree_move")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.tree_migrate.TreeMigrate.tree_move")
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    old_paths = configuration.repository_paths
 | 
			
		||||
    new_paths = RepositoryPaths(old_paths.root, old_paths.repository_id, _force_current_tree=True)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import argparse
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Triggers
 | 
			
		||||
from ahriman.application.handlers.triggers import Triggers
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
# nothing to test here
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.ahriman import _parser
 | 
			
		||||
from ahriman.application.handlers import UnsafeCommands
 | 
			
		||||
from ahriman.application.handlers.unsafe_commands import UnsafeCommands
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
 | 
			
		||||
    must run command
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    commands_mock = mocker.patch("ahriman.application.handlers.UnsafeCommands.get_unsafe_commands",
 | 
			
		||||
    commands_mock = mocker.patch("ahriman.application.handlers.unsafe_commands.UnsafeCommands.get_unsafe_commands",
 | 
			
		||||
                                 return_value=["command"])
 | 
			
		||||
    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
			
		||||
 | 
			
		||||
@ -44,9 +44,9 @@ def test_run_check(args: argparse.Namespace, configuration: Configuration, mocke
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.subcommand = ["clean"]
 | 
			
		||||
    commands_mock = mocker.patch("ahriman.application.handlers.UnsafeCommands.get_unsafe_commands",
 | 
			
		||||
    commands_mock = mocker.patch("ahriman.application.handlers.unsafe_commands.UnsafeCommands.get_unsafe_commands",
 | 
			
		||||
                                 return_value=["command"])
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.UnsafeCommands.check_unsafe")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.unsafe_commands.UnsafeCommands.check_unsafe")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    UnsafeCommands.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -58,7 +58,7 @@ def test_check_unsafe(mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must check if command is unsafe
 | 
			
		||||
    """
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
    UnsafeCommands.check_unsafe(["service-clean"], ["service-clean"], _parser())
 | 
			
		||||
    check_mock.assert_called_once_with(True, False)
 | 
			
		||||
 | 
			
		||||
@ -67,7 +67,7 @@ def test_check_unsafe_safe(mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must check if command is safe
 | 
			
		||||
    """
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
    UnsafeCommands.check_unsafe(["package-status"], ["service-clean"], _parser())
 | 
			
		||||
    check_mock.assert_called_once_with(True, True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.application.application import Application
 | 
			
		||||
from ahriman.application.handlers import Update
 | 
			
		||||
from ahriman.application.handlers.update import Update
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.package import Package
 | 
			
		||||
@ -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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.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])
 | 
			
		||||
@ -81,7 +81,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Update.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -101,7 +101,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Update.run(args, repository_id, configuration, report=False)
 | 
			
		||||
@ -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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.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")
 | 
			
		||||
 | 
			
		||||
@ -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_status")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
    mocker.patch("ahriman.application.application.Application.updates")
 | 
			
		||||
    changes_mock = mocker.patch("ahriman.application.application.Application.changes")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Users
 | 
			
		||||
from ahriman.application.handlers.users import Users
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.database import SQLite
 | 
			
		||||
from ahriman.core.exceptions import PasswordError
 | 
			
		||||
@ -42,7 +42,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, database: S
 | 
			
		||||
                packager_id=args.packager, key=args.key)
 | 
			
		||||
    mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
 | 
			
		||||
    mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
 | 
			
		||||
    create_user_mock = mocker.patch("ahriman.application.handlers.Users.user_create", return_value=user)
 | 
			
		||||
    create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
 | 
			
		||||
    update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -60,7 +60,7 @@ def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration,
 | 
			
		||||
    user = User(username=args.username, password=args.password, access=args.role,
 | 
			
		||||
                packager_id=args.packager, key=args.key)
 | 
			
		||||
    mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
 | 
			
		||||
    create_user_mock = mocker.patch("ahriman.application.handlers.Users.user_create", return_value=user)
 | 
			
		||||
    create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
 | 
			
		||||
    update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -80,7 +80,7 @@ def test_run_empty_salt_without_password(args: argparse.Namespace, configuration
 | 
			
		||||
                packager_id=args.packager, key=args.key)
 | 
			
		||||
    mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
 | 
			
		||||
    mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
 | 
			
		||||
    create_user_mock = mocker.patch("ahriman.application.handlers.Users.user_create", return_value=user)
 | 
			
		||||
    create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
 | 
			
		||||
    update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -97,7 +97,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
    list_mock = mocker.patch("ahriman.core.database.SQLite.user_list", return_value=[user])
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
@ -116,7 +116,7 @@ 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_status")
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    Users.run(args, repository_id, configuration, report=False)
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Validate
 | 
			
		||||
from ahriman.application.handlers.validate import Validate
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.configuration.schema import CONFIGURATION_SCHEMA
 | 
			
		||||
from ahriman.core.configuration.validator import Validator
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import pytest
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Versions
 | 
			
		||||
from ahriman.application.handlers.versions import Versions
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
 | 
			
		||||
    """
 | 
			
		||||
    must run command
 | 
			
		||||
    """
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.Versions.package_dependencies")
 | 
			
		||||
    application_mock = mocker.patch("ahriman.application.handlers.versions.Versions.package_dependencies")
 | 
			
		||||
    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
			
		||||
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@ import pytest
 | 
			
		||||
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application.handlers import Web
 | 
			
		||||
from ahriman.application.handlers.web import Web
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.repository import Repository
 | 
			
		||||
from ahriman.models.log_handler import LogHandler
 | 
			
		||||
@ -43,7 +43,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
 | 
			
		||||
    stop_mock = mocker.patch("ahriman.core.spawn.Spawn.stop")
 | 
			
		||||
    join_mock = mocker.patch("ahriman.core.spawn.Spawn.join")
 | 
			
		||||
    _, repository_id = configuration.check_loaded()
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Handler.repositories_extract", return_value=[repository_id])
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=[repository_id])
 | 
			
		||||
 | 
			
		||||
    Web.run(args, repository_id, configuration, report=False)
 | 
			
		||||
    setup_mock.assert_called_once_with(configuration, pytest.helpers.anyvar(int), [repository_id])
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ from pathlib import Path
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.application import ahriman
 | 
			
		||||
from ahriman.application.handlers import Handler
 | 
			
		||||
from ahriman.application.handlers.handler import Handler
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.action import Action
 | 
			
		||||
from ahriman.models.build_status import BuildStatusEnum
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								tests/ahriman/core/test_module_loader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								tests/ahriman/core/test_module_loader.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
import ahriman.web.views
 | 
			
		||||
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
 | 
			
		||||
from ahriman.core.module_loader import _modules, implementations
 | 
			
		||||
from ahriman.web.views.base import BaseView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_implementations() -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must load implementations from the package
 | 
			
		||||
    """
 | 
			
		||||
    routes = list(implementations(ahriman.web.views, BaseView))
 | 
			
		||||
    assert routes
 | 
			
		||||
    assert all(isinstance(view, type) for view in routes)
 | 
			
		||||
    assert all(issubclass(view, BaseView) for view in routes)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_modules() -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must load modules
 | 
			
		||||
    """
 | 
			
		||||
    modules = list(_modules(Path(__file__).parent.parent, "ahriman.web.views"))
 | 
			
		||||
    assert modules
 | 
			
		||||
    assert all(not module.ispkg for module in modules)
 | 
			
		||||
@ -132,7 +132,7 @@ def test_get_local_files(github: GitHub, resource_path_root: Path, mocker: Mocke
 | 
			
		||||
    """
 | 
			
		||||
    must get all local files recursively
 | 
			
		||||
    """
 | 
			
		||||
    walk_mock = mocker.patch("ahriman.core.utils.walk")
 | 
			
		||||
    walk_mock = mocker.patch("ahriman.core.upload.github.walk")
 | 
			
		||||
    github.get_local_files(resource_path_root)
 | 
			
		||||
    walk_mock.assert_called()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user