Compare commits

..

3 Commits

Author SHA1 Message Date
a828bf65e5 chore: copyright update 2024-01-03 03:29:43 +02:00
8773cdcc81 feat: raise 404 in case if package is unknown for logs and patches 2024-01-03 03:26:16 +02:00
d339d04c8f feat: threadsafe services
In the most cases it was enough to just add lock. In case of worker
trigger, since there is atomic operation on timer, it was also required
to add queue (coz python doesn't have atomics)
2024-01-03 03:25:13 +02:00
126 changed files with 8819 additions and 10471 deletions

View File

@ -83,7 +83,6 @@ limit-inference-results=100
# usually to register additional checkers. # usually to register additional checkers.
load-plugins=pylint.extensions.docparams, load-plugins=pylint.extensions.docparams,
definition_order, definition_order,
import_order,
# Pickle collected data for later comparisons. # Pickle collected data for later comparisons.
persistent=yes persistent=yes

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 993 KiB

View File

@ -4,6 +4,14 @@ ahriman.core.log package
Submodules Submodules
---------- ----------
ahriman.core.log.filtered\_access\_logger module
------------------------------------------------
.. automodule:: ahriman.core.log.filtered_access_logger
:members:
:no-undoc-members:
:show-inheritance:
ahriman.core.log.http\_log\_handler module ahriman.core.log.http\_log\_handler module
------------------------------------------ ------------------------------------------

View File

@ -41,7 +41,7 @@ This package contains everything required for the most of application actions an
* ``ahriman.core.formatters`` package provides ``Printer`` sub-classes for printing data (e.g. package properties) to stdout which are used by some handlers. * ``ahriman.core.formatters`` package provides ``Printer`` sub-classes for printing data (e.g. package properties) to stdout which are used by some handlers.
* ``ahriman.core.gitremote`` is a package with remote PKGBUILD triggers. Should not be called directly. * ``ahriman.core.gitremote`` is a package with remote PKGBUILD triggers. Should not be called directly.
* ``ahriman.core.http`` package provides HTTP clients which can be used later by other classes. * ``ahriman.core.http`` package provides HTTP clients which can be used later by other classes.
* ``ahriman.core.log`` is a log utils package. It includes logger loader class, custom HTTP based logger and some wrappers. * ``ahriman.core.log`` is a log utils package. It includes logger loader class, custom HTTP based logger and access logger for HTTP services with additional filters.
* ``ahriman.core.report`` is a package with reporting triggers. Should not be called directly. * ``ahriman.core.report`` is a package with reporting triggers. Should not be called directly.
* ``ahriman.core.repository`` contains several traits and base repository (``ahriman.core.repository.Repository`` class) implementation. * ``ahriman.core.repository`` contains several traits and base repository (``ahriman.core.repository.Repository`` class) implementation.
* ``ahriman.core.sign`` package provides sign feature (only gpg calls are available). * ``ahriman.core.sign`` package provides sign feature (only gpg calls are available).

View File

@ -39,9 +39,9 @@ It will check current settings on common errors and compare configuration with k
Base configuration settings. Base configuration settings.
* ``apply_migrations`` - perform database migrations on the application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations manually. * ``apply_migrations`` - perform migrations on application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations manually.
* ``database`` - path to the application SQLite database, string, required. * ``database`` - path to SQLite database, string, required.
* ``include`` - path to directory with configuration files overrides, string, optional. Files will be read in alphabetical order. * ``include`` - path to directory with configuration files overrides, string, optional.
* ``logging`` - path to logging configuration, string, required. Check ``logging.ini`` for reference. * ``logging`` - path to logging configuration, string, required. Check ``logging.ini`` for reference.
``alpm:*`` groups ``alpm:*`` groups
@ -51,9 +51,9 @@ libalpm and AUR related configuration. Group name can refer to architecture, e.g
* ``database`` - path to pacman system database cache, string, required. * ``database`` - path to pacman system database cache, string, required.
* ``mirror`` - package database mirror used by pacman for synchronization, string, required. This option supports standard pacman substitutions with ``$arch`` and ``$repo``. Note that the mentioned mirror should contain all repositories which are set by ``alpm.repositories`` option. * ``mirror`` - package database mirror used by pacman for synchronization, string, required. This option supports standard pacman substitutions with ``$arch`` and ``$repo``. Note that the mentioned mirror should contain all repositories which are set by ``alpm.repositories`` option.
* ``repositories`` - list of pacman repositories, used for package search, space separated list of strings, required. * ``repositories`` - list of pacman repositories, space separated list of strings, required.
* ``root`` - root for alpm library, string, required. In the most cases it must point to the system root. * ``root`` - root for alpm library, string, required.
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands). If set to ``no``, databases must be synchronized manually. * ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands).
``auth`` group ``auth`` group
-------------- --------------
@ -64,7 +64,7 @@ Base authorization settings. ``OAuth`` provider requires ``aioauth-client`` libr
* ``allow_read_only`` - allow requesting status APIs without authorization, boolean, required. * ``allow_read_only`` - allow requesting status APIs without authorization, boolean, required.
* ``client_id`` - OAuth2 application client ID, string, required in case if ``oauth`` is used. * ``client_id`` - OAuth2 application client ID, string, required in case if ``oauth`` is used.
* ``client_secret`` - OAuth2 application client secret key, string, required in case if ``oauth`` is used. * ``client_secret`` - OAuth2 application client secret key, string, required in case if ``oauth`` is used.
* ``cookie_secret_key`` - secret key which will be used for cookies encryption, string, optional. It must be 32 bytes URL-safe base64-encoded and can be generated as following ``base64.urlsafe_b64encode(os.urandom(32)).decode("utf8")``. If not set, it will be generated automatically; note, however, that in this case, all sessions will be automatically invalidated during the service restart. * ``cookie_secret_key`` - secret key which will be used for cookies encryption, string, optional. It must be 32 url-safe base64-encoded bytes and can be generated as following ``base64.urlsafe_b64encode(os.urandom(32)).decode("utf8")``. If not set, it will be generated automatically; note, however, that in this case, all sessions will be automatically invalidated during the service restart.
* ``max_age`` - parameter which controls both cookie expiration and token expiration inside the service in seconds, integer, optional, default is 7 days. * ``max_age`` - parameter which controls both cookie expiration and token expiration inside the service in seconds, integer, optional, default is 7 days.
* ``oauth_icon`` - OAuth2 login button icon, string, optional, default is ``google``. Must be valid `Bootstrap icon <https://icons.getbootstrap.com/>`__ name. * ``oauth_icon`` - OAuth2 login button icon, string, optional, default is ``google``. Must be valid `Bootstrap icon <https://icons.getbootstrap.com/>`__ name.
* ``oauth_provider`` - OAuth2 provider class name as is in ``aioauth-client`` (e.g. ``GoogleClient``, ``GithubClient`` etc), string, required in case if ``oauth`` is used. * ``oauth_provider`` - OAuth2 provider class name as is in ``aioauth-client`` (e.g. ``GoogleClient``, ``GithubClient`` etc), string, required in case if ``oauth`` is used.
@ -86,7 +86,7 @@ Build related configuration. Group name can refer to architecture, e.g. ``build:
* ``triggers`` - list of ``ahriman.core.triggers.Trigger`` class implementation (e.g. ``ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger``) which will be loaded and run at the end of processing, space separated list of strings, optional. You can also specify triggers by their paths, e.g. ``/usr/lib/python3.10/site-packages/ahriman/core/report/report.py.ReportTrigger``. Triggers are run in the order of definition. * ``triggers`` - list of ``ahriman.core.triggers.Trigger`` class implementation (e.g. ``ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger``) which will be loaded and run at the end of processing, space separated list of strings, optional. You can also specify triggers by their paths, e.g. ``/usr/lib/python3.10/site-packages/ahriman/core/report/report.py.ReportTrigger``. Triggers are run in the order of definition.
* ``triggers_known`` - optional list of ``ahriman.core.triggers.Trigger`` class implementations which are not run automatically and used only for trigger discovery and configuration validation. * ``triggers_known`` - optional list of ``ahriman.core.triggers.Trigger`` class implementations which are not run automatically and used only for trigger discovery and configuration validation.
* ``vcs_allowed_age`` - maximal age in seconds of the VCS packages before their version will be updated with its remote source, integer, optional, default is 7 days. * ``vcs_allowed_age`` - maximal age in seconds of the VCS packages before their version will be updated with its remote source, integer, optional, default is 7 days.
* ``workers`` - list of worker nodes addresses used for build process, space separated list of strings, optional. Each worker address must be valid and reachable URL, e.g. ``https://10.0.0.1:8080``. If none set, the build process will be run on the current node. There is also special trigger which loads this value based on the list of the discovered nodes. * ``workers`` - list of worker nodes addresses used for build process, space separated list of strings, optional. Each worker address must be valid and reachable url, e.g. ``https://10.0.0.1:8080``. If none set, the build process will be run on the current node. There is also special trigger which loads this value based on the list of the discovered nodes.
``repository`` group ``repository`` group
-------------------- --------------------
@ -109,9 +109,9 @@ Settings for signing packages or repository. Group name can refer to architectur
Reporting to web service related settings. In most cases there is fallback to web section settings. Reporting to web service related settings. In most cases there is fallback to web section settings.
* ``enabled`` - enable reporting to web service, boolean, optional, default ``yes`` for backward compatibility. * ``enabled`` - enable reporting to web service, boolean, optional, default ``yes`` for backward compatibility.
* ``address`` - remote web service address with protocol, string, optional. In case of websocket, the ``http+unix`` scheme and URL encoded address (e.g. ``%2Fvar%2Flib%2Fahriman`` for ``/var/lib/ahriman``) must be used, e.g. ``http+unix://%2Fvar%2Flib%2Fahriman%2Fsocket``. In case if none set, it will be guessed from ``web`` section. * ``address`` - remote web service address with protocol, string, optional. In case of websocket, the ``http+unix`` scheme and url encoded address (e.g. ``%2Fvar%2Flib%2Fahriman`` for ``/var/lib/ahriman``) must be used, e.g. ``http+unix://%2Fvar%2Flib%2Fahriman%2Fsocket``. In case if none set, it will be guessed from ``web`` section.
* ``password`` - password to authorize in web service in order to update service status, string, required in case if authorization enabled. * ``password`` - password to authorize in web service in order to update service status, string, required in case if authorization enabled.
* ``suppress_http_log_errors`` - suppress HTTP log errors, boolean, optional, default ``no``. If set to ``yes``, any HTTP log errors (e.g. if web server is not available, but HTTP logging is enabled) will be suppressed. * ``suppress_http_log_errors`` - suppress http log errors, boolean, optional, default ``no``. If set to ``yes``, any http log errors (e.g. if web server is not available, but http logging is enabled) will be suppressed.
* ``timeout`` - HTTP request timeout in seconds, integer, optional, default is ``30``. * ``timeout`` - HTTP request timeout in seconds, integer, optional, default is ``30``.
* ``username`` - username to authorize in web service in order to update service status, string, required in case if authorization enabled. * ``username`` - username to authorize in web service in order to update service status, string, required in case if authorization enabled.
@ -123,7 +123,7 @@ Web server settings. This feature requires ``aiohttp`` libraries to be installed
* ``address`` - optional address in form ``proto://host:port`` (``port`` can be omitted in case of default ``proto`` ports), will be used instead of ``http://{host}:{port}`` in case if set, string, optional. This option is required in case if ``OAuth`` provider is used. * ``address`` - optional address in form ``proto://host:port`` (``port`` can be omitted in case of default ``proto`` ports), will be used instead of ``http://{host}:{port}`` in case if set, string, optional. This option is required in case if ``OAuth`` provider is used.
* ``enable_archive_upload`` - allow to upload packages via HTTP (i.e. call of ``/api/v1/service/upload`` uri), boolean, optional, default ``no``. * ``enable_archive_upload`` - allow to upload packages via HTTP (i.e. call of ``/api/v1/service/upload`` uri), boolean, optional, default ``no``.
* ``host`` - host to bind, string, optional. * ``host`` - host to bind, string, optional.
* ``index_url`` - full URL of the repository index page, string, optional. * ``index_url`` - full url of the repository index page, string, optional.
* ``max_body_size`` - max body size in bytes to be validated for archive upload, integer, optional. If not set, validation will be disabled. * ``max_body_size`` - max body size in bytes to be validated for archive upload, integer, optional. If not set, validation will be disabled.
* ``port`` - port to bind, integer, optional. * ``port`` - port to bind, integer, optional.
* ``service_only`` - disable status routes (including logs), boolean, optional, default ``no``. * ``service_only`` - disable status routes (including logs), boolean, optional, default ``no``.
@ -145,10 +145,10 @@ Keyring generator plugin
* ``type`` - type of the generator, string, optional, must be set to ``keyring-generator`` if exists. * ``type`` - type of the generator, string, optional, must be set to ``keyring-generator`` if exists.
* ``description`` - keyring package description, string, optional, default is ``repo PGP keyring``, where ``repo`` is the repository name. * ``description`` - keyring package description, string, optional, default is ``repo PGP keyring``, where ``repo`` is the repository name.
* ``homepage`` - URL to homepage location if any, string, optional. * ``homepage`` - url to homepage location if any, string, optional.
* ``license`` - list of licenses which are applied to this package, space separated list of strings, optional, default is ``Unlicense``. * ``license`` - list of licenses which are applied to this package, space separated list of strings, optional, default is ``Unlicense``.
* ``package`` - keyring package name, string, optional, default is ``repo-keyring``, where ``repo`` is the repository name. * ``package`` - keyring package name, string, optional, default is ``repo-keyring``, where ``repo`` is the repository name.
* ``packagers`` - list of packagers keys, space separated list of strings, optional, if not set, the user keys from database will be used. * ``packagers`` - list of packagers keys, space separated list of strings, optional, if not set, the ``key_*`` options from ``sign`` group will be used.
* ``revoked`` - list of revoked packagers keys, space separated list of strings, optional. * ``revoked`` - list of revoked packagers keys, space separated list of strings, optional.
* ``trusted`` - list of master keys, space separated list of strings, optional, if not set, the ``key`` option from ``sign`` group will be used. * ``trusted`` - list of master keys, space separated list of strings, optional, if not set, the ``key`` option from ``sign`` group will be used.
@ -164,7 +164,7 @@ Mirrorlist generator plugin
* ``type`` - type of the generator, string, optional, must be set to ``mirrorlist-generator`` if exists. * ``type`` - type of the generator, string, optional, must be set to ``mirrorlist-generator`` if exists.
* ``description`` - mirrorlist package description, string, optional, default is ``repo mirror list for use by pacman``, where ``repo`` is the repository name. * ``description`` - mirrorlist package description, string, optional, default is ``repo mirror list for use by pacman``, where ``repo`` is the repository name.
* ``homepage`` - URL to homepage location if any, string, optional. * ``homepage`` - url to homepage location if any, string, optional.
* ``license`` - list of licenses which are applied to this package, space separated list of strings, optional, default is ``Unlicense``. * ``license`` - list of licenses which are applied to this package, space separated list of strings, optional, default is ``Unlicense``.
* ``package`` - mirrorlist package name, string, optional, default is ``repo-mirrorlist``, where ``repo`` is the repository name. * ``package`` - mirrorlist package name, string, optional, default is ``repo-mirrorlist``, where ``repo`` is the repository name.
* ``path`` - absolute path to generated mirrorlist file, string, optional, default is ``/etc/pacman.d/repo-mirrorlist``, where ``repo`` is the repository name. * ``path`` - absolute path to generated mirrorlist file, string, optional, default is ``/etc/pacman.d/repo-mirrorlist``, where ``repo`` is the repository name.
@ -175,7 +175,7 @@ Mirrorlist generator plugin
Remote git source synchronization settings. Unlike ``Upload`` triggers those triggers are used for PKGBUILD synchronization - fetch from remote repository PKGBUILDs before updating process. Remote git source synchronization settings. Unlike ``Upload`` triggers those triggers are used for PKGBUILD synchronization - fetch from remote repository PKGBUILDs before updating process.
It supports authorization; to do so you'd need to prefix the URL with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details. Alternatively, you can use any other option supported by git, e.g.: It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details. Alternatively, you can use any other option supported by git, e.g.:
* by SSH key: generate SSH key as ``ahriman`` user and put public part of it to the repository keys. * by SSH key: generate SSH key as ``ahriman`` user and put public part of it to the repository keys.
* by git credentials helper: consult with the `related man page <https://git-scm.com/docs/gitcredentials>`__. * by git credentials helper: consult with the `related man page <https://git-scm.com/docs/gitcredentials>`__.
@ -187,7 +187,7 @@ Available options are:
Remote pull trigger Remote pull trigger
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
* ``pull_url`` - URL of the remote repository from which PKGBUILDs can be pulled before build process, string, required. * ``pull_url`` - url of the remote repository from which PKGBUILDs can be pulled before build process, string, required.
* ``pull_branch`` - branch of the remote repository from which PKGBUILDs can be pulled before build process, string, optional, default is ``master``. * ``pull_branch`` - branch of the remote repository from which PKGBUILDs can be pulled before build process, string, optional, default is ``master``.
``remote-push`` group ``remote-push`` group
@ -195,7 +195,7 @@ Remote pull trigger
Remote git source synchronization settings. Same as remote pull triggers those triggers are used for PKGBUILD synchronization - push updated PKGBUILDs to the remote repository after build process. Remote git source synchronization settings. Same as remote pull triggers those triggers are used for PKGBUILD synchronization - push updated PKGBUILDs to the remote repository after build process.
It supports authorization; to do so you'd need to prefix the URL with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details. Alternatively, you can use any other option supported by git, e.g.: It supports authorization; to do so you'd need to prefix the url with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details. Alternatively, you can use any other option supported by git, e.g.:
* by SSH key: generate SSH key as ``ahriman`` user and put public part of it to the repository keys. * by SSH key: generate SSH key as ``ahriman`` user and put public part of it to the repository keys.
* by git credentials helper: consult with the `related man page <https://git-scm.com/docs/gitcredentials>`__. * by git credentials helper: consult with the `related man page <https://git-scm.com/docs/gitcredentials>`__.
@ -209,7 +209,7 @@ Remote push trigger
* ``commit_email`` - git commit email, string, optional, default is ``ahriman@localhost``. * ``commit_email`` - git commit email, string, optional, default is ``ahriman@localhost``.
* ``commit_user`` - git commit user, string, optional, default is ``ahriman``. * ``commit_user`` - git commit user, string, optional, default is ``ahriman``.
* ``push_url`` - URL of the remote repository to which PKGBUILDs should be pushed after build process, string, required. * ``push_url`` - url of the remote repository to which PKGBUILDs should be pushed after build process, string, required.
* ``push_branch`` - branch of the remote repository to which PKGBUILDs should be pushed after build process, string, optional, default is ``master``. * ``push_branch`` - branch of the remote repository to which PKGBUILDs should be pushed after build process, string, optional, default is ``master``.
``report`` group ``report`` group

