mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-10-30 21:33:43 +00:00 
			
		
		
		
	feat: use split packages (#135)
* move argument parsers to handlers themselves
* use hatchling instead of flit
* Revert "use hatchling instead of flit"
This reverts commit d18d146d79.
* add package-splitt script
* replace simplify walk method
* split packages
* explicitly install packages
* separate support triggers from main package
* add docs examples
* sort actions
* docs update
* add metapackage
* review fixes
			
			
This commit is contained in:
		
							
								
								
									
										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 the list of the functions, each of them 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 view must be derived from ``ahriman.web.views.base.BaseView`` and should implement desired HTTP methods. The API specification will be also loaded automatically if available, but optional. The implementation must be saved into the ``ahriman.web.views`` package | ||||
|  | ||||
| Let's consider example of 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 a self-study, please, consider to check examples of usages in the main package. | ||||
|  | ||||
| In order to view to be added to the route list correctly, few more properties 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.*.packages`` 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 | ||||
| """"""""""""""""" | ||||
|  | ||||
|  | ||||
| @ -13,7 +13,7 @@ TL;DR | ||||
|  | ||||
| .. code-block:: shell | ||||
|  | ||||
|    yay -S ahriman | ||||
|    yay -S ahriman-core | ||||
|    ahriman -a x86_64 -r aur service-setup --packager "ahriman bot <ahriman@example.com>" | ||||
|    systemctl enable --now ahriman@x86_64-aur.timer | ||||
|  | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -2,7 +2,7 @@ Initial setup | ||||
| ============= | ||||
|  | ||||
| #.  | ||||
|    Install package as usual. | ||||
|    Install package(s) as usual. At least, ``ahriman-core`` package is required; other features can be installed separately. Alternatively, it is possible to install meta-package, which includes everything. | ||||
| #.  | ||||
|    Change settings if required, see :doc:`configuration reference <configuration>` for more details. | ||||
| #. | ||||
|  | ||||
| @ -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,12 +20,14 @@ | ||||
| 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 | ||||
| from ahriman.models.repository_id import RepositoryId | ||||
| from ahriman.web.web import run_server, setup_server | ||||
|  | ||||
|  | ||||
| class Web(Handler): | ||||
| @ -47,9 +49,6 @@ class Web(Handler): | ||||
|             configuration(Configuration): configuration instance | ||||
|             report(bool): force enable or disable reporting | ||||
|         """ | ||||
|         # we are using local import for optional dependencies | ||||
|         from ahriman.web.web import run_server, setup_server | ||||
|  | ||||
|         spawner_args = Web.extract_arguments(args, configuration) | ||||
|         spawner = Spawn(args.parser(), list(spawner_args)) | ||||
|         spawner.start() | ||||
| @ -71,6 +70,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 +114,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 | ||||
|  | ||||
| @ -19,90 +19,30 @@ | ||||
| # | ||||
| 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) -> Generator[tuple[str, type[View]], None, None]: | ||||
|     """ | ||||
|     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 | ||||
|     """ | ||||
|     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]) | ||||
|  | ||||
|     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 | ||||
|         tuple[str, type[View]]: map of the route to its view | ||||
|     """ | ||||
|     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 | ||||
|     for view in implementations(ahriman.web.views, BaseView): | ||||
|         for route in view.routes(configuration): | ||||
|             yield route, view | ||||
|  | ||||
|  | ||||
| def setup_routes(application: Application, configuration: Configuration) -> None: | ||||
| @ -115,6 +55,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): | ||||
|         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,9 @@ | ||||
| from ahriman.application.handlers.triggers import Triggers | ||||
| from ahriman.application.handlers.triggers_support import TriggersSupport | ||||
|  | ||||
|  | ||||
| def test_arguments() -> None: | ||||
|     """ | ||||
|     must define own arguments | ||||
|     """ | ||||
|     assert TriggersSupport.arguments != Triggers.arguments | ||||
| @ -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 | ||||
| @ -36,14 +36,14 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository: | ||||
|     """ | ||||
|     args = _default_args(args) | ||||
|     mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) | ||||
|     setup_mock = mocker.patch("ahriman.web.web.setup_server") | ||||
|     run_mock = mocker.patch("ahriman.web.web.run_server") | ||||
|     setup_mock = mocker.patch("ahriman.application.handlers.web.setup_server") | ||||
|     run_mock = mocker.patch("ahriman.application.handlers.web.run_server") | ||||
|     start_mock = mocker.patch("ahriman.core.spawn.Spawn.start") | ||||
|     trigger_mock = mocker.patch("ahriman.core.triggers.TriggerLoader.load") | ||||
|     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 | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user