View File

@ -174,7 +174,7 @@ For that purpose you could use ``RemotePullTrigger`` trigger. To do so you will
[gitremote] [gitremote]
pull_url = https://github.com/username/repository pull_url = https://github.com/username/repository
During the next application run it will fetch repository from the specified URL and will try to find packages there which can be used as local sources. During the next application run it will fetch repository from the specified url and will try to find packages there which can be used as local sources.
This feature can be also used to build packages which are not listed in AUR, the example of the feature use can be found `here <https://github.com/arcan1s/ahriman/tree/master/recipes/pull>`__. This feature can be also used to build packages which are not listed in AUR, the example of the feature use can be found `here <https://github.com/arcan1s/ahriman/tree/master/recipes/pull>`__.
@ -191,7 +191,7 @@ For that purpose you'd need to use another trigger called ``RemotePushTrigger``.
[gitremote] [gitremote]
push_url = https://github.com/username/repository push_url = https://github.com/username/repository
Unlike ``RemotePullTrigger`` trigger, the ``RemotePushTrigger`` more likely will require authorization. It is highly recommended to use application tokens for that instead of using your password (e.g. for GitHub you can generate tokens `here <https://github.com/settings/tokens>`__ with scope ``public_repo``). Authorization can be supplied by using authorization part of the URL, e.g. ``https://key:token@github.com/username/repository``. Unlike ``RemotePullTrigger`` trigger, the ``RemotePushTrigger`` more likely will require authorization. It is highly recommended to use application tokens for that instead of using your password (e.g. for GitHub you can generate tokens `here <https://github.com/settings/tokens>`__ with scope ``public_repo``). Authorization can be supplied by using authorization part of the url, e.g. ``https://key:token@github.com/username/repository``.
How to change PKGBUILDs before build How to change PKGBUILDs before build
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -461,7 +461,7 @@ The following environment variables are supported:
* ``AHRIMAN_POSTSETUP_COMMAND`` - if set, the command which will be called (as root) after the setup command, but before any other actions. * ``AHRIMAN_POSTSETUP_COMMAND`` - if set, the command which will be called (as root) after the setup command, but before any other actions.
* ``AHRIMAN_PRESETUP_COMMAND`` - if set, the command which will be called (as root) right before the setup command. * ``AHRIMAN_PRESETUP_COMMAND`` - if set, the command which will be called (as root) right before the setup command.
* ``AHRIMAN_REPOSITORY`` - repository name, default is ``aur-clone``. * ``AHRIMAN_REPOSITORY`` - repository name, default is ``aur-clone``.
* ``AHRIMAN_REPOSITORY_SERVER`` - optional override for the repository URL. Useful if you would like to download packages from remote instead of local filesystem. * ``AHRIMAN_REPOSITORY_SERVER`` - optional override for the repository url. Useful if you would like to download packages from remote instead of local filesystem.
* ``AHRIMAN_REPOSITORY_ROOT`` - repository root. Because of filesystem rights it is required to override default repository root. By default, it uses ``ahriman`` directory inside ahriman's home, which can be passed as mount volume. * ``AHRIMAN_REPOSITORY_ROOT`` - repository root. Because of filesystem rights it is required to override default repository root. By default, it uses ``ahriman`` directory inside ahriman's home, which can be passed as mount volume.
* ``AHRIMAN_UNIX_SOCKET`` - full path to unix socket which is used by web server, default is empty. Note that more likely you would like to put it inside ``AHRIMAN_REPOSITORY_ROOT`` directory (e.g. ``/var/lib/ahriman/ahriman/ahriman-web.sock``) or to ``/tmp``. * ``AHRIMAN_UNIX_SOCKET`` - full path to unix socket which is used by web server, default is empty. Note that more likely you would like to put it inside ``AHRIMAN_REPOSITORY_ROOT`` directory (e.g. ``/var/lib/ahriman/ahriman/ahriman-web.sock``) or to ``/tmp``.
* ``AHRIMAN_USER`` - ahriman user, usually must not be overwritten, default is ``ahriman``. * ``AHRIMAN_USER`` - ahriman user, usually must not be overwritten, default is ``ahriman``.

View File

@ -49,7 +49,7 @@ Generator for keyring package. This trigger will extract keys from local keychai
``ahriman.core.support.MirrorlistTrigger`` ``ahriman.core.support.MirrorlistTrigger``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Simple generator for mirrorlist package, based on the URLs which were set by configuration. This trigger will generate sources including PKGBUILD, which can be used later for package building. Simple generator for mirrorlist package, based on the urls which were set by configuration. This trigger will generate sources including PKGBUILD, which can be used later for package building.
``ahriman.core.upload.UploadTrigger`` ``ahriman.core.upload.UploadTrigger``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -95,7 +95,7 @@ Trigger example
Lets consider example of reporting trigger (e.g. `slack <https://slack.com/>`__, which provides easy HTTP API for integration triggers). Lets consider example of reporting trigger (e.g. `slack <https://slack.com/>`__, which provides easy HTTP API for integration triggers).
In order to post message to slack we will need a specific trigger URL (something like ``https://hooks.slack.com/services/company_id/trigger_id``), channel (e.g. ``#archrepo``) and username (``repo-bot``). In order to post message to slack we will need a specific trigger url (something like ``https://hooks.slack.com/services/company_id/trigger_id``), channel (e.g. ``#archrepo``) and username (``repo-bot``).
As it has been mentioned, our trigger must derive from specific class: As it has been mentioned, our trigger must derive from specific class:

View File

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

View File

@ -1,352 +1,85 @@
[settings] [settings]
; Relative path to directory with configuration files overrides. Overrides will be applied in alphabetic order.
include = ahriman.ini.d include = ahriman.ini.d
; Relative path to configuration used by logging package.
logging = ahriman.ini.d/logging.ini logging = ahriman.ini.d/logging.ini
; Perform database migrations on the application start. Do not touch this option unless you know what are you doing. apply_migrations = yes
;apply_migrations = yes
; Path to the application SQLite database.
database = /var/lib/ahriman/ahriman.db database = /var/lib/ahriman/ahriman.db
[alpm] [alpm]
; Path to pacman system database cache.
database = /var/lib/pacman database = /var/lib/pacman
; Arch linux mirror used by local pacman for synchronization.
mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
; Space separated list of pacman repositories to search for packages.
repositories = core extra multilib repositories = core extra multilib
; Pacman's root directory. In the most cases it must point to the system root.
root = / root = /
; Use local packages cache. If this option is enabled, the service will be able to synchronize databases (available
; as additional option for some subcommands). If set to no, databases must be synchronized manually.
use_ahriman_cache = yes use_ahriman_cache = yes
[auth] [auth]
; Authentication provider, must be one of disabled, configuration, oauth.
target = disabled target = disabled
; Allow read-only endpoint to be called without authentication. max_age = 604800
oauth_provider = GoogleClient
oauth_scopes = https://www.googleapis.com/auth/userinfo.email
allow_read_only = yes 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 =
; 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
; Optional password salt.
;salt =
[build] [build]
; List of additional flags passed to archbuild command. archbuild_flags =
;archbuild_flags = ignore_packages =
; List of packages to be ignored during automatic updates. makechrootpkg_flags =
;ignore_packages =
; List of additional flags passed to makechrootpkg command.
;makechrootpkg_flags =
; List of additional flags passed to makepkg command.
makepkg_flags = --nocolor --ignorearch makepkg_flags = --nocolor --ignorearch
; List of enabled triggers in the order of calls.
triggers = ahriman.core.gitremote.RemotePullTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.gitremote.RemotePushTrigger triggers = ahriman.core.gitremote.RemotePullTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.gitremote.RemotePushTrigger
; List of well-known triggers. Used only for configuration purposes.
triggers_known = ahriman.core.distributed.WorkerLoaderTrigger ahriman.core.distributed.WorkerRegisterTrigger ahriman.core.distributed.WorkerTrigger ahriman.core.distributed.WorkerUnregisterTrigger ahriman.core.gitremote.RemotePullTrigger ahriman.core.gitremote.RemotePushTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.support.KeyringTrigger ahriman.core.support.MirrorlistTrigger triggers_known = ahriman.core.distributed.WorkerLoaderTrigger ahriman.core.distributed.WorkerRegisterTrigger ahriman.core.distributed.WorkerTrigger ahriman.core.distributed.WorkerUnregisterTrigger ahriman.core.gitremote.RemotePullTrigger ahriman.core.gitremote.RemotePushTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.support.KeyringTrigger ahriman.core.support.MirrorlistTrigger
; Maximal age in seconds of the VCS packages before their version will be updated with its remote source. vcs_allowed_age = 604800
;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] [repository]
; Application root.
root = /var/lib/ahriman root = /var/lib/ahriman
[sign] [sign]
; Enable repository or package signing. Must be one of repository, package.
target = target =
; PGP key used for signing as default.
;key =
[status]
; Global switch to enable or disable status reporting.
enabled = yes
; Address of the remote service, e.g.:
; address = http://1.0.0.1:8080
; In case if unix sockets are used, it might point to the valid socket with encoded path, e.g.:
; address = http+unix://%2Fvar%2Flib%2Fahriman%2Fsocket
;address =
; Optional password for authentication (if enabled).
;password =
; Do not log HTTP errors if occurs.
suppress_http_log_errors = yes
; HTTP request timeout in seconds.
;timeout = 30
; 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 =
; Enable file upload endpoint used by some triggers.
;enable_archive_upload = no
; Address to bind the server.
host = 127.0.0.1
; Full URL to the repository index page used by templates.
;index_url =
; Max file size in bytes which can be uploaded to the server.
;max_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 = /usr/share/ahriman/templates/static
; List of directories with templates.
templates = /usr/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] [keyring]
; List of configuration section names for keyring generator plugin, e.g.:
; target = keyring-trigger
target = 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] [mirrorlist]
; List of configuration section names for mirrorlist generator plugin, e.g.:
; target = mirrorlist-trigger
target = 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] [remote-pull]
; List of configuration section names for git remote pull plugin, e.g.:
; target = remote-pull-trigger
target = target =
; git remote pull trigger sample.
;[remote-pull-trigger]
; Valid URL to pull repository, e.g.:
; pull_url = https://github.com/arcan1s/arcanisrepo.git
;pull_url =
; Remote branch to pull.
;pull_branch = master
[remote-push] [remote-push]
; List of configuration section names for git remote push plugin, e.g.:
; target = remote-push-trigger
target = target =
; git remote push trigger sample.
;[remote-push-trigger]
; Author commit email.
;commit_email = ahriman@localhost
; Author commit user.
;commit_user = ahriman
; Valid URL to push repository, e.g.:
; push_url = https://key:token@github.com/arcan1s/arcanisrepo.git
; Note, that more likely authentication must be enabled.
;push_url =
; Remote branch to push.
;push_branch = master
[report] [report]
; List of configuration section names for reporting plugin.
target = console target = console
; Console reporting trigger configuration sample.
[console] [console]
; Trigger type name
;type = console
; Use utf8 symbols in output.
use_utf = yes use_utf = yes
; Email reporting trigger configuration sample.
[email] [email]
; Trigger type name no_empty_report = yes
;type = email
; Optional URL to the repository homepage.
;homepage=
; SMTP server address.
;host =
; Prefix for packages links. Link to a package will be formed as link_path / filename.
;link_path =
; Skip report generation if no packages were updated.
;no_empty_report = yes
; SMTP password.
;password =
; SMTP server port.
;port =
; List of emails to receive the reports.
;receivers =
; Sender email.
;sender =
; SMTP server SSL mode, one of ssl, starttls, disabled.
;ssl = disabled
; Template name to be used.
template = email-index.jinja2 template = email-index.jinja2
; Template name to be used for full packages list generation (same as HTML report).
;template_full =
; List of directories with templates.
templates = /usr/share/ahriman/templates templates = /usr/share/ahriman/templates
; SMTP user. ssl = disabled
;user =
; HTML reporting trigger configuration sample.
[html] [html]
; Trigger type name
;type = html
; Optional URL to the repository homepage.
;homepage=
; Prefix for packages links. Link to a package will be formed as link_path / filename.
;link_path =
; Output path for the HTML report.
;path =
; Template name to be used.
template = repo-index.jinja2 template = repo-index.jinja2
; List of directories with templates.
templates = /usr/share/ahriman/templates templates = /usr/share/ahriman/templates
; Remote service callback trigger configuration sample. [status]
[remote-call] enabled = yes
; Trigger type name suppress_http_log_errors = yes
;type = remote-call
; Call for AUR packages update.
;aur = no
; Call for local packages update.
;local = no
; Call for manual packages update.
;manual = no
; Wait until remote process will be terminated in seconds.
;wait_timeout = -1
; Telegram reporting trigger configuration sample.
[telegram] [telegram]
; Trigger type name
;type = telegram
; Telegram bot API key.
;api_key =
; Telegram chat ID.
;chat_id =
; Optional URL to the repository homepage.
;homepage=
; Prefix for packages links. Link to a package will be formed as link_path / filename.
;link_path =
; Template name to be used.
template = telegram-index.jinja2 template = telegram-index.jinja2
; Telegram specific template mode, one of MarkdownV2, HTML or Markdown.
;template_type = HTML
; List of directories with templates.
templates = /usr/share/ahriman/templates templates = /usr/share/ahriman/templates
; HTTP request timeout in seconds.
;timeout = 30
[upload] [upload]
; List of configuration section names for remote upload plugin, e.g.:
; target = rsync s3
target = target =
; GitHub upload trigger configuration sample.
[github]
; Trigger type name
;type = github
; GitHub repository owner username.
;owner =
; GitHub API key. public_repo (repo) scope is required.
;password =
; GitHub repository name.
;repository =
; HTTP request timeout in seconds.
;timeout = 30
; Include repository name to release name (recommended).
;use_full_release_name = no
; GitHub authentication username.
;username =
; Remote instance upload trigger configuration sample.
[remote-service]
; Trigger type name
;type = remote-service
; HTTP request timeout in seconds.
;timeout = 30
; rsync upload trigger configuration sample.
[rsync] [rsync]
; Trigger type name
;type = rsync
; rsync command to run.
command = rsync --archive --compress --partial --delete command = rsync --archive --compress --partial --delete
; Remote address and directory to sync, e.g.:
; remote = ahriman@10.0.0.1:/srv/repo
;remote =
; S3 upload trigger configuration sample.
[s3] [s3]
; Trigger type name chunk_size = 8388608
;type = s3
; AWS services access key.
;access_key =
; AWS S3 bucket name.
;bucket =
; Chunk size tp calculate ETags. Do not edit this value.
;chunk_size = 8388608
; Optional path prefix for stored objects.
;object_path =
; AWS S3 bucket region.
;region =
; AWS services secret key.
;secret_key =
; Remote worker configuration sample. [web]
;[worker] host = 127.0.0.1
; Remotely reachable address of this instance, e.g.: static_path = /usr/share/ahriman/templates/static
; address = http://10.0.0.1:8080 templates = /usr/share/ahriman/templates
;address = unix_socket_unsafe = yes
; Unique identifier of this instance if any.
;identifier =
; Maximum amount of time in seconds after which worker will be considered offline in case of no reports.
;time_to_live = 60

View File

@ -36,7 +36,7 @@ level = DEBUG
qualname = root qualname = root
[logger_http] [logger_http]
level = WARNING level = DEBUG
qualname = http qualname = http
propagate = 0 propagate = 0

View File

@ -1,4 +1,4 @@
.TH AHRIMAN "1" "2024\-01\-08" "ahriman" "Generated Python Manual" .TH AHRIMAN "1" "2024\-01\-02" "ahriman" "Generated Python Manual"
.SH NAME .SH NAME
ahriman ahriman
.SH SYNOPSIS .SH SYNOPSIS

View File

@ -25,19 +25,19 @@ from pylint.lint import PyLinter
from typing import Any from typing import Any
class MethodType(StrEnum): class MethodTypeOrder(StrEnum):
""" """
method type enumeration method type enumeration
Attributes: Attributes:
Class(MethodType): (class attribute) class method Class(MethodTypeOrder): (class attribute) class method
Delete(MethodType): (class attribute) destructor-like methods Delete(MethodTypeOrder): (class attribute) destructor-like methods
Init(MethodType): (class attribute) initialization method Init(MethodTypeOrder): (class attribute) initialization method
Magic(MethodType): (class attribute) other magical methods Magic(MethodTypeOrder): (class attribute) other magical methods
New(MethodType): (class attribute) constructor method New(MethodTypeOrder): (class attribute) constructor method
Normal(MethodType): (class attribute) usual method Normal(MethodTypeOrder): (class attribute) usual method
Property(MethodType): (class attribute) property method Property(MethodTypeOrder): (class attribute) property method
Static(MethodType): (class attribute) static method Static(MethodTypeOrder): (class attribute) static method
""" """
Class = "classmethod" Class = "classmethod"
@ -59,20 +59,20 @@ class DefinitionOrder(BaseRawFileChecker):
""" """
DECORATED_METHODS_ORDER = { DECORATED_METHODS_ORDER = {
"cached_property": MethodType.Property, "cached_property": MethodTypeOrder.Property,
"classmethod": MethodType.Class, "classmethod": MethodTypeOrder.Class,
"property": MethodType.Property, "property": MethodTypeOrder.Property,
"staticmethod": MethodType.Static, "staticmethod": MethodTypeOrder.Static,
} }
name = "method-ordering"
msgs = { msgs = {
"W6001": ( "W6001": (
"Invalid method order %s, expected %s", "Invalid method order %s, expected %s",
"methods-out-of-order", "methods-out-of-order",
"Methods are defined out of recommended order.", "Methods are defined out of recommended order.",
), )
} }
name = "method-ordering"
options = ( options = (
( (
"method-type-order", "method-type-order",
@ -114,7 +114,7 @@ class DefinitionOrder(BaseRawFileChecker):
return list(filter(is_defined_function, source)) return list(filter(is_defined_function, source))
@staticmethod @staticmethod
def resolve_type(function: nodes.FunctionDef) -> MethodType: def resolve_type(function: nodes.FunctionDef) -> MethodTypeOrder:
""" """
resolve type of the function resolve type of the function
@ -122,15 +122,15 @@ class DefinitionOrder(BaseRawFileChecker):
function(nodes.FunctionDef): function definition function(nodes.FunctionDef): function definition
Returns: Returns:
MethodType: resolved function type MethodTypeOrder: resolved function type
""" """
# init methods # init methods
if function.name in ("__init__", "__post_init__"): if function.name in ("__init__", "__post_init__"):
return MethodType.Init return MethodTypeOrder.Init
if function.name in ("__new__",): if function.name in ("__new__",):
return MethodType.New return MethodTypeOrder.New
if function.name in ("__del__",): if function.name in ("__del__",):
return MethodType.Delete return MethodTypeOrder.Delete
# decorated methods # decorated methods
decorators = [] decorators = []
@ -142,10 +142,10 @@ class DefinitionOrder(BaseRawFileChecker):
# magic methods # magic methods
if function.name.startswith("__") and function.name.endswith("__"): if function.name.startswith("__") and function.name.endswith("__"):
return MethodType.Magic return MethodTypeOrder.Magic
# normal method # normal method
return MethodType.Normal return MethodTypeOrder.Normal
def check_class(self, clazz: nodes.ClassDef) -> None: def check_class(self, clazz: nodes.ClassDef) -> None:
""" """
@ -184,7 +184,7 @@ class DefinitionOrder(BaseRawFileChecker):
try: try:
function_type_index = self.linter.config.method_type_order.index(function_type) function_type_index = self.linter.config.method_type_order.index(function_type)
except ValueError: except ValueError:
function_type_index = len(self.linter.config.method_type_order) # not in the list function_type_index = 10 # not in the list
return function_type_index, function.name return function_type_index, function.name

View File

@ -1,196 +0,0 @@
#
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from astroid import nodes
from collections.abc import Iterable
from enum import StrEnum
from pylint.checkers import BaseRawFileChecker
from pylint.lint import PyLinter
from typing import Any
class ImportType(StrEnum):
"""
import type enumeration
Attributes:
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
"""
Package = "package"
PackageFrom = "package-from"
System = "system"
SystemFrom = "system-from"
class ImportOrder(BaseRawFileChecker):
"""
check if imports are defined in recommended order
"""
msgs = {
"W6002": (
"Invalid import order %s, expected before %s",
"imports-out-of-order",
"Imports are defined out of recommended order.",
),
"W6003": (
"Import contains more than one package: %s",
"multiple-package-imports",
"Multiple package imports are not allowed.",
),
"W6004": (
"Invalid from import order %s, expected %s",
"from-imports-out-of-order",
"From imports are defined out of recommended order.",
),
}
name = "import-ordering"
options = (
(
"import-type-order",
{
"default": [
"system",
"system-from",
"package",
"package-from",
],
"type": "csv",
"metavar": "<comma-separated types>",
"help": "Import types order to check.",
},
),
(
"root-module",
{
"default": "ahriman",
"type": "string",
"help": "Root module name",
}
)
)
@staticmethod
def imports(source: Iterable[Any], start_lineno: int = 0) -> list[nodes.Import | nodes.ImportFrom]:
"""
extract import nodes from list of raw nodes
Args:
source(Iterable[Any]): all available nodes
start_lineno(int, optional): minimal allowed line number (Default value = 0)
Returns:
list[nodes.Import | nodes.ImportFrom]: list of import nodes
"""
def is_defined_import(imports: Any) -> bool:
return isinstance(imports, (nodes.Import, nodes.ImportFrom)) \
and imports.lineno is not None \
and imports.lineno >= start_lineno
return list(filter(is_defined_import, source))
def check_from_imports(self, imports: nodes.ImportFrom) -> None:
"""
check import from statement
Args:
imports(nodes.ImportFrom): import from node
"""
imported = [names for names, _ in imports.names]
for real, expected in zip(imported, sorted(imported)):
if real == expected:
continue
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:
"""
check imports
Args:
imports(list[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)
# extract index
try:
import_type_index = self.linter.config.import_type_order.index(import_type)
except ValueError:
import_type_index = len(self.linter.config.import_type_order)
# check ordering if possible
if last_statement is not None:
_, last_statement_name = last_statement
if last_statement > (import_type_index, import_name):
self.add_message("imports-out-of-order", line=statement.lineno,
args=(import_name, last_statement_name))
# update the last value
last_statement = import_type_index, import_name
def check_package_imports(self, imports: nodes.Import) -> None:
"""
check package import
Args:
imports(nodes.Import): package import node
"""
if len(imports.names) != 1:
self.add_message("multiple-package-imports", line=imports.lineno, args=(imports.names,))
def process_module(self, node: nodes.Module) -> None:
"""
process module
Args:
node(nodes.Module): module node to check
"""
root_module, *_ = node.qname().split(".")
self.check_imports(self.imports(node.values()), root_module)
def register(linter: PyLinter) -> None:
"""
register custom checker
Args:
linter(PyLinter): linter in which checker should be registered
"""
linter.register_checker(ImportOrder(linter))

View File

@ -8,8 +8,7 @@ name = "ahriman"
description = "ArcH linux ReposItory MANager" description = "ArcH linux ReposItory MANager"
readme = "README.md" readme = "README.md"
# Actually we are using features from the latest python, however, ubuntu, which is used for CI doesn't have it requires-python = ">=3.11"
requires-python = ">=3"
license = {file = "COPYING"} license = {file = "COPYING"}
authors = [ authors = [

View File

@ -9,9 +9,7 @@ Collection of the examples of docker compose configuration files, which covers s
* [Distributed](distributed): cluster of three nodes, one with web interface and two workers which are responsible for build process. * [Distributed](distributed): cluster of three nodes, one with web interface and two workers which are responsible for build process.
* [Distributed manual](distributed-manual): same as [distributed](distributed), but two nodes and update process must be run on worker node manually. * [Distributed manual](distributed-manual): same as [distributed](distributed), but two nodes and update process must be run on worker node manually.
* [i686](i686): non-x86_64 architecture setup. * [i686](i686): non-x86_64 architecture setup.
* [Index](index): repository with index page generator enabled.
* [Multi repo](multirepo): run web service with two separated repositories. * [Multi repo](multirepo): run web service with two separated repositories.
* [OAuth](oauth): web service with OAuth (GitHub provider) authentication enabled.
* [Pull](pull): normal service, but in addition with pulling packages from another source (e.g. GitHub repository). * [Pull](pull): normal service, but in addition with pulling packages from another source (e.g. GitHub repository).
* [Sign](sign): create repository with database signing. * [Sign](sign): create repository with database signing.
* [Web](web): simple web service with authentication enabled. * [Web](web): simple web service with authentication enabled.

View File

@ -1,6 +0,0 @@
# Index
1. Setup repository named `ahriman-demo` with architecture `x86_64`.
2. Generate index page.
3. Repository is available at `http://localhost:8080/repo`.
4. Index page is available at `http://localhost:8080/repo/ahriman-demo/x86_64/index.html`

View File

@ -1,48 +0,0 @@
services:
backend:
image: arcan1s/ahriman:edge
privileged: true
environment:
AHRIMAN_DEBUG: yes
AHRIMAN_OUTPUT: console
AHRIMAN_REPOSITORY: ahriman-demo
configs:
- source: service
target: /etc/ahriman.ini.d/99-settings.ini
volumes:
- type: volume
source: repository
target: /var/lib/ahriman
volume:
nocopy: true
command: repo-report
frontend:
image: nginx
ports:
- 8080:80
configs:
- source: nginx
target: /etc/nginx/conf.d/default.conf
volumes:
- type: volume
source: repository
target: /srv
read_only: true
volume:
nocopy: true
configs:
nginx:
file: nginx.conf
service:
file: service.ini
volumes:
repository:

View File

@ -1,9 +0,0 @@
server {
listen 80;
location /repo {
rewrite ^/repo/(.*) /$1 break;
autoindex on;
root /srv/ahriman/repository;
}
}

View File

@ -1,6 +0,0 @@
[report]
target = html
[html]
path = /var/lib/ahriman/ahriman/repository/ahriman-demo/x86_64/index.html
link_path = http://localhost:8080/repo/ahriman-demo/x86_64

View File

@ -1,15 +0,0 @@
# OAuth
1. Create user from `AHRIMAN_OAUTH_USER` environment variable (same as GitHub user).
2. Configure OAuth to use GitHub provider with client ID and secret specified in variables `AHRIMAN_OAUTH_CLIENT_ID` and `AHRIMAN_OAUTH_CLIENT_SECRET` variables respectively.
3. Setup repository named `ahriman-demo` with architecture `x86_64`.
4. Start web server at port `8080`.
5. Repository is available at `http://localhost:8080/repo`.
Before you start, you need to create an application. It can be done by:
1. Go to `https://github.com/settings/applications/new`
2. Set application name and its homepage.
3. Set callback url to `http://localhost:8080/api/v1/login`
4. Copy Client ID.
5. Generate new client secret and copy it.

View File

@ -1,58 +0,0 @@
services:
backend:
image: arcan1s/ahriman:edge
privileged: true
environment:
AHRIMAN_DEBUG: yes
AHRIMAN_OAUTH_CLIENT_ID: ${AHRIMAN_OAUTH_CLIENT_ID}
AHRIMAN_OAUTH_CLIENT_SECRET: ${AHRIMAN_OAUTH_CLIENT_SECRET}
AHRIMAN_OUTPUT: console
AHRIMAN_PORT: 8080
AHRIMAN_PRESETUP_COMMAND: sudo -u ahriman ahriman user-add ${AHRIMAN_OAUTH_USER} -R full -p ""
AHRIMAN_REPOSITORY: ahriman-demo
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
configs:
- source: service
target: /etc/ahriman.ini.d/99-settings.ini
volumes:
- type: volume
source: repository
target: /var/lib/ahriman
volume:
nocopy: true
healthcheck:
test: curl --fail --silent --output /dev/null http://backend:8080/api/v1/info
interval: 10s
start_period: 30s
command: web
frontend:
image: nginx
ports:
- 8080:80
configs:
- source: nginx
target: /etc/nginx/conf.d/default.conf
volumes:
- type: volume
source: repository
target: /srv
read_only: true
volume:
nocopy: true
configs:
nginx:
file: nginx.conf
service:
file: service.ini
volumes:
repository:

View File

@ -1,18 +0,0 @@
server {
listen 80;
location /repo {
rewrite ^/repo/(.*) /$1 break;
autoindex on;
root /srv/ahriman/repository;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarder-Proto $scheme;
proxy_pass http://backend:8080;
}
}

View File

@ -1,11 +0,0 @@
[auth]
target = oauth
client_id = $AHRIMAN_OAUTH_CLIENT_ID
client_secret = $AHRIMAN_OAUTH_CLIENT_SECRET
oauth_icon = github
oauth_provider = GithubClient
oauth_scopes = read:user
[web]
address = http://localhost:8080

View File

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

View File

@ -71,7 +71,7 @@ def _parser() -> argparse.ArgumentParser:
fromfile_prefix_chars="@", formatter_class=_formatter) fromfile_prefix_chars="@", formatter_class=_formatter)
parser.add_argument("-a", "--architecture", help="filter by target architecture") parser.add_argument("-a", "--architecture", help="filter by target architecture")
parser.add_argument("-c", "--configuration", help="configuration path", type=Path, parser.add_argument("-c", "--configuration", help="configuration path", type=Path,
default=Path("/") / "etc" / "ahriman.ini") default=Path("/etc") / "ahriman.ini")
parser.add_argument("--force", help="force run, remove file lock", action="store_true") parser.add_argument("--force", help="force run, remove file lock", action="store_true")
parser.add_argument("-l", "--lock", help="lock file", type=Path, parser.add_argument("-l", "--lock", help="lock file", type=Path,
default=Path(tempfile.gettempdir()) / "ahriman.lock") default=Path(tempfile.gettempdir()) / "ahriman.lock")
@ -999,7 +999,7 @@ def _set_service_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=_formatter) formatter_class=_formatter)
parser.add_argument("--build-as-user", help="force makepkg user to the specific one") 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", parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
type=Path, default=Path("/") / "usr" / "share" / "devtools" / "pacman.conf.d" / "extra.conf") type=Path, default=Path("/usr") / "share" / "devtools" / "pacman.conf.d" / "extra.conf")
parser.add_argument("--generate-salt", help="generate salt for user passwords", parser.add_argument("--generate-salt", help="generate salt for user passwords",
action=argparse.BooleanOptionalAction, default=False) action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--makeflags-jobs", help="append MAKEFLAGS variable with parallelism set to number of cores", parser.add_argument("--makeflags-jobs", help="append MAKEFLAGS variable with parallelism set to number of cores",

View File

@ -163,8 +163,8 @@ class ApplicationRepository(ApplicationProperties):
built_packages = self.repository.packages_built() built_packages = self.repository.packages_built()
if built_packages: # speedup a bit if built_packages: # speedup a bit
build_result = self.repository.process_update(built_packages, packagers) build_result = self.repository.process_update(built_packages, packagers)
self.on_result(build_result)
result.merge(build_result) result.merge(build_result)
self.on_result(result.merge(build_result))
builder = Updater.load(self.repository_id, self.configuration, self.repository) builder = Updater.load(self.repository_id, self.configuration, self.repository)
@ -173,8 +173,7 @@ class ApplicationRepository(ApplicationProperties):
for num, partition in enumerate(partitions): for num, partition in enumerate(partitions):
self.logger.info("processing chunk #%i %s", num, [package.base for package in partition]) self.logger.info("processing chunk #%i %s", num, [package.base for package in partition])
build_result = builder.update(partition, packagers, bump_pkgrel=bump_pkgrel) build_result = builder.update(partition, packagers, bump_pkgrel=bump_pkgrel)
self.on_result(build_result) self.on_result(result.merge(build_result))
result.merge(build_result)
return result return result

View File

@ -17,13 +17,14 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from ahriman.application.handlers.handler import Handler
from ahriman.application.handlers.add import Add from ahriman.application.handlers.add import Add
from ahriman.application.handlers.backup import Backup from ahriman.application.handlers.backup import Backup
from ahriman.application.handlers.change import Change from ahriman.application.handlers.change import Change
from ahriman.application.handlers.clean import Clean from ahriman.application.handlers.clean import Clean
from ahriman.application.handlers.daemon import Daemon from ahriman.application.handlers.daemon import Daemon
from ahriman.application.handlers.dump import Dump 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.help import Help
from ahriman.application.handlers.key_import import KeyImport from ahriman.application.handlers.key_import import KeyImport
from ahriman.application.handlers.patch import Patch from ahriman.application.handlers.patch import Patch

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.packagers import Packagers from ahriman.models.packagers import Packagers
from ahriman.models.pkgbuild_patch import PkgbuildPatch from ahriman.models.pkgbuild_patch import PkgbuildPatch

View File

@ -23,7 +23,7 @@ import pwd
from pathlib import Path from pathlib import Path
from tarfile import TarFile from tarfile import TarFile
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite from ahriman.core.database import SQLite
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import ChangesPrinter from ahriman.core.formatters import ChangesPrinter
from ahriman.models.action import Action from ahriman.models.action import Action

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -21,7 +21,7 @@ import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.application.updates_iterator import FixedUpdatesIterator, UpdatesIterator from ahriman.application.application.updates_iterator import FixedUpdatesIterator, UpdatesIterator
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.application.handlers.update import Update from ahriman.application.handlers.update import Update
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
# #
import argparse import argparse
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import ConfigurationPathsPrinter, ConfigurationPrinter, StringPrinter from ahriman.core.formatters import ConfigurationPathsPrinter, ConfigurationPrinter, StringPrinter
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
# #
import argparse import argparse
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -23,7 +23,7 @@ import sys
from pathlib import Path from pathlib import Path
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.build_tools.sources import Sources from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import PatchPrinter from ahriman.core.formatters import PatchPrinter

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.build_status import BuildStatusEnum from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package from ahriman.models.package import Package

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
# #
import argparse import argparse
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import RepositoryPrinter from ahriman.core.formatters import RepositoryPrinter
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -21,7 +21,7 @@ import argparse
from tarfile import TarFile from tarfile import TarFile
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse import argparse
import shlex import shlex
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -19,10 +19,10 @@
# #
import argparse import argparse
from collections.abc import Callable, Iterable
from dataclasses import fields from dataclasses import fields
from collections.abc import Callable, Iterable
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.alpm.remote import AUR, Official from ahriman.core.alpm.remote import AUR, Official
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import OptionError from ahriman.core.exceptions import OptionError

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman import __version__ from ahriman import __version__
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import UpdatePrinter from ahriman.core.formatters import UpdatePrinter
from ahriman.models.package import Package from ahriman.models.package import Package

View File

@ -24,7 +24,7 @@ from pwd import getpwuid
from urllib.parse import quote_plus as urlencode from urllib.parse import quote_plus as urlencode
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import MissingArchitectureError from ahriman.core.exceptions import MissingArchitectureError
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId
@ -44,9 +44,9 @@ class Setup(Handler):
ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
ARCHBUILD_COMMAND_PATH = Path("/") / "usr" / "bin" / "archbuild" ARCHBUILD_COMMAND_PATH = Path("/usr") / "bin" / "archbuild"
MIRRORLIST_PATH = Path("/") / "etc" / "pacman.d" / "mirrorlist" MIRRORLIST_PATH = Path("/etc") / "pacman.d" / "mirrorlist"
SUDOERS_DIR_PATH = Path("/") / "etc" / "sudoers.d" SUDOERS_DIR_PATH = Path("/etc") / "sudoers.d"
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -23,7 +23,7 @@ import sys
from pathlib import Path from pathlib import Path
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -22,7 +22,7 @@ import argparse
from collections.abc import Callable from collections.abc import Callable
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import PackagePrinter, StatusPrinter from ahriman.core.formatters import PackagePrinter, StatusPrinter
from ahriman.models.build_status import BuildStatus from ahriman.models.build_status import BuildStatus

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.action import Action from ahriman.models.action import Action
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter, TreePrinter from ahriman.core.formatters import StringPrinter, TreePrinter
from ahriman.core.tree import Tree from ahriman.core.tree import Tree

View File

@ -19,7 +19,7 @@
# #
import argparse import argparse
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths

View File

@ -20,7 +20,7 @@
import argparse import argparse
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId
from ahriman.models.result import Result from ahriman.models.result import Result

View File

@ -19,7 +19,7 @@
# #
import argparse import argparse
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter from ahriman.core.formatters import StringPrinter
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -22,7 +22,7 @@ import argparse
from collections.abc import Callable from collections.abc import Callable
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.packagers import Packagers from ahriman.models.packagers import Packagers
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse import argparse
import getpass import getpass
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite from ahriman.core.database import SQLite
from ahriman.core.exceptions import PasswordError from ahriman.core.exceptions import PasswordError

View File

@ -22,7 +22,7 @@ import copy
from typing import Any from typing import Any
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.configuration.schema import CONFIGURATION_SCHEMA, ConfigurationSchema from ahriman.core.configuration.schema import CONFIGURATION_SCHEMA, ConfigurationSchema
from ahriman.core.configuration.validator import Validator from ahriman.core.configuration.validator import Validator

View File

@ -25,7 +25,7 @@ from collections.abc import Generator
from importlib import metadata from importlib import metadata
from ahriman import __version__ from ahriman import __version__
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.formatters import VersionPrinter from ahriman.core.formatters import VersionPrinter
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -21,7 +21,7 @@ import argparse
from collections.abc import Generator from collections.abc import Generator
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.spawn import Spawn from ahriman.core.spawn import Spawn
from ahriman.core.triggers import TriggerLoader from ahriman.core.triggers import TriggerLoader

View File

@ -17,7 +17,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from ahriman.core.alpm.remote.remote import Remote
from ahriman.core.alpm.remote.aur import AUR from ahriman.core.alpm.remote.aur import AUR
from ahriman.core.alpm.remote.official import Official from ahriman.core.alpm.remote.official import Official
from ahriman.core.alpm.remote.official_syncdb import OfficialSyncdb from ahriman.core.alpm.remote.official_syncdb import OfficialSyncdb
from ahriman.core.alpm.remote.remote import Remote

View File

@ -20,7 +20,7 @@
from typing import Any from typing import Any
from ahriman.core.alpm.pacman import Pacman from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote.remote import Remote from ahriman.core.alpm.remote import Remote
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
from ahriman.models.aur_package import AURPackage from ahriman.models.aur_package import AURPackage

View File

@ -20,7 +20,7 @@
from typing import Any from typing import Any
from ahriman.core.alpm.pacman import Pacman from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote.remote import Remote from ahriman.core.alpm.remote import Remote
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
from ahriman.models.aur_package import AURPackage from ahriman.models.aur_package import AURPackage

View File

@ -18,7 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from ahriman.core.alpm.pacman import Pacman from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote.official import Official from ahriman.core.alpm.remote import Official
from ahriman.core.exceptions import UnknownPackageError from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.aur_package import AURPackage from ahriman.models.aur_package import AURPackage

View File

@ -130,7 +130,7 @@ class OAuth(Mapping):
client.access_token = access_token client.access_token = access_token
user, _ = await client.user_info() user, _ = await client.user_info()
username: str = user.email or user.username # type: ignore[attr-defined] username: str = user.email # type: ignore[attr-defined]
return username return username
except Exception: except Exception:
self.logger.exception("got exception while performing request") self.logger.exception("got exception while performing request")

View File

@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from ahriman.core.database.operations.operations import Operations
from ahriman.core.database.operations.auth_operations import AuthOperations from ahriman.core.database.operations.auth_operations import AuthOperations
from ahriman.core.database.operations.build_operations import BuildOperations from ahriman.core.database.operations.build_operations import BuildOperations
from ahriman.core.database.operations.changes_operations import ChangesOperations from ahriman.core.database.operations.changes_operations import ChangesOperations

View File

@ -19,7 +19,7 @@
# #
from sqlite3 import Connection from sqlite3 import Connection
from ahriman.core.database.operations.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.user import User from ahriman.models.user import User
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess

View File

@ -19,7 +19,7 @@
# #
from sqlite3 import Connection from sqlite3 import Connection
from ahriman.core.database.operations.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
# #
from sqlite3 import Connection from sqlite3 import Connection
from ahriman.core.database.operations.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.changes import Changes from ahriman.models.changes import Changes
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
# #
from sqlite3 import Connection from sqlite3 import Connection
from ahriman.core.database.operations.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.log_record_id import LogRecordId from ahriman.models.log_record_id import LogRecordId
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
from collections.abc import Generator, Iterable from collections.abc import Generator, Iterable
from sqlite3 import Connection from sqlite3 import Connection
from ahriman.core.database.operations.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.build_status import BuildStatus from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.package_description import PackageDescription from ahriman.models.package_description import PackageDescription

View File

@ -20,7 +20,7 @@
from collections import defaultdict from collections import defaultdict
from sqlite3 import Connection from sqlite3 import Connection
from ahriman.core.database.operations.operations import Operations from ahriman.core.database.operations import Operations
from ahriman.models.pkgbuild_patch import PkgbuildPatch from ahriman.models.pkgbuild_patch import PkgbuildPatch

View File

@ -51,7 +51,7 @@ class DistributedSystem(Trigger, WebClient):
"time_to_live": { "time_to_live": {
"type": "integer", "type": "integer",
"coerce": "integer", "coerce": "integer",
"min": 1, "min": 0,
}, },
}, },
}, },

View File

@ -17,6 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from collections import deque
from threading import Lock, Timer from threading import Lock, Timer
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
@ -46,42 +47,42 @@ class WorkerTrigger(DistributedSystem):
self.ping_interval = configuration.getint(section, "time_to_live", fallback=60) / 4.0 self.ping_interval = configuration.getint(section, "time_to_live", fallback=60) / 4.0
self._lock = Lock() self._lock = Lock()
self._timer: Timer | None = None self._timers: deque[Timer] = deque() # because python doesn't have atomics
def create_timer(self) -> None: def create_timer(self) -> None:
""" """
create timer object and put it to queue create timer object and put it to queue
""" """
self._timer = Timer(self.ping_interval, self.ping) timer = Timer(self.ping_interval, self.ping)
self._timer.start() timer.start()
self._timers.append(timer)
def on_start(self) -> None: def on_start(self) -> None:
""" """
trigger action which will be called at the start of the application trigger action which will be called at the start of the application
""" """
self.logger.info("registering instance %s in %s", self.worker, self.address) self.logger.info("registering instance %s at %s", self.worker, self.address)
with self._lock:
self.create_timer() self.create_timer()
def on_stop(self) -> None: def on_stop(self) -> None:
""" """
trigger action which will be called before the stop of the application trigger action which will be called before the stop of the application
""" """
self.logger.info("removing instance %s in %s", self.worker, self.address) self.logger.info("removing instance %s at %s", self.worker, self.address)
with self._lock: with self._lock:
if self._timer is None: for timer in self._timers:
return timer.cancel() # cancel running timers
self._timers.clear() # clear queue
self._timer.cancel() # cancel remaining timers
self._timer = None # reset state
def ping(self) -> None: def ping(self) -> None:
""" """
register itself as alive worker and update the timer register itself as alive worker and update the timer
""" """
with self._lock: with self._lock:
if self._timer is None: # no active timer set, exit loop try:
return self._timers.popleft() # make sure that we have something to pull
self.register() self.register()
self.create_timer() self.create_timer()
except IndexError:
self.logger.warning("trigger is terminated, shutdown update cycle")

View File

@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from ahriman.core.formatters.printer import Printer
from ahriman.core.formatters.aur_printer import AurPrinter from ahriman.core.formatters.aur_printer import AurPrinter
from ahriman.core.formatters.build_printer import BuildPrinter from ahriman.core.formatters.build_printer import BuildPrinter
from ahriman.core.formatters.changes_printer import ChangesPrinter from ahriman.core.formatters.changes_printer import ChangesPrinter
@ -24,7 +26,6 @@ from ahriman.core.formatters.configuration_paths_printer import ConfigurationPat
from ahriman.core.formatters.configuration_printer import ConfigurationPrinter from ahriman.core.formatters.configuration_printer import ConfigurationPrinter
from ahriman.core.formatters.package_printer import PackagePrinter from ahriman.core.formatters.package_printer import PackagePrinter
from ahriman.core.formatters.patch_printer import PatchPrinter from ahriman.core.formatters.patch_printer import PatchPrinter
from ahriman.core.formatters.printer import Printer
from ahriman.core.formatters.repository_printer import RepositoryPrinter from ahriman.core.formatters.repository_printer import RepositoryPrinter
from ahriman.core.formatters.status_printer import StatusPrinter from ahriman.core.formatters.status_printer import StatusPrinter
from ahriman.core.formatters.string_printer import StringPrinter from ahriman.core.formatters.string_printer import StringPrinter

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from ahriman.core.formatters.printer import Printer from ahriman.core.formatters import Printer
from ahriman.models.changes import Changes from ahriman.models.changes import Changes
from ahriman.models.property import Property from ahriman.models.property import Property

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from ahriman.core.formatters.printer import Printer from ahriman.core.formatters import Printer
class StringPrinter(Printer): class StringPrinter(Printer):

View File

@ -77,12 +77,12 @@ class SyncHttpClient(LazyLogging):
return session return session
@staticmethod @staticmethod
def exception_response_text(exception: requests.RequestException) -> str: def exception_response_text(exception: requests.exceptions.RequestException) -> str:
""" """
safe response exception text generation safe response exception text generation
Args: Args:
exception(requests.RequestException): exception raised exception(requests.exceptions.RequestException): exception raised
Returns: Returns:
str: text of the response if it is not None and empty string otherwise str: text of the response if it is not None and empty string otherwise

View File

@ -0,0 +1,108 @@
#
# 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 re
from aiohttp.web import AccessLogger, BaseRequest, StreamResponse
class FilteredAccessLogger(AccessLogger):
"""
access logger implementation with log filter enabled
Attributes:
DISTRIBUTED_PATH_REGEX(str): (class attribute) regex used for distributed system uri
HEALTH_PATH_REGEX(re.Pattern): (class attribute) regex for health check endpoint
LOG_PATH_REGEX(re.Pattern): (class attribute) regex for logs uri
PROCESS_PATH_REGEX(re.Pattern): (class attribute) regex for process uri
"""
DISTRIBUTED_PATH_REGEX = "/api/v1/distributed"
HEALTH_PATH_REGEX = "/api/v1/info"
LOG_PATH_REGEX = re.compile(r"^/api/v1/packages/[^/]+/logs$")
# technically process id is uuid, but we might change it later
PROCESS_PATH_REGEX = re.compile(r"^/api/v1/service/process/[^/]+$")
@staticmethod
def is_distributed_post(request: BaseRequest) -> bool:
"""
check if the request is for distributed services ping
Args:
request(BaseRequest): http reqeust descriptor
Returns:
bool: True in case if request is distributed service ping endpoint and False otherwise
"""
return request.method == "POST" and FilteredAccessLogger.DISTRIBUTED_PATH_REGEX == request.path
@staticmethod
def is_info_get(request: BaseRequest) -> bool:
"""
check if the request is for health check
Args:
request(BaseRequest): http reqeust descriptor
Returns:
bool: True in case if request is health check and false otherwise
"""
return request.method == "GET" and FilteredAccessLogger.HEALTH_PATH_REGEX == request.path
@staticmethod
def is_logs_post(request: BaseRequest) -> bool:
"""
check if request looks like logs posting
Args:
request(BaseRequest): http reqeust descriptor
Returns:
bool: True in case if request looks like logs positing and False otherwise
"""
return request.method == "POST" and FilteredAccessLogger.LOG_PATH_REGEX.match(request.path) is not None
@staticmethod
def is_process_get(request: BaseRequest) -> bool:
"""
check if request looks like process status request
Args:
request(BaseRequest): http reqeust descriptor
Returns:
bool: True in case if request looks like process status request and False otherwise
"""
return request.method == "GET" and FilteredAccessLogger.PROCESS_PATH_REGEX.match(request.path) is not None
def log(self, request: BaseRequest, response: StreamResponse, time: float) -> None:
"""
access log with enabled filter by request path
Args:
request(BaseRequest): http reqeust descriptor
response(StreamResponse): streaming response object
time(float): log record timestamp
"""
if self.is_distributed_post(request) \
or self.is_info_get(request) \
or self.is_logs_post(request) \
or self.is_process_get(request):
return
AccessLogger.log(self, request, response, time)

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from logging import NullHandler # pylint: disable=imports-out-of-order from logging import NullHandler
from typing import Any from typing import Any

View File

@ -40,7 +40,7 @@ class LogLoader:
DEFAULT_LOG_FORMAT = "[%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d %(funcName)s]: %(message)s" DEFAULT_LOG_FORMAT = "[%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d %(funcName)s]: %(message)s"
DEFAULT_LOG_LEVEL = logging.DEBUG DEFAULT_LOG_LEVEL = logging.DEBUG
DEFAULT_SYSLOG_DEVICE = Path("/") / "dev" / "log" DEFAULT_SYSLOG_DEVICE = Path("/dev") / "log"
@staticmethod @staticmethod
def handler(selected: LogHandler | None) -> LogHandler: def handler(selected: LogHandler | None) -> LogHandler:

View File

@ -18,8 +18,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.report.report import Report
from ahriman.core.triggers import Trigger from ahriman.core.triggers import Trigger
from ahriman.core.report.report import Report
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId
from ahriman.models.result import Result from ahriman.models.result import Result

View File

@ -190,8 +190,7 @@ class Watcher(LazyLogging):
Returns: Returns:
list[PkgbuildPatch]: list of patches which are stored for the package list[PkgbuildPatch]: list of patches which are stored for the package
""" """
# patches are package base based, we don't know (and don't differentiate) to which package does them belong self.package_get(package_base)
# so here we skip checking if package exists or not
variables = [variable] if variable is not None else None variables = [variable] if variable is not None else None
return self.database.patches_list(package_base, variables).get(package_base, []) return self.database.patches_list(package_base, variables).get(package_base, [])

View File

@ -94,7 +94,7 @@ class WebClient(Client, SyncAhrimanClient):
Returns: Returns:
str: full url for web service for logs str: full url for web service for logs
""" """
return f"{self.address}/api/v1/packages/{urlencode(package_base)}/changes" return f"{self.address}/api/v1/packages/{package_base}/changes"
def _logs_url(self, package_base: str) -> str: def _logs_url(self, package_base: str) -> str:
""" """
@ -106,7 +106,7 @@ class WebClient(Client, SyncAhrimanClient):
Returns: Returns:
str: full url for web service for logs str: full url for web service for logs
""" """
return f"{self.address}/api/v1/packages/{urlencode(package_base)}/logs" return f"{self.address}/api/v1/packages/{package_base}/logs"
def _package_url(self, package_base: str = "") -> str: def _package_url(self, package_base: str = "") -> str:
""" """
@ -118,7 +118,7 @@ class WebClient(Client, SyncAhrimanClient):
Returns: Returns:
str: full url of web service for specific package base str: full url of web service for specific package base
""" """
suffix = f"/{urlencode(package_base)}" if package_base else "" suffix = f"/{package_base}" if package_base else ""
return f"{self.address}/api/v1/packages{suffix}" return f"{self.address}/api/v1/packages{suffix}"
def _status_url(self) -> str: def _status_url(self) -> str:

View File

@ -51,7 +51,7 @@ class MirrorlistGenerator(PkgbuildGenerator):
# configuration fields # configuration fields
self.servers = configuration.getlist(section, "servers") self.servers = configuration.getlist(section, "servers")
self.path = configuration.getpath( self.path = configuration.getpath(
section, "path", fallback=Path("/") / "etc" / "pacman.d" / f"{repository_id.name}-mirrorlist") section, "path", fallback=Path("/etc") / "pacman.d" / f"{repository_id.name}-mirrorlist")
self.path = self.path.relative_to("/") # in pkgbuild we are always operating with relative to / path self.path = self.path.relative_to("/") # in pkgbuild we are always operating with relative to / path
# pkgbuild description fields # pkgbuild description fields
self.pkgbuild_pkgname = configuration.get(section, "package", fallback=f"{repository_id.name}-mirrorlist") self.pkgbuild_pkgname = configuration.get(section, "package", fallback=f"{repository_id.name}-mirrorlist")

View File

@ -19,7 +19,7 @@
# #
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable, Iterable from collections.abc import Iterable, Callable
from typing import Any, Self from typing import Any, Self
from ahriman.models.package import Package from ahriman.models.package import Package

View File

@ -17,9 +17,9 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from secrets import token_urlsafe as generate_password
from dataclasses import dataclass, replace from dataclasses import dataclass, replace
from passlib.hash import sha512_crypt from passlib.hash import sha512_crypt
from secrets import token_urlsafe as generate_password
from typing import Self from typing import Self
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess

View File

@ -35,7 +35,7 @@ from ahriman.web.schemas.package_names_schema import PackageNamesSchema
from ahriman.web.schemas.package_patch_schema import PackagePatchSchema from ahriman.web.schemas.package_patch_schema import PackagePatchSchema
from ahriman.web.schemas.package_properties_schema import PackagePropertiesSchema from ahriman.web.schemas.package_properties_schema import PackagePropertiesSchema
from ahriman.web.schemas.package_schema import PackageSchema from ahriman.web.schemas.package_schema import PackageSchema
from ahriman.web.schemas.package_status_schema import PackageStatusSchema, PackageStatusSimplifiedSchema from ahriman.web.schemas.package_status_schema import PackageStatusSimplifiedSchema, PackageStatusSchema
from ahriman.web.schemas.pagination_schema import PaginationSchema from ahriman.web.schemas.pagination_schema import PaginationSchema
from ahriman.web.schemas.patch_name_schema import PatchNameSchema from ahriman.web.schemas.patch_name_schema import PatchNameSchema
from ahriman.web.schemas.patch_schema import PatchSchema from ahriman.web.schemas.patch_schema import PatchSchema

View File

@ -25,22 +25,6 @@ from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema
from ahriman.web.schemas.status_schema import StatusSchema from ahriman.web.schemas.status_schema import StatusSchema
class PackageStatusSchema(Schema):
"""
response package status schema
"""
package = fields.Nested(PackageSchema(), required=True, metadata={
"description": "Package description",
})
status = fields.Nested(StatusSchema(), required=True, metadata={
"description": "Last package status",
})
repository = fields.Nested(RepositoryIdSchema(), required=True, metadata={
"description": "Repository identifier",
})
class PackageStatusSimplifiedSchema(Schema): class PackageStatusSimplifiedSchema(Schema):
""" """
special request package status schema special request package status schema
@ -55,3 +39,19 @@ class PackageStatusSimplifiedSchema(Schema):
repository = fields.Nested(RepositoryIdSchema(), required=True, metadata={ repository = fields.Nested(RepositoryIdSchema(), required=True, metadata={
"description": "Repository identifier", "description": "Repository identifier",
}) })
class PackageStatusSchema(Schema):
"""
response package status schema
"""
package = fields.Nested(PackageSchema(), required=True, metadata={
"description": "Package description",
})
status = fields.Nested(StatusSchema(), required=True, metadata={
"description": "Last package status",
})
repository = fields.Nested(RepositoryIdSchema(), required=True, metadata={
"description": "Repository identifier",
})

View File

@ -17,8 +17,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Request, StreamResponse, View
from aiohttp_cors import CorsViewMixin # type: ignore[import-untyped] from aiohttp_cors import CorsViewMixin # type: ignore[import-untyped]
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Request, StreamResponse, View
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from typing import TypeVar from typing import TypeVar

View File

@ -19,8 +19,8 @@
# #
import aiohttp_apispec # type: ignore[import-untyped] import aiohttp_apispec # type: ignore[import-untyped]
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
from collections.abc import Callable from collections.abc import Callable
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess
from ahriman.models.worker import Worker from ahriman.models.worker import Worker

View File

@ -22,7 +22,7 @@ import aiohttp_apispec # type: ignore[import-untyped]
from aiohttp.web import HTTPBadRequest, Response, json_response from aiohttp.web import HTTPBadRequest, Response, json_response
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, ProcessIdSchema, RepositoryIdSchema, UpdateFlagsSchema from ahriman.web.schemas import AuthSchema, ErrorSchema, ProcessIdSchema, UpdateFlagsSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView from ahriman.web.views.base import BaseView

View File

@ -20,8 +20,8 @@
import aiohttp_apispec # type: ignore[import-untyped] import aiohttp_apispec # type: ignore[import-untyped]
import itertools import itertools
from aiohttp.web import HTTPNoContent, Response, json_response
from collections.abc import Callable from collections.abc import Callable
from aiohttp.web import HTTPNoContent, Response, json_response
from ahriman.models.build_status import BuildStatus from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package from ahriman.models.package import Package

View File

@ -21,6 +21,7 @@ import aiohttp_apispec # type: ignore[import-untyped]
from aiohttp.web import HTTPNoContent, HTTPNotFound, Response, json_response from aiohttp.web import HTTPNoContent, HTTPNotFound, Response, json_response
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PatchNameSchema, PatchSchema from ahriman.web.schemas import AuthSchema, ErrorSchema, PatchNameSchema, PatchSchema
from ahriman.web.views.base import BaseView from ahriman.web.views.base import BaseView
@ -75,7 +76,7 @@ class PatchView(StatusViewGuard, BaseView):
200: {"description": "Success response", "schema": PatchSchema}, 200: {"description": "Success response", "schema": PatchSchema},
401: {"description": "Authorization required", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema},
404: {"description": "Patch name is unknown", "schema": ErrorSchema}, 404: {"description": "Package base and/or patch name are unknown", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema},
}, },
security=[{"token": [GET_PERMISSION]}], security=[{"token": [GET_PERMISSION]}],
@ -90,12 +91,15 @@ class PatchView(StatusViewGuard, BaseView):
Response: 200 with package patch on success Response: 200 with package patch on success
Raises: Raises:
HTTPNotFound: if package patch is unknown HTTPNotFound: if package base is unknown
""" """
package_base = self.request.match_info["package"] package_base = self.request.match_info["package"]
variable = self.request.match_info["patch"] variable = self.request.match_info["patch"]
try:
patches = self.service().patches_get(package_base, variable) patches = self.service().patches_get(package_base, variable)
except UnknownPackageError:
raise HTTPNotFound(reason=f"Package {package_base} is unknown")
selected = next((patch for patch in patches if patch.key == variable), None) selected = next((patch for patch in patches if patch.key == variable), None)
if selected is None: if selected is None:

View File

@ -19,8 +19,9 @@
# #
import aiohttp_apispec # type: ignore[import-untyped] import aiohttp_apispec # type: ignore[import-untyped]
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.pkgbuild_patch import PkgbuildPatch from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PatchSchema from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PatchSchema
@ -49,6 +50,7 @@ class PatchesView(StatusViewGuard, BaseView):
200: {"description": "Success response", "schema": PatchSchema(many=True)}, 200: {"description": "Success response", "schema": PatchSchema(many=True)},
401: {"description": "Authorization required", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema},
404: {"description": "Package base is unknown", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema},
}, },
security=[{"token": [GET_PERMISSION]}], security=[{"token": [GET_PERMISSION]}],
@ -61,9 +63,15 @@ class PatchesView(StatusViewGuard, BaseView):
Returns: Returns:
Response: 200 with package patches on success Response: 200 with package patches on success
Raises:
HTTPNotFound: if package base is unknown
""" """
package_base = self.request.match_info["package"] package_base = self.request.match_info["package"]
try:
patches = self.service().patches_get(package_base, None) patches = self.service().patches_get(package_base, None)
except UnknownPackageError:
raise HTTPNotFound(reason=f"Package {package_base} is unknown")
response = [patch.view() for patch in patches] response = [patch.view() for patch in patches]
return json_response(response) return json_response(response)

View File

@ -26,7 +26,7 @@ from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.counters import Counters from ahriman.models.counters import Counters
from ahriman.models.internal_status import InternalStatus from ahriman.models.internal_status import InternalStatus
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, InternalStatusSchema, RepositoryIdSchema, StatusSchema from ahriman.web.schemas import AuthSchema, ErrorSchema, InternalStatusSchema, StatusSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard from ahriman.web.views.status_view_guard import StatusViewGuard

View File

@ -29,6 +29,7 @@ from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite from ahriman.core.database import SQLite
from ahriman.core.distributed import WorkersCache from ahriman.core.distributed import WorkersCache
from ahriman.core.exceptions import InitializeError from ahriman.core.exceptions import InitializeError
from ahriman.core.log.filtered_access_logger import FilteredAccessLogger
from ahriman.core.spawn import Spawn from ahriman.core.spawn import Spawn
from ahriman.core.status.watcher import Watcher from ahriman.core.status.watcher import Watcher
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId
@ -121,7 +122,7 @@ def run_server(application: Application) -> None:
unix_socket = _create_socket(configuration, application) unix_socket = _create_socket(configuration, application)
run_app(application, host=host, port=port, sock=unix_socket, handle_signals=True, run_app(application, host=host, port=port, sock=unix_socket, handle_signals=True,
access_log=logging.getLogger("http")) access_log=logging.getLogger("http"), access_log_class=FilteredAccessLogger)
def setup_server(configuration: Configuration, spawner: Spawn, repositories: list[RepositoryId]) -> Application: def setup_server(configuration: Configuration, spawner: Spawn, repositories: list[RepositoryId]) -> Application:

View File

@ -100,7 +100,7 @@ def test_add_local_cache(application_packages: ApplicationPackages, package_ahri
""" """
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman) mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
mocker.patch("pathlib.Path.is_dir", autospec=True, mocker.patch("pathlib.Path.is_dir", autospec=True,
side_effect=lambda p: p.is_relative_to(application_packages.repository.paths.cache)) side_effect=lambda p: True if p.is_relative_to(application_packages.repository.paths.cache) else False)
init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init") init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init")
copytree_mock = mocker.patch("shutil.copytree") copytree_mock = mocker.patch("shutil.copytree")
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert") build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")

View File

@ -47,7 +47,7 @@ def test_iter(updates_iterator: UpdatesIterator) -> None:
""" """
must return self as iterator must return self as iterator
""" """
assert iter(updates_iterator) == updates_iterator assert updates_iterator.__iter__() == updates_iterator
def test_next(updates_iterator: UpdatesIterator, package_ahriman: Package, mocker: MockerFixture) -> None: def test_next(updates_iterator: UpdatesIterator, package_ahriman: Package, mocker: MockerFixture) -> None:
@ -58,7 +58,7 @@ def test_next(updates_iterator: UpdatesIterator, package_ahriman: Package, mocke
side_effect=[([package_ahriman.base], 2), (None, 2), StopIteration]) side_effect=[([package_ahriman.base], 2), (None, 2), StopIteration])
sleep_mock = mocker.patch("time.sleep") sleep_mock = mocker.patch("time.sleep")
updates = list(updates_iterator) updates = [packages for packages in updates_iterator]
assert updates == [[package_ahriman.base], None] assert updates == [[package_ahriman.base], None]
sleep_mock.assert_has_calls([MockCall(0.5), MockCall(0.5)]) sleep_mock.assert_has_calls([MockCall(0.5), MockCall(0.5)])

View File

@ -156,7 +156,7 @@ def test_configuration_create_ahriman(args: argparse.Namespace, configuration: C
def test_configuration_create_ahriman_no_multilib(args: argparse.Namespace, configuration: Configuration, def test_configuration_create_ahriman_no_multilib(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None: repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
""" """
must create configuration for the service without multilib repository must create configuration for the service without multilib repository
""" """

View File

@ -558,13 +558,14 @@ def user() -> User:
@pytest.fixture @pytest.fixture
def watcher(repository_id: RepositoryId, database: SQLite) -> Watcher: def watcher(repository_id: RepositoryId, database: SQLite, repository: Repository) -> Watcher:
""" """
package status watcher fixture package status watcher fixture
Args: Args:
repository_id(RepositoryId): repository identifier fixture repository_id(RepositoryId): repository identifier fixture
database(SQLite): database fixture database(SQLite): database fixture
repository(Repository): repository fixture
Returns: Returns:
Watcher: package status watcher test instance Watcher: package status watcher test instance

View File

@ -48,7 +48,7 @@ def test_multisearch_single(aur_package_ahriman: AURPackage, pacman: Pacman, moc
search_mock.assert_called_once_with("ahriman", pacman=pacman) search_mock.assert_called_once_with("ahriman", pacman=pacman)
def test_remote_git_url(remote: Remote) -> None: def test_remote_git_url(remote: Remote, pacman: Pacman) -> None:
""" """
must raise NotImplemented for missing remote git url must raise NotImplemented for missing remote git url
""" """
@ -56,7 +56,7 @@ def test_remote_git_url(remote: Remote) -> None:
remote.remote_git_url("package", "repositorys") remote.remote_git_url("package", "repositorys")
def test_remote_web_url(remote: Remote) -> None: def test_remote_web_url(remote: Remote, pacman: Pacman) -> None:
""" """
must raise NotImplemented for missing remote web url must raise NotImplemented for missing remote web url
""" """

Some files were not shown because too many files have changed in this diff Show More