mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-11-17 05:53:41 +00:00
Compare commits
17 Commits
2.12.0
...
5ddc08fce7
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ddc08fce7 | |||
| f2f6f6df70 | |||
| 2760b36977 | |||
| a689448854 | |||
| aef3cb95bc | |||
| d72677aa29 | |||
| 82d1be52a8 | |||
| 7536d6bb82 | |||
| b050c409cf | |||
| d77cf7c4bb | |||
| e03fcbfab5 | |||
| 62dd77317d | |||
| 95056cfbe7 | |||
| 2d31a415ce | |||
| 5cbeec40f8 | |||
| 4a644fc80f | |||
| d1aeeda975 |
@ -122,7 +122,7 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
def __hash__(self) -> int: ... # basically any magic (or look-alike) method
|
def __hash__(self) -> int: ... # basically any magic (or look-alike) method
|
||||||
```
|
```
|
||||||
|
|
||||||
Methods inside one group should be ordered alphabetically, the only exceptions are `__init__` (`__post_init__` for dataclasses) and `__new__` methods which should be defined first. For test methods it is recommended to follow the order in which functions are defined.
|
Methods inside one group should be ordered alphabetically, the only exceptions are `__init__` (`__post_init__` for dataclasses), `__new__` and `__del__` methods which should be defined first. For test methods it is recommended to follow the order in which functions are defined.
|
||||||
|
|
||||||
Though, we would like to highlight abstract methods (i.e. ones which raise `NotImplementedError`), we still keep in global order at the moment.
|
Though, we would like to highlight abstract methods (i.e. ones which raise `NotImplementedError`), we still keep in global order at the moment.
|
||||||
|
|
||||||
@ -225,3 +225,25 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
### Other checks
|
### Other checks
|
||||||
|
|
||||||
The projects also uses typing checks (provided by `mypy`) and some linter checks provided by `pylint` and `bandit`. Those checks must be passed successfully for any open pull requests.
|
The projects also uses typing checks (provided by `mypy`) and some linter checks provided by `pylint` and `bandit`. Those checks must be passed successfully for any open pull requests.
|
||||||
|
|
||||||
|
## Developers how to
|
||||||
|
|
||||||
|
### Run automated checks
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make check tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate documentation templates
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make specification
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create release
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make VERSION=x.y.z check tests release
|
||||||
|
```
|
||||||
|
|
||||||
|
The command above will also run checks first and will generate documentation, tags, etc., and will push them to GitHub. Other things will be handled by GitHub workflows automatically.
|
||||||
|
|||||||
4
Makefile
4
Makefile
@ -1,4 +1,4 @@
|
|||||||
.PHONY: archive archlinux check clean directory html push specification tests version
|
.PHONY: archive archlinux check clean directory html release specification tests version
|
||||||
.DEFAULT_GOAL := archlinux
|
.DEFAULT_GOAL := archlinux
|
||||||
|
|
||||||
PROJECT := ahriman
|
PROJECT := ahriman
|
||||||
@ -37,7 +37,7 @@ html: specification
|
|||||||
rm -rf docs/html
|
rm -rf docs/html
|
||||||
tox -e docs-html
|
tox -e docs-html
|
||||||
|
|
||||||
push: specification archlinux
|
release: specification archlinux
|
||||||
git add package/archlinux/PKGBUILD src/ahriman/__init__.py docs/ahriman-architecture.svg package/share/man/man1/ahriman.1 package/share/bash-completion/completions/_ahriman package/share/zsh/site-functions/_ahriman
|
git add package/archlinux/PKGBUILD src/ahriman/__init__.py docs/ahriman-architecture.svg package/share/man/man1/ahriman.1 package/share/bash-completion/completions/_ahriman package/share/zsh/site-functions/_ahriman
|
||||||
git commit -m "Release $(VERSION)"
|
git commit -m "Release $(VERSION)"
|
||||||
git tag "$(VERSION)"
|
git tag "$(VERSION)"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 993 KiB |
@ -1,6 +1,14 @@
|
|||||||
ahriman.application.application package
|
ahriman.application.application package
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
|
Subpackages
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 4
|
||||||
|
|
||||||
|
ahriman.application.application.workers
|
||||||
|
|
||||||
Submodules
|
Submodules
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
37
docs/ahriman.application.application.workers.rst
Normal file
37
docs/ahriman.application.application.workers.rst
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
ahriman.application.application.workers package
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
ahriman.application.application.workers.local\_updater module
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.application.application.workers.local_updater
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.application.application.workers.remote\_updater module
|
||||||
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.application.application.workers.remote_updater
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.application.application.workers.updater module
|
||||||
|
------------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.application.application.workers.updater
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.application.application.workers
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@ -20,6 +20,14 @@ ahriman.application.handlers.backup module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.application.handlers.change module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.application.handlers.change
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.application.handlers.clean module
|
ahriman.application.handlers.clean module
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -100,6 +100,14 @@ ahriman.core.database.migrations.m011\_repository\_name module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.core.database.migrations.m012\_last\_commit\_sha module
|
||||||
|
---------------------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.database.migrations.m012_last_commit_sha
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
Module contents
|
Module contents
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,14 @@ ahriman.core.database.operations.build\_operations module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.core.database.operations.changes\_operations module
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.database.operations.changes_operations
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.core.database.operations.logs\_operations module
|
ahriman.core.database.operations.logs\_operations module
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,14 @@ ahriman.core.formatters.build\_printer module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.core.formatters.changes\_printer module
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.formatters.changes_printer
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.core.formatters.configuration\_paths\_printer module
|
ahriman.core.formatters.configuration\_paths\_printer module
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,14 @@ ahriman.core.http package
|
|||||||
Submodules
|
Submodules
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
ahriman.core.http.sync\_ahriman\_client module
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.http.sync_ahriman_client
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.core.http.sync\_http\_client module
|
ahriman.core.http.sync\_http\_client module
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,14 @@ ahriman.core.repository.executor module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.core.repository.package\_info module
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.repository.package_info
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.core.repository.repository module
|
ahriman.core.repository.repository module
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,14 @@ ahriman.models.build\_status module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.models.changes module
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.models.changes
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.models.context\_key module
|
ahriman.models.context\_key module
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
@ -244,6 +252,14 @@ ahriman.models.waiter module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.models.worker module
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.models.worker
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
Module contents
|
Module contents
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,22 @@ ahriman.web.schemas.auth\_schema module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.schemas.build\_options\_schema module
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.schemas.build_options_schema
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.schemas.changes\_schema module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.schemas.changes_schema
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.schemas.counters\_schema module
|
ahriman.web.schemas.counters\_schema module
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,14 @@ ahriman.web.views.v1.status package
|
|||||||
Submodules
|
Submodules
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
ahriman.web.views.v1.status.changes module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.status.changes
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.views.v1.status.logs module
|
ahriman.web.views.v1.status.logs module
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,6 @@ Base configuration settings.
|
|||||||
* ``database`` - path to SQLite database, string, required.
|
* ``database`` - path to SQLite database, string, required.
|
||||||
* ``include`` - path to directory with configuration files overrides, string, optional.
|
* ``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.
|
||||||
* ``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.
|
|
||||||
|
|
||||||
``alpm:*`` groups
|
``alpm:*`` groups
|
||||||
-----------------
|
-----------------
|
||||||
@ -86,7 +85,8 @@ Build related configuration. Group name can refer to architecture, e.g. ``build:
|
|||||||
* ``makechrootpkg_flags`` - additional flags passed to ``makechrootpkg`` command, space separated list of strings, optional.
|
* ``makechrootpkg_flags`` - additional flags passed to ``makechrootpkg`` command, space separated list of strings, optional.
|
||||||
* ``triggers`` - list of ``ahriman.core.triggers.Trigger`` class implementation (e.g. ``ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger``) which will be loaded and run at the end of processing, space separated list of strings, optional. You can also specify triggers by their paths, e.g. ``/usr/lib/python3.10/site-packages/ahriman/core/report/report.py.ReportTrigger``. Triggers are run in the order of mention.
|
* ``triggers`` - list of ``ahriman.core.triggers.Trigger`` class implementation (e.g. ``ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger``) which will be loaded and run at the end of processing, space separated list of strings, optional. You can also specify triggers by their paths, e.g. ``/usr/lib/python3.10/site-packages/ahriman/core/report/report.py.ReportTrigger``. Triggers are run in the order of mention.
|
||||||
* ``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, int, optional, default ``604800``.
|
* ``vcs_allowed_age`` - maximal age in seconds of the VCS packages before their version will be updated with its remote source, integer, optional, default ``604800``.
|
||||||
|
* ``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.
|
||||||
|
|
||||||
``repository`` group
|
``repository`` group
|
||||||
--------------------
|
--------------------
|
||||||
@ -103,6 +103,18 @@ Settings for signing packages or repository. Group name can refer to architectur
|
|||||||
* ``target`` - configuration flag to enable signing, space separated list of strings, required. Allowed values are ``package`` (sign each package separately), ``repository`` (sign repository database file).
|
* ``target`` - configuration flag to enable signing, space separated list of strings, required. Allowed values are ``package`` (sign each package separately), ``repository`` (sign repository database file).
|
||||||
* ``key`` - default PGP key, string, required. This key will also be used for database signing if enabled.
|
* ``key`` - default PGP key, string, required. This key will also be used for database signing if enabled.
|
||||||
|
|
||||||
|
``status`` group
|
||||||
|
----------------
|
||||||
|
|
||||||
|
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.
|
||||||
|
* ``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.
|
||||||
|
* ``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``.
|
||||||
|
* ``username`` - username to authorize in web service in order to update service status, string, required in case if authorization enabled.
|
||||||
|
|
||||||
``web`` group
|
``web`` group
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
@ -116,15 +128,12 @@ Web server settings. If any of ``host``/``port`` is not set, web integration wil
|
|||||||
* ``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.
|
||||||
* ``password`` - password to authorize in web service in order to update service status, string, required in case if authorization enabled.
|
* ``port`` - port to bind, integer, optional.
|
||||||
* ``port`` - port to bind, int, optional.
|
|
||||||
* ``static_path`` - path to directory with static files, string, required.
|
* ``static_path`` - path to directory with static files, string, required.
|
||||||
* ``templates`` - path to templates directories, space separated list of strings, required.
|
* ``templates`` - path to templates directories, space separated list of strings, required.
|
||||||
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
|
|
||||||
* ``unix_socket`` - path to the listening unix socket, string, optional. If set, server will create the socket on the specified address which can (and will) be used by application. Note, that unlike usual host/port configuration, unix socket allows to perform requests without authorization.
|
* ``unix_socket`` - path to the listening unix socket, string, optional. If set, server will create the socket on the specified address which can (and will) be used by application. Note, that unlike usual host/port configuration, unix socket allows to perform requests without authorization.
|
||||||
* ``unix_socket_unsafe`` - set unsafe (o+w) permissions to unix socket, boolean, optional, default ``yes``. This option is enabled by default, because it is supposed that unix socket is created in safe environment (only web service is supposed to be used in unsafe), but it can be disabled by configuration.
|
* ``unix_socket_unsafe`` - set unsafe (o+w) permissions to unix socket, boolean, optional, default ``yes``. This option is enabled by default, because it is supposed that unix socket is created in safe environment (only web service is supposed to be used in unsafe), but it can be disabled by configuration.
|
||||||
* ``username`` - username to authorize in web service in order to update service status, string, required in case if authorization enabled.
|
* ``wait_timeout`` - wait timeout in seconds, maximum amount of time to be waited before lock will be free, integer, optional.
|
||||||
* ``wait_timeout`` - wait timeout in seconds, maximum amount of time to be waited before lock will be free, int, optional.
|
|
||||||
|
|
||||||
``keyring`` group
|
``keyring`` group
|
||||||
--------------------
|
--------------------
|
||||||
@ -237,7 +246,7 @@ Section name must be either ``email`` (plus optional architecture name, e.g. ``e
|
|||||||
* ``link_path`` - prefix for HTML links, string, required.
|
* ``link_path`` - prefix for HTML links, string, required.
|
||||||
* ``no_empty_report`` - skip report generation for empty packages list, boolean, optional, default ``yes``.
|
* ``no_empty_report`` - skip report generation for empty packages list, boolean, optional, default ``yes``.
|
||||||
* ``password`` - SMTP password to authenticate, string, optional.
|
* ``password`` - SMTP password to authenticate, string, optional.
|
||||||
* ``port`` - SMTP port for sending emails, int, required.
|
* ``port`` - SMTP port for sending emails, integer, required.
|
||||||
* ``receivers`` - SMTP receiver addresses, space separated list of strings, required.
|
* ``receivers`` - SMTP receiver addresses, space separated list of strings, required.
|
||||||
* ``sender`` - SMTP sender address, string, required.
|
* ``sender`` - SMTP sender address, string, required.
|
||||||
* ``ssl`` - SSL mode for SMTP connection, one of ``ssl``, ``starttls``, ``disabled``, optional, default ``disabled``.
|
* ``ssl`` - SSL mode for SMTP connection, one of ``ssl``, ``starttls``, ``disabled``, optional, default ``disabled``.
|
||||||
@ -267,7 +276,7 @@ Section name must be either ``remote-call`` (plus optional architecture name, e.
|
|||||||
* ``aur`` - check for AUR packages updates, boolean, optional, default ``no``.
|
* ``aur`` - check for AUR packages updates, boolean, optional, default ``no``.
|
||||||
* ``local`` - check for local packages updates, boolean, optional, default ``no``.
|
* ``local`` - check for local packages updates, boolean, optional, default ``no``.
|
||||||
* ``manual`` - update manually built packages, boolean, optional, default ``no``.
|
* ``manual`` - update manually built packages, boolean, optional, default ``no``.
|
||||||
* ``wait_timeout`` - maximum amount of time in seconds to be waited before remote process will be terminated, int, optional, default ``-1``.
|
* ``wait_timeout`` - maximum amount of time in seconds to be waited before remote process will be terminated, integer, optional, default ``-1``.
|
||||||
|
|
||||||
``telegram`` type
|
``telegram`` type
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^
|
||||||
@ -282,7 +291,7 @@ Section name must be either ``telegram`` (plus optional architecture name, e.g.
|
|||||||
* ``template`` - Jinja2 template name, string, required.
|
* ``template`` - Jinja2 template name, string, required.
|
||||||
* ``template_type`` - ``parse_mode`` to be passed to telegram API, one of ``MarkdownV2``, ``HTML``, ``Markdown``, string, optional, default ``HTML``.
|
* ``template_type`` - ``parse_mode`` to be passed to telegram API, one of ``MarkdownV2``, ``HTML``, ``Markdown``, string, optional, default ``HTML``.
|
||||||
* ``templates`` - path to templates directories, space separated list of strings, required.
|
* ``templates`` - path to templates directories, space separated list of strings, required.
|
||||||
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
|
* ``timeout`` - HTTP request timeout in seconds, integer, optional, default is ``30``.
|
||||||
|
|
||||||
``upload`` group
|
``upload`` group
|
||||||
----------------
|
----------------
|
||||||
@ -312,7 +321,7 @@ This feature requires GitHub key creation (see below). Section name must be eith
|
|||||||
#. Generate new token. Required scope is ``public_repo`` (or ``repo`` for private repository support).
|
#. Generate new token. Required scope is ``public_repo`` (or ``repo`` for private repository support).
|
||||||
|
|
||||||
* ``repository`` - GitHub repository name, string, required. Repository must be created before any action and must have active branch (e.g. with readme).
|
* ``repository`` - GitHub repository name, string, required. Repository must be created before any action and must have active branch (e.g. with readme).
|
||||||
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
|
* ``timeout`` - HTTP request timeout in seconds, integer, optional, default is ``30``.
|
||||||
* ``use_full_release_name`` - if set to ``yes``, the release will contain both repository name and architecture, and only architecture otherwise, boolean, optional, default ``no`` (legacy behavior).
|
* ``use_full_release_name`` - if set to ``yes``, the release will contain both repository name and architecture, and only architecture otherwise, boolean, optional, default ``no`` (legacy behavior).
|
||||||
* ``username`` - GitHub authorization user, string, required. Basically the same as ``owner``.
|
* ``username`` - GitHub authorization user, string, required. Basically the same as ``owner``.
|
||||||
|
|
||||||
@ -322,7 +331,7 @@ This feature requires GitHub key creation (see below). Section name must be eith
|
|||||||
Section name must be either ``remote-service`` (plus optional architecture name, e.g. ``remote-service:x86_64``) or random name with ``type`` set.
|
Section name must be either ``remote-service`` (plus optional architecture name, e.g. ``remote-service:x86_64``) or random name with ``type`` set.
|
||||||
|
|
||||||
* ``type`` - type of the report, string, optional, must be set to ``remote-service`` if exists.
|
* ``type`` - type of the report, string, optional, must be set to ``remote-service`` if exists.
|
||||||
* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
|
* ``timeout`` - HTTP request timeout in seconds, integer, optional, default is ``30``.
|
||||||
|
|
||||||
``rsync`` type
|
``rsync`` type
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
@ -341,7 +350,7 @@ Requires ``boto3`` library to be installed. Section name must be either ``s3`` (
|
|||||||
* ``type`` - type of the upload, string, optional, must be set to ``s3`` if exists.
|
* ``type`` - type of the upload, string, optional, must be set to ``s3`` if exists.
|
||||||
* ``access_key`` - AWS access key ID, string, required.
|
* ``access_key`` - AWS access key ID, string, required.
|
||||||
* ``bucket`` - bucket name (e.g. ``bucket``), string, required.
|
* ``bucket`` - bucket name (e.g. ``bucket``), string, required.
|
||||||
* ``chunk_size`` - chunk size for calculating entity tags, int, optional, default 8 * 1024 * 1024.
|
* ``chunk_size`` - chunk size for calculating entity tags, integer, optional, default 8 * 1024 * 1024.
|
||||||
* ``object_path`` - path prefix for stored objects, string, optional. If none set, the prefix as in repository tree will be used.
|
* ``object_path`` - path prefix for stored objects, string, optional. If none set, the prefix as in repository tree will be used.
|
||||||
* ``region`` - bucket region (e.g. ``eu-central-1``), string, required.
|
* ``region`` - bucket region (e.g. ``eu-central-1``), string, required.
|
||||||
* ``secret_key`` - AWS secret access key, string, required.
|
* ``secret_key`` - AWS secret access key, string, required.
|
||||||
145
docs/faq.rst
145
docs/faq.rst
@ -114,8 +114,8 @@ But for some cases you would like to have multiple different reports with the sa
|
|||||||
type = email
|
type = email
|
||||||
...
|
...
|
||||||
|
|
||||||
How do I add new package
|
How to add new package
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. code-block:: shell
|
.. code-block:: shell
|
||||||
|
|
||||||
@ -237,6 +237,27 @@ Normally the service handles VCS packages correctly, however it requires additio
|
|||||||
|
|
||||||
pacman -S breezy darcs mercurial subversion
|
pacman -S breezy darcs mercurial subversion
|
||||||
|
|
||||||
|
How to review changes before build
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In this scenario, the update process must be separated to several stages. First, it is required to check updates:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
sudo -u ahriman ahriman repo-check
|
||||||
|
|
||||||
|
During the check process, the service will generate changes from the last known commit and will send it to remote service. In order to verify source files changes, the web interface or special subcommand can be used:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
ahriman package-changes ahriman
|
||||||
|
|
||||||
|
After validation, the operator can run update process with approved list of packages, e.g.:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
sudo -u ahriman ahriman repo-update ahriman
|
||||||
|
|
||||||
How to remove package
|
How to remove package
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
@ -869,12 +890,12 @@ Worker nodes configuration
|
|||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
[web]
|
[status]
|
||||||
address = master.example.com
|
address = https://master.example.com
|
||||||
username = worker-user
|
username = worker-user
|
||||||
password = very-secure-password
|
password = very-secure-password
|
||||||
|
|
||||||
As it has been mentioned above, ``web.address`` must be available for workers. In case if unix socket is used, it can be passed as ``web.unix_socket`` variable as usual. Optional ``web.username``/``web.password`` can be supplied in case if authentication was enabled on master node.
|
As it has been mentioned above, ``status.address`` must be available for workers. In case if unix socket is used, it can be passed in the same option as usual. Optional ``status.username``/``status.password`` can be supplied in case if authentication was enabled on master node.
|
||||||
|
|
||||||
#.
|
#.
|
||||||
Each worker must call master node on success:
|
Each worker must call master node on success:
|
||||||
@ -958,7 +979,7 @@ The user ``worker-user`` has been created additionally. Worker node config (``wo
|
|||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
[web]
|
[status]
|
||||||
address = http://172.17.0.1:8080
|
address = http://172.17.0.1:8080
|
||||||
username = worker-user
|
username = worker-user
|
||||||
password = very-secure-password
|
password = very-secure-password
|
||||||
@ -1001,6 +1022,116 @@ This action must be done in two steps:
|
|||||||
#. Remove package on worker.
|
#. Remove package on worker.
|
||||||
#. Remove package on master node.
|
#. Remove package on master node.
|
||||||
|
|
||||||
|
Delegate builds to remote workers
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This setup heavily uses upload feature described above and, in addition, also delegates build process automatically to build machines. Same as above, there must be at least two instances available (``master`` and ``worker``), however, all ``worker`` nodes must be run in the web service mode.
|
||||||
|
|
||||||
|
Master node configuration
|
||||||
|
"""""""""""""""""""""""""
|
||||||
|
|
||||||
|
In addition to the configuration above, the worker list must be defined in configuration file (``build.workers`` option), i.e.:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[build]
|
||||||
|
workers = https://worker1.example.com https://worker2.example.com
|
||||||
|
|
||||||
|
[web]
|
||||||
|
enable_archive_upload = yes
|
||||||
|
wait_timeout = 0
|
||||||
|
|
||||||
|
In the example above, ``https://worker1.example.com`` and ``https://worker2.example.com`` are remote ``worker`` node addresses available for ``master`` node.
|
||||||
|
|
||||||
|
In case if authentication is required (which is recommended way to setup it), it can be set by using ``status`` section as usual.
|
||||||
|
|
||||||
|
Worker nodes configuration
|
||||||
|
""""""""""""""""""""""""""
|
||||||
|
|
||||||
|
It is required to point to the master node repository, otherwise internal dependencies will not be handled correctly. In order to do so, the ``--server`` argument (or ``AHRIMAN_REPOSITORY_SERVER`` environment variable for docker images) can be used.
|
||||||
|
|
||||||
|
Also, in case if authentication is enabled, the same user with the same password must be created for all workers.
|
||||||
|
|
||||||
|
It is also recommended to set ``web.wait_timeout`` to infinte in case of multiple conflicting runs.
|
||||||
|
|
||||||
|
Other settings are the same as mentioned above.
|
||||||
|
|
||||||
|
Triple node minimal docker example
|
||||||
|
""""""""""""""""""""""""""""""""""
|
||||||
|
|
||||||
|
In this example, all instances are run on the same machine with address ``172.17.0.1`` with ports available outside of container. Master node config (``master.ini``) as:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
target = mapping
|
||||||
|
|
||||||
|
[status]
|
||||||
|
username = builder-user
|
||||||
|
password = very-secure-password
|
||||||
|
|
||||||
|
[build]
|
||||||
|
workers = http://172.17.0.1:8081 http://172.17.0.1:8082
|
||||||
|
|
||||||
|
[web]
|
||||||
|
enable_archive_upload = yes
|
||||||
|
wait_timeout = 0
|
||||||
|
|
||||||
|
Command to run master node:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
docker run --privileged -p 8080:8080 -e AHRIMAN_PORT=8080 -v master.ini:/etc/ahriman.ini.d/overrides.ini arcan1s/ahriman:latest web
|
||||||
|
|
||||||
|
Worker nodes (applicable for all workers) config (``worker.ini``) as:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
target = mapping
|
||||||
|
|
||||||
|
[status]
|
||||||
|
address = http://172.17.0.1:8080
|
||||||
|
username = builder-user
|
||||||
|
password = very-secure-password
|
||||||
|
|
||||||
|
[upload]
|
||||||
|
target = remote-service
|
||||||
|
|
||||||
|
[remote-service]
|
||||||
|
|
||||||
|
[report]
|
||||||
|
target = remote-call
|
||||||
|
|
||||||
|
[remote-call]
|
||||||
|
manual = yes
|
||||||
|
wait_timeout = 0
|
||||||
|
|
||||||
|
[build]
|
||||||
|
triggers = ahriman.core.upload.UploadTrigger ahriman.core.report.ReportTrigger
|
||||||
|
|
||||||
|
Command to run worker nodes (considering there will be two workers, one is on ``8081`` port and other is on ``8082``):
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
docker run --privileged -p 8081:8081 -e AHRIMAN_PORT=8081 -v worker.ini:/etc/ahriman.ini.d/overrides.ini arcan1s/ahriman:latest web
|
||||||
|
docker run --privileged -p 8082:8082 -e AHRIMAN_PORT=8082 -v worker.ini:/etc/ahriman.ini.d/overrides.ini arcan1s/ahriman:latest web
|
||||||
|
|
||||||
|
Unlike the previous setup, it doesn't require to mount repository root for ``worker`` nodes, because ``worker`` nodes don't use it anyway.
|
||||||
|
|
||||||
|
Addition of new package, package removal, repository update
|
||||||
|
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||||
|
|
||||||
|
In all scenarios, update process must be run only on ``master`` node. Unlike the setup described above, automatic update must be enabled only for ``master`` node also.
|
||||||
|
|
||||||
|
Known limitations
|
||||||
|
"""""""""""""""""
|
||||||
|
|
||||||
|
* Workers don't support local packages. However, it is possible to build custom packages by providing sources by using ``ahriman.core.gitremote.RemotePullTrigger`` trigger.
|
||||||
|
* No dynamic nodes discovery. In case if one of worker nodes is unavailable, the build process will fail.
|
||||||
|
* No pkgrel bump on conflicts. Well, it works, however, it isn't guaranteed.
|
||||||
|
* The identical user must be created for all workers. However, the ``master`` node user can be different from this one.
|
||||||
|
|
||||||
Maintenance packages
|
Maintenance packages
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
@ -1142,7 +1273,7 @@ How to enable basic authorization
|
|||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
|
||||||
[web]
|
[status]
|
||||||
username = api
|
username = api
|
||||||
password = pa55w0rd
|
password = pa55w0rd
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
# Maintainer: Evgeniy Alekseev
|
# Maintainer: Evgeniy Alekseev
|
||||||
|
|
||||||
pkgname='ahriman'
|
pkgname='ahriman'
|
||||||
pkgver=2.12.0
|
pkgver=2.12.2
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="ArcH linux ReposItory MANager"
|
pkgdesc="ArcH linux ReposItory MANager"
|
||||||
arch=('any')
|
arch=('any')
|
||||||
|
|||||||
@ -2,6 +2,6 @@
|
|||||||
Description=ArcH linux ReposItory MANager (%i)
|
Description=ArcH linux ReposItory MANager (%i)
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/bin/ahriman --repository-id "%I" repo-update --refresh
|
ExecStart=/usr/bin/ahriman --repository-id "%I" repo-update --no-changes --refresh
|
||||||
User=ahriman
|
User=ahriman
|
||||||
Group=ahriman
|
Group=ahriman
|
||||||
@ -3,7 +3,6 @@ include = ahriman.ini.d
|
|||||||
logging = ahriman.ini.d/logging.ini
|
logging = ahriman.ini.d/logging.ini
|
||||||
apply_migrations = yes
|
apply_migrations = yes
|
||||||
database = /var/lib/ahriman/ahriman.db
|
database = /var/lib/ahriman/ahriman.db
|
||||||
suppress_http_log_errors = yes
|
|
||||||
|
|
||||||
[alpm]
|
[alpm]
|
||||||
database = /var/lib/pacman
|
database = /var/lib/pacman
|
||||||
@ -62,6 +61,10 @@ ssl = disabled
|
|||||||
template = repo-index.jinja2
|
template = repo-index.jinja2
|
||||||
templates = /usr/share/ahriman/templates
|
templates = /usr/share/ahriman/templates
|
||||||
|
|
||||||
|
[status]
|
||||||
|
enabled = yes
|
||||||
|
suppress_http_log_errors = yes
|
||||||
|
|
||||||
[telegram]
|
[telegram]
|
||||||
template = telegram-index.jinja2
|
template = telegram-index.jinja2
|
||||||
templates = /usr/share/ahriman/templates
|
templates = /usr/share/ahriman/templates
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
const alertPlaceholder = $("#alert-placeholder");
|
const alertPlaceholder = $("#alert-placeholder");
|
||||||
|
|
||||||
function createAlert(title, message, clz) {
|
function createAlert(title, message, clz, action) {
|
||||||
const wrapper = document.createElement("div");
|
const wrapper = document.createElement("div");
|
||||||
wrapper.classList.add("toast", clz);
|
wrapper.classList.add("toast", clz);
|
||||||
wrapper.role = "alert";
|
wrapper.role = "alert";
|
||||||
@ -23,7 +23,7 @@
|
|||||||
const toast = new bootstrap.Toast(wrapper);
|
const toast = new bootstrap.Toast(wrapper);
|
||||||
wrapper.addEventListener("hidden.bs.toast", () => {
|
wrapper.addEventListener("hidden.bs.toast", () => {
|
||||||
wrapper.remove(); // bootstrap doesn't remove elements
|
wrapper.remove(); // bootstrap doesn't remove elements
|
||||||
reload();
|
(action || reload)();
|
||||||
});
|
});
|
||||||
toast.show();
|
toast.show();
|
||||||
}
|
}
|
||||||
@ -38,8 +38,8 @@
|
|||||||
createAlert(title, description(details), "text-bg-danger");
|
createAlert(title, description(details), "text-bg-danger");
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSuccess(title, description) {
|
function showSuccess(title, description, action) {
|
||||||
createAlert(title, description, "text-bg-success");
|
createAlert(title, description, "text-bg-success", action);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<div id="login-modal" tabindex="-1" role="dialog" class="modal fade">
|
<div id="login-modal" tabindex="-1" role="dialog" class="modal fade">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<form action="/api/v1/login" method="post">
|
<form id="login-form" onsubmit="return false">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h4 class="modal-title">Login</h4>
|
<h4 class="modal-title">Login</h4>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||||
@ -26,7 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary"><i class="bi bi-person"></i> login</button>
|
<button type="submit" class="btn btn-primary" onclick="login()"><i class="bi bi-person"></i> login</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -34,16 +34,45 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const passwordInput = $("#login-password");
|
const loginModal = $("#login-modal");
|
||||||
|
const loginForm = $("#login-form");
|
||||||
|
loginModal.on("hidden.bs.modal", () => {
|
||||||
|
loginForm.trigger("reset");
|
||||||
|
});
|
||||||
|
|
||||||
|
const loginPasswordInput = $("#login-password");
|
||||||
|
const loginUsernameInput = $("#login-username");
|
||||||
const showHidePasswordButton = $("#login-show-hide-password-button");
|
const showHidePasswordButton = $("#login-show-hide-password-button");
|
||||||
|
|
||||||
|
function login() {
|
||||||
|
const password = loginPasswordInput.val();
|
||||||
|
const username = loginUsernameInput.val();
|
||||||
|
|
||||||
|
if (username && password) {
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/v1/login",
|
||||||
|
data: JSON.stringify({username: username, password: password}),
|
||||||
|
type: "POST",
|
||||||
|
contentType: "application/json",
|
||||||
|
success: _ => {
|
||||||
|
loginModal.modal("hide");
|
||||||
|
showSuccess("Logged in", `Successfully logged in as ${username}`, () => location.href = "/");
|
||||||
|
},
|
||||||
|
error: (jqXHR, _, errorThrown) => {
|
||||||
|
const message = _ => `Could not login as ${username}`;
|
||||||
|
showFailure("Login error", message, jqXHR, errorThrown);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function showPassword() {
|
function showPassword() {
|
||||||
if (passwordInput.attr("type") === "password") {
|
if (loginPasswordInput.attr("type") === "password") {
|
||||||
passwordInput.attr("type", "text");
|
loginPasswordInput.attr("type", "text");
|
||||||
showHidePasswordButton.removeClass("bi-eye");
|
showHidePasswordButton.removeClass("bi-eye");
|
||||||
showHidePasswordButton.addClass("bi-eye-slash");
|
showHidePasswordButton.addClass("bi-eye-slash");
|
||||||
} else {
|
} else {
|
||||||
passwordInput.attr("type", "password");
|
loginPasswordInput.attr("type", "password");
|
||||||
showHidePasswordButton.removeClass("bi-eye-slash");
|
showHidePasswordButton.removeClass("bi-eye-slash");
|
||||||
showHidePasswordButton.addClass("bi-eye");
|
showHidePasswordButton.addClass("bi-eye");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,13 +36,27 @@
|
|||||||
|
|
||||||
<hr class="col-12">
|
<hr class="col-12">
|
||||||
|
|
||||||
<h3>Environment variables</h3>
|
<div id="package-info-variables-block" hidden>
|
||||||
<div id="package-info-variables-div" class="form-group row"></div>
|
<h3>Environment variables</h3>
|
||||||
|
<div id="package-info-variables-div" class="form-group row"></div>
|
||||||
|
|
||||||
<hr class="col-12">
|
<hr class="col-12">
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3>Build logs</h3>
|
<nav>
|
||||||
<pre class="language-logs"><samp id="package-info-logs-input" class="pre-scrollable language-logs"></samp><button id="package-info-logs-copy-button" type="button" class="btn language-logs" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
<div class="nav nav-tabs" role="tablist">
|
||||||
|
<button id="package-info-logs-button" class="nav-link active" data-bs-toggle="tab" data-bs-target="#package-info-logs" type="button" role="tab" aria-controls="package-info-logs" aria-selected="true"><h3>Build logs</h3></button>
|
||||||
|
<button id="package-info-changes-button" class="nav-link" data-bs-toggle="tab" data-bs-target="#package-info-changes" type="button" role="tab" aria-controls="package-info-changes" aria-selected="false"><h3>Changes</h3></button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="tab-content" id="nav-tabContent">
|
||||||
|
<div id="package-info-logs" class="tab-pane fade show active" role="tabpanel" aria-labelledby="package-info-logs-button" tabindex="0">
|
||||||
|
<pre class="language-console"><code id="package-info-logs-input" class="pre-scrollable language-console"></code><button id="package-info-logs-copy-button" type="button" class="btn language-console" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
||||||
|
</div>
|
||||||
|
<div id="package-info-changes" class="tab-pane fade" role="tabpanel" aria-labelledby="package-info-changes-button" tabindex="0">
|
||||||
|
<pre class="language-diff"><code id="package-info-changes-input" class="pre-scrollable language-diff"></code><button id="package-info-changes-copy-button" type="button" class="btn language-diff" onclick="copyChanges()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()" data-bs-dismiss="modal" hidden><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button>
|
<button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()" data-bs-dismiss="modal" hidden><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button>
|
||||||
@ -68,9 +82,11 @@
|
|||||||
packageInfoUpstreamUrl.empty();
|
packageInfoUpstreamUrl.empty();
|
||||||
packageInfoVersion.empty();
|
packageInfoVersion.empty();
|
||||||
|
|
||||||
|
packageInfoVariablesBlock.attr("hidden", true);
|
||||||
packageInfoVariablesDiv.empty();
|
packageInfoVariablesDiv.empty();
|
||||||
|
|
||||||
packageInfoLogsInput.empty();
|
packageInfoLogsInput.empty();
|
||||||
|
packageInfoChangesInput.empty();
|
||||||
|
|
||||||
packageInfoModal.trigger("reset");
|
packageInfoModal.trigger("reset");
|
||||||
|
|
||||||
@ -80,6 +96,9 @@
|
|||||||
const packageInfoLogsInput = $("#package-info-logs-input");
|
const packageInfoLogsInput = $("#package-info-logs-input");
|
||||||
const packageInfoLogsCopyButton = $("#package-info-logs-copy-button");
|
const packageInfoLogsCopyButton = $("#package-info-logs-copy-button");
|
||||||
|
|
||||||
|
const packageInfoChangesInput = $("#package-info-changes-input");
|
||||||
|
const packageInfoChangesCopyButton = $("#package-info-changes-copy-button");
|
||||||
|
|
||||||
const packageInfoAurUrl = $("#package-info-aur-url");
|
const packageInfoAurUrl = $("#package-info-aur-url");
|
||||||
const packageInfoDepends = $("#package-info-depends");
|
const packageInfoDepends = $("#package-info-depends");
|
||||||
const packageInfoGroups = $("#package-info-groups");
|
const packageInfoGroups = $("#package-info-groups");
|
||||||
@ -89,8 +108,14 @@
|
|||||||
const packageInfoUpstreamUrl = $("#package-info-upstream-url");
|
const packageInfoUpstreamUrl = $("#package-info-upstream-url");
|
||||||
const packageInfoVersion = $("#package-info-version");
|
const packageInfoVersion = $("#package-info-version");
|
||||||
|
|
||||||
|
const packageInfoVariablesBlock = $("#package-info-variables-block");
|
||||||
const packageInfoVariablesDiv = $("#package-info-variables-div");
|
const packageInfoVariablesDiv = $("#package-info-variables-div");
|
||||||
|
|
||||||
|
async function copyChanges() {
|
||||||
|
const changes = packageInfoChangesInput.text();
|
||||||
|
await copyToClipboard(changes, packageInfoChangesCopyButton);
|
||||||
|
}
|
||||||
|
|
||||||
async function copyLogs() {
|
async function copyLogs() {
|
||||||
const logs = packageInfoLogsInput.text();
|
const logs = packageInfoLogsInput.text();
|
||||||
await copyToClipboard(logs, packageInfoLogsCopyButton);
|
await copyToClipboard(logs, packageInfoLogsCopyButton);
|
||||||
@ -142,6 +167,24 @@
|
|||||||
packageInfoVariablesDiv.append(variableInput);
|
packageInfoVariablesDiv.append(variableInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadChanges(packageBase, onFailure) {
|
||||||
|
$.ajax({
|
||||||
|
url: `/api/v1/packages/${packageBase}/changes`,
|
||||||
|
data: {
|
||||||
|
architecture: repository.architecture,
|
||||||
|
repository: repository.repository,
|
||||||
|
},
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
success: response => {
|
||||||
|
const changes = response.changes;
|
||||||
|
packageInfoChangesInput.text(changes || "");
|
||||||
|
packageInfoChangesInput.map((_, el) => hljs.highlightElement(el));
|
||||||
|
},
|
||||||
|
error: onFailure,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function loadLogs(packageBase, onFailure) {
|
function loadLogs(packageBase, onFailure) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: `/api/v2/packages/${packageBase}/logs`,
|
url: `/api/v2/packages/${packageBase}/logs`,
|
||||||
@ -156,6 +199,7 @@
|
|||||||
return `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`;
|
return `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`;
|
||||||
});
|
});
|
||||||
packageInfoLogsInput.text(logs.join("\n"));
|
packageInfoLogsInput.text(logs.join("\n"));
|
||||||
|
packageInfoLogsInput.map((_, el) => hljs.highlightElement(el));
|
||||||
},
|
},
|
||||||
error: onFailure,
|
error: onFailure,
|
||||||
});
|
});
|
||||||
@ -182,7 +226,12 @@
|
|||||||
const description = response.find(Boolean);
|
const description = response.find(Boolean);
|
||||||
const packages = Object.keys(description.package.packages);
|
const packages = Object.keys(description.package.packages);
|
||||||
const aurUrl = description.package.remote.web_url;
|
const aurUrl = description.package.remote.web_url;
|
||||||
const upstreamUrls = Object.values(description.package.packages).map(single => single.url);
|
const upstreamUrls = Array.from(
|
||||||
|
new Set(
|
||||||
|
Object.values(description.package.packages)
|
||||||
|
.map(single => single.url)
|
||||||
|
)
|
||||||
|
).sort();
|
||||||
|
|
||||||
packageInfo.text(`${description.package.base} ${description.status.status} at ${new Date(1000 * description.status.timestamp).toISOStringShort()}`);
|
packageInfo.text(`${description.package.base} ${description.status.status} at ${new Date(1000 * description.status.timestamp).toISOStringShort()}`);
|
||||||
|
|
||||||
@ -223,6 +272,7 @@
|
|||||||
success: response => {
|
success: response => {
|
||||||
packageInfoVariablesDiv.empty();
|
packageInfoVariablesDiv.empty();
|
||||||
response.map(patch => insertVariable(packageBase, patch));
|
response.map(patch => insertVariable(packageBase, patch));
|
||||||
|
packageInfoVariablesBlock.attr("hidden", response.length === 0);
|
||||||
},
|
},
|
||||||
error: onFailure,
|
error: onFailure,
|
||||||
});
|
});
|
||||||
@ -255,6 +305,7 @@
|
|||||||
loadPackage(packageBase, onFailure);
|
loadPackage(packageBase, onFailure);
|
||||||
loadPatches(packageBase, onFailure);
|
loadPatches(packageBase, onFailure);
|
||||||
loadLogs(packageBase, onFailure);
|
loadLogs(packageBase, onFailure);
|
||||||
|
loadChanges(packageBase, onFailure)
|
||||||
|
|
||||||
if (isPackageBaseSet) packageInfoModal.modal("show");
|
if (isPackageBaseSet) packageInfoModal.modal("show");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/resizable/bootstrap-table-resizable.js" integrity="sha384-wd8Vc6Febikdnsnk9vthRWRvMwffw246vhqiqNO3aSNe1maTEA07Vh3zAQiSyDji" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/resizable/bootstrap-table-resizable.js" integrity="sha384-wd8Vc6Febikdnsnk9vthRWRvMwffw246vhqiqNO3aSNe1maTEA07Vh3zAQiSyDji" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/filter-control/bootstrap-table-filter-control.js" integrity="sha384-NIqcjpr/3eZI1iNzz7hgT5rgp70qFUzkZffeCgVva9gi80B5vqcm7gn+8QvlWxko" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.22.1/dist/extensions/filter-control/bootstrap-table-filter-control.js" integrity="sha384-NIqcjpr/3eZI1iNzz7hgT5rgp70qFUzkZffeCgVva9gi80B5vqcm7gn+8QvlWxko" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js" integrity="sha384-F/bZzf7p3Joyp5psL90p/p89AZJsndkSoGwRpXcZhleCWhd8SnRuoYo4d0yirjJp" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function copyToClipboard(text, button) {
|
async function copyToClipboard(text, button) {
|
||||||
if (navigator.clipboard === undefined) {
|
if (navigator.clipboard === undefined) {
|
||||||
|
|||||||
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.css" integrity="sha384-zLkQsiLfAQqGeIJeKLC+rcCR1YoYaQFLCL7cLDUoKE1ajKJzySpjzWGfYS2vjSG+" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/daterangepicker@3.1.0/daterangepicker.css" integrity="sha384-zLkQsiLfAQqGeIJeKLC+rcCR1YoYaQFLCL7cLDUoKE1ajKJzySpjzWGfYS2vjSG+" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github.min.css" integrity="sha384-eFTL69TLRZTkNfYZOLM+G04821K1qZao/4QLJbet1pP4tcF+fdXq/9CdqAbWRl/L" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.pre-scrollable {
|
.pre-scrollable {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
# AUTOMATICALLY GENERATED by `shtab`
|
# AUTOMATICALLY GENERATED by `shtab`
|
||||||
|
|
||||||
_shtab_ahriman_subparsers=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
|
_shtab_ahriman_subparsers=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
|
||||||
|
|
||||||
_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--log-handler' '-q' '--quiet' '--report' '--no-report' '-r' '--repository' '--unsafe' '--wait-timeout' '-V' '--version')
|
_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--log-handler' '-q' '--quiet' '--report' '--no-report' '-r' '--repository' '--unsafe' '-V' '--version' '--wait-timeout')
|
||||||
_shtab_ahriman_aur_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
|
_shtab_ahriman_aur_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
|
||||||
_shtab_ahriman_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
|
_shtab_ahriman_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
|
||||||
_shtab_ahriman_help_commands_unsafe_option_strings=('-h' '--help')
|
_shtab_ahriman_help_commands_unsafe_option_strings=('-h' '--help')
|
||||||
@ -13,6 +13,8 @@ _shtab_ahriman_version_option_strings=('-h' '--help')
|
|||||||
_shtab_ahriman_package_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
|
_shtab_ahriman_package_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
|
||||||
_shtab_ahriman_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
|
_shtab_ahriman_add_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
|
||||||
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
|
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
|
||||||
|
_shtab_ahriman_package_changes_option_strings=('-h' '--help' '-e' '--exit-code')
|
||||||
|
_shtab_ahriman_package_changes_remove_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_package_remove_option_strings=('-h' '--help')
|
_shtab_ahriman_package_remove_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_remove_option_strings=('-h' '--help')
|
_shtab_ahriman_remove_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_package_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
|
_shtab_ahriman_package_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
|
||||||
@ -25,12 +27,12 @@ _shtab_ahriman_patch_list_option_strings=('-h' '--help' '-e' '--exit-code' '-v'
|
|||||||
_shtab_ahriman_patch_remove_option_strings=('-h' '--help' '-v' '--variable')
|
_shtab_ahriman_patch_remove_option_strings=('-h' '--help' '-v' '--variable')
|
||||||
_shtab_ahriman_patch_set_add_option_strings=('-h' '--help' '-t' '--track')
|
_shtab_ahriman_patch_set_add_option_strings=('-h' '--help' '-t' '--track')
|
||||||
_shtab_ahriman_repo_backup_option_strings=('-h' '--help')
|
_shtab_ahriman_repo_backup_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_repo_check_option_strings=('-h' '--help' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
|
_shtab_ahriman_repo_check_option_strings=('-h' '--help' '--changes' '--no-changes' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
|
||||||
_shtab_ahriman_check_option_strings=('-h' '--help' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
|
_shtab_ahriman_check_option_strings=('-h' '--help' '--changes' '--no-changes' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
|
||||||
_shtab_ahriman_repo_create_keyring_option_strings=('-h' '--help')
|
_shtab_ahriman_repo_create_keyring_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_repo_create_mirrorlist_option_strings=('-h' '--help')
|
_shtab_ahriman_repo_create_mirrorlist_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_repo_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
|
_shtab_ahriman_repo_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '--dry-run' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
|
||||||
_shtab_ahriman_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
|
_shtab_ahriman_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '--dry-run' '--local' '--no-local' '--manual' '--no-manual' '--vcs' '--no-vcs' '-y' '--refresh')
|
||||||
_shtab_ahriman_repo_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
|
_shtab_ahriman_repo_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
|
||||||
_shtab_ahriman_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
|
_shtab_ahriman_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
|
||||||
_shtab_ahriman_repo_remove_unknown_option_strings=('-h' '--help' '--dry-run')
|
_shtab_ahriman_repo_remove_unknown_option_strings=('-h' '--help' '--dry-run')
|
||||||
@ -45,8 +47,8 @@ _shtab_ahriman_repo_sync_option_strings=('-h' '--help')
|
|||||||
_shtab_ahriman_sync_option_strings=('-h' '--help')
|
_shtab_ahriman_sync_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_repo_tree_option_strings=('-h' '--help' '-p' '--partitions')
|
_shtab_ahriman_repo_tree_option_strings=('-h' '--help' '-p' '--partitions')
|
||||||
_shtab_ahriman_repo_triggers_option_strings=('-h' '--help')
|
_shtab_ahriman_repo_triggers_option_strings=('-h' '--help')
|
||||||
_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
|
_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
|
||||||
_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
|
_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
|
||||||
_shtab_ahriman_service_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
|
_shtab_ahriman_service_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
|
||||||
_shtab_ahriman_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
|
_shtab_ahriman_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
|
||||||
_shtab_ahriman_repo_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
|
_shtab_ahriman_repo_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
|
||||||
@ -76,7 +78,7 @@ _shtab_ahriman_web_option_strings=('-h' '--help')
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
|
_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
|
||||||
_shtab_ahriman___log_handler_choices=('console' 'syslog' 'journald')
|
_shtab_ahriman___log_handler_choices=('console' 'syslog' 'journald')
|
||||||
_shtab_ahriman_aur_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'submitter' 'url' 'url_path' 'version')
|
_shtab_ahriman_aur_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'submitter' 'url' 'url_path' 'version')
|
||||||
_shtab_ahriman_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'submitter' 'url' 'url_path' 'version')
|
_shtab_ahriman_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'submitter' 'url' 'url_path' 'version')
|
||||||
@ -187,6 +189,12 @@ _shtab_ahriman_package_update__n_nargs=0
|
|||||||
_shtab_ahriman_package_update___now_nargs=0
|
_shtab_ahriman_package_update___now_nargs=0
|
||||||
_shtab_ahriman_package_update__y_nargs=0
|
_shtab_ahriman_package_update__y_nargs=0
|
||||||
_shtab_ahriman_package_update___refresh_nargs=0
|
_shtab_ahriman_package_update___refresh_nargs=0
|
||||||
|
_shtab_ahriman_package_changes__h_nargs=0
|
||||||
|
_shtab_ahriman_package_changes___help_nargs=0
|
||||||
|
_shtab_ahriman_package_changes__e_nargs=0
|
||||||
|
_shtab_ahriman_package_changes___exit_code_nargs=0
|
||||||
|
_shtab_ahriman_package_changes_remove__h_nargs=0
|
||||||
|
_shtab_ahriman_package_changes_remove___help_nargs=0
|
||||||
_shtab_ahriman_package_remove_pos_0_nargs=+
|
_shtab_ahriman_package_remove_pos_0_nargs=+
|
||||||
_shtab_ahriman_package_remove__h_nargs=0
|
_shtab_ahriman_package_remove__h_nargs=0
|
||||||
_shtab_ahriman_package_remove___help_nargs=0
|
_shtab_ahriman_package_remove___help_nargs=0
|
||||||
@ -233,6 +241,8 @@ _shtab_ahriman_repo_backup___help_nargs=0
|
|||||||
_shtab_ahriman_repo_check_pos_0_nargs=*
|
_shtab_ahriman_repo_check_pos_0_nargs=*
|
||||||
_shtab_ahriman_repo_check__h_nargs=0
|
_shtab_ahriman_repo_check__h_nargs=0
|
||||||
_shtab_ahriman_repo_check___help_nargs=0
|
_shtab_ahriman_repo_check___help_nargs=0
|
||||||
|
_shtab_ahriman_repo_check___changes_nargs=0
|
||||||
|
_shtab_ahriman_repo_check___no_changes_nargs=0
|
||||||
_shtab_ahriman_repo_check__e_nargs=0
|
_shtab_ahriman_repo_check__e_nargs=0
|
||||||
_shtab_ahriman_repo_check___exit_code_nargs=0
|
_shtab_ahriman_repo_check___exit_code_nargs=0
|
||||||
_shtab_ahriman_repo_check___vcs_nargs=0
|
_shtab_ahriman_repo_check___vcs_nargs=0
|
||||||
@ -242,6 +252,8 @@ _shtab_ahriman_repo_check___refresh_nargs=0
|
|||||||
_shtab_ahriman_check_pos_0_nargs=*
|
_shtab_ahriman_check_pos_0_nargs=*
|
||||||
_shtab_ahriman_check__h_nargs=0
|
_shtab_ahriman_check__h_nargs=0
|
||||||
_shtab_ahriman_check___help_nargs=0
|
_shtab_ahriman_check___help_nargs=0
|
||||||
|
_shtab_ahriman_check___changes_nargs=0
|
||||||
|
_shtab_ahriman_check___no_changes_nargs=0
|
||||||
_shtab_ahriman_check__e_nargs=0
|
_shtab_ahriman_check__e_nargs=0
|
||||||
_shtab_ahriman_check___exit_code_nargs=0
|
_shtab_ahriman_check___exit_code_nargs=0
|
||||||
_shtab_ahriman_check___vcs_nargs=0
|
_shtab_ahriman_check___vcs_nargs=0
|
||||||
@ -256,8 +268,11 @@ _shtab_ahriman_repo_daemon__h_nargs=0
|
|||||||
_shtab_ahriman_repo_daemon___help_nargs=0
|
_shtab_ahriman_repo_daemon___help_nargs=0
|
||||||
_shtab_ahriman_repo_daemon___aur_nargs=0
|
_shtab_ahriman_repo_daemon___aur_nargs=0
|
||||||
_shtab_ahriman_repo_daemon___no_aur_nargs=0
|
_shtab_ahriman_repo_daemon___no_aur_nargs=0
|
||||||
|
_shtab_ahriman_repo_daemon___changes_nargs=0
|
||||||
|
_shtab_ahriman_repo_daemon___no_changes_nargs=0
|
||||||
_shtab_ahriman_repo_daemon___dependencies_nargs=0
|
_shtab_ahriman_repo_daemon___dependencies_nargs=0
|
||||||
_shtab_ahriman_repo_daemon___no_dependencies_nargs=0
|
_shtab_ahriman_repo_daemon___no_dependencies_nargs=0
|
||||||
|
_shtab_ahriman_repo_daemon___dry_run_nargs=0
|
||||||
_shtab_ahriman_repo_daemon___local_nargs=0
|
_shtab_ahriman_repo_daemon___local_nargs=0
|
||||||
_shtab_ahriman_repo_daemon___no_local_nargs=0
|
_shtab_ahriman_repo_daemon___no_local_nargs=0
|
||||||
_shtab_ahriman_repo_daemon___manual_nargs=0
|
_shtab_ahriman_repo_daemon___manual_nargs=0
|
||||||
@ -270,8 +285,11 @@ _shtab_ahriman_daemon__h_nargs=0
|
|||||||
_shtab_ahriman_daemon___help_nargs=0
|
_shtab_ahriman_daemon___help_nargs=0
|
||||||
_shtab_ahriman_daemon___aur_nargs=0
|
_shtab_ahriman_daemon___aur_nargs=0
|
||||||
_shtab_ahriman_daemon___no_aur_nargs=0
|
_shtab_ahriman_daemon___no_aur_nargs=0
|
||||||
|
_shtab_ahriman_daemon___changes_nargs=0
|
||||||
|
_shtab_ahriman_daemon___no_changes_nargs=0
|
||||||
_shtab_ahriman_daemon___dependencies_nargs=0
|
_shtab_ahriman_daemon___dependencies_nargs=0
|
||||||
_shtab_ahriman_daemon___no_dependencies_nargs=0
|
_shtab_ahriman_daemon___no_dependencies_nargs=0
|
||||||
|
_shtab_ahriman_daemon___dry_run_nargs=0
|
||||||
_shtab_ahriman_daemon___local_nargs=0
|
_shtab_ahriman_daemon___local_nargs=0
|
||||||
_shtab_ahriman_daemon___no_local_nargs=0
|
_shtab_ahriman_daemon___no_local_nargs=0
|
||||||
_shtab_ahriman_daemon___manual_nargs=0
|
_shtab_ahriman_daemon___manual_nargs=0
|
||||||
@ -330,6 +348,8 @@ _shtab_ahriman_repo_update__h_nargs=0
|
|||||||
_shtab_ahriman_repo_update___help_nargs=0
|
_shtab_ahriman_repo_update___help_nargs=0
|
||||||
_shtab_ahriman_repo_update___aur_nargs=0
|
_shtab_ahriman_repo_update___aur_nargs=0
|
||||||
_shtab_ahriman_repo_update___no_aur_nargs=0
|
_shtab_ahriman_repo_update___no_aur_nargs=0
|
||||||
|
_shtab_ahriman_repo_update___changes_nargs=0
|
||||||
|
_shtab_ahriman_repo_update___no_changes_nargs=0
|
||||||
_shtab_ahriman_repo_update___dependencies_nargs=0
|
_shtab_ahriman_repo_update___dependencies_nargs=0
|
||||||
_shtab_ahriman_repo_update___no_dependencies_nargs=0
|
_shtab_ahriman_repo_update___no_dependencies_nargs=0
|
||||||
_shtab_ahriman_repo_update___dry_run_nargs=0
|
_shtab_ahriman_repo_update___dry_run_nargs=0
|
||||||
@ -350,6 +370,8 @@ _shtab_ahriman_update__h_nargs=0
|
|||||||
_shtab_ahriman_update___help_nargs=0
|
_shtab_ahriman_update___help_nargs=0
|
||||||
_shtab_ahriman_update___aur_nargs=0
|
_shtab_ahriman_update___aur_nargs=0
|
||||||
_shtab_ahriman_update___no_aur_nargs=0
|
_shtab_ahriman_update___no_aur_nargs=0
|
||||||
|
_shtab_ahriman_update___changes_nargs=0
|
||||||
|
_shtab_ahriman_update___no_changes_nargs=0
|
||||||
_shtab_ahriman_update___dependencies_nargs=0
|
_shtab_ahriman_update___dependencies_nargs=0
|
||||||
_shtab_ahriman_update___no_dependencies_nargs=0
|
_shtab_ahriman_update___no_dependencies_nargs=0
|
||||||
_shtab_ahriman_update___dry_run_nargs=0
|
_shtab_ahriman_update___dry_run_nargs=0
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
.TH AHRIMAN "1" "2023\-11\-06" "ahriman" "Generated Python Manual"
|
.TH AHRIMAN "1" "2023\-12\-08" "ahriman" "Generated Python Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ahriman
|
ahriman
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B ahriman
|
.B ahriman
|
||||||
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [--wait-timeout WAIT_TIMEOUT] [-V] {aur-search,search,help-commands-unsafe,help,help-updates,help-version,version,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-create-keyring,repo-create-mirrorlist,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-repositories,service-run,run,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,service-tree-migrate,user-add,user-list,user-remove,web} ...
|
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {aur-search,search,help-commands-unsafe,help,help-updates,help-version,version,package-add,add,package-update,package-changes,package-changes-remove,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-create-keyring,repo-create-mirrorlist,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-repositories,service-run,run,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,service-tree-migrate,user-add,user-list,user-remove,web} ...
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
ArcH linux ReposItory MANager
|
ArcH linux ReposItory MANager
|
||||||
|
|
||||||
@ -44,15 +44,15 @@ filter by target repository
|
|||||||
\fB\-\-unsafe\fR
|
\fB\-\-unsafe\fR
|
||||||
allow to run ahriman as non\-ahriman user. Some actions might be unavailable
|
allow to run ahriman as non\-ahriman user. Some actions might be unavailable
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB\-V\fR, \fB\-\-version\fR
|
||||||
|
show program's version number and exit
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-wait\-timeout\fR \fI\,WAIT_TIMEOUT\/\fR
|
\fB\-\-wait\-timeout\fR \fI\,WAIT_TIMEOUT\/\fR
|
||||||
wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of
|
wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of
|
||||||
zero value, the application will wait infinitely
|
zero value, the application will wait infinitely
|
||||||
|
|
||||||
.TP
|
|
||||||
\fB\-V\fR, \fB\-\-version\fR
|
|
||||||
show program's version number and exit
|
|
||||||
|
|
||||||
.SH
|
.SH
|
||||||
COMMAND
|
COMMAND
|
||||||
.TP
|
.TP
|
||||||
@ -74,6 +74,12 @@ application version
|
|||||||
\fBahriman\fR \fI\,package\-add\/\fR
|
\fBahriman\fR \fI\,package\-add\/\fR
|
||||||
add package
|
add package
|
||||||
.TP
|
.TP
|
||||||
|
\fBahriman\fR \fI\,package\-changes\/\fR
|
||||||
|
get package changes
|
||||||
|
.TP
|
||||||
|
\fBahriman\fR \fI\,package\-changes\-remove\/\fR
|
||||||
|
remove package changes
|
||||||
|
.TP
|
||||||
\fBahriman\fR \fI\,package\-remove\/\fR
|
\fBahriman\fR \fI\,package\-remove\/\fR
|
||||||
remove package
|
remove package
|
||||||
.TP
|
.TP
|
||||||
@ -208,22 +214,22 @@ sort field by this field. In case if two packages have the same value of the spe
|
|||||||
by name
|
by name
|
||||||
|
|
||||||
.SH COMMAND \fI\,'ahriman help\-commands\-unsafe'\/\fR
|
.SH COMMAND \fI\,'ahriman help\-commands\-unsafe'\/\fR
|
||||||
usage: ahriman help\-commands\-unsafe [\-h] [command ...]
|
usage: ahriman help\-commands\-unsafe [\-h] [subcommand ...]
|
||||||
|
|
||||||
list unsafe commands as defined in default args
|
list unsafe commands as defined in default args
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fBcommand\fR
|
\fBsubcommand\fR
|
||||||
instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1
|
instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1
|
||||||
otherwise
|
otherwise
|
||||||
|
|
||||||
.SH COMMAND \fI\,'ahriman help'\/\fR
|
.SH COMMAND \fI\,'ahriman help'\/\fR
|
||||||
usage: ahriman help [\-h] [command]
|
usage: ahriman help [\-h] [subcommand]
|
||||||
|
|
||||||
show help message for application or command and exit
|
show help message for application or command and exit
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fBcommand\fR
|
\fBsubcommand\fR
|
||||||
show help message for specific command
|
show help message for specific command
|
||||||
|
|
||||||
.SH COMMAND \fI\,'ahriman help\-updates'\/\fR
|
.SH COMMAND \fI\,'ahriman help\-updates'\/\fR
|
||||||
@ -285,6 +291,29 @@ build as user
|
|||||||
\fB\-v\fR \fI\,VARIABLE\/\fR, \fB\-\-variable\fR \fI\,VARIABLE\/\fR
|
\fB\-v\fR \fI\,VARIABLE\/\fR, \fB\-\-variable\fR \fI\,VARIABLE\/\fR
|
||||||
apply specified makepkg variables to the next build
|
apply specified makepkg variables to the next build
|
||||||
|
|
||||||
|
.SH COMMAND \fI\,'ahriman package\-changes'\/\fR
|
||||||
|
usage: ahriman package\-changes [\-h] [\-e] package
|
||||||
|
|
||||||
|
retrieve package changes stored in database
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fBpackage\fR
|
||||||
|
package base
|
||||||
|
|
||||||
|
.SH OPTIONS \fI\,'ahriman package\-changes'\/\fR
|
||||||
|
.TP
|
||||||
|
\fB\-e\fR, \fB\-\-exit\-code\fR
|
||||||
|
return non\-zero exit status if result is empty
|
||||||
|
|
||||||
|
.SH COMMAND \fI\,'ahriman package\-changes\-remove'\/\fR
|
||||||
|
usage: ahriman package\-changes\-remove [\-h] package
|
||||||
|
|
||||||
|
remove the package changes stored remotely
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fBpackage\fR
|
||||||
|
package base
|
||||||
|
|
||||||
.SH COMMAND \fI\,'ahriman package\-remove'\/\fR
|
.SH COMMAND \fI\,'ahriman package\-remove'\/\fR
|
||||||
usage: ahriman package\-remove [\-h] package [package ...]
|
usage: ahriman package\-remove [\-h] package [package ...]
|
||||||
|
|
||||||
@ -418,7 +447,7 @@ backup repository settings and database
|
|||||||
path of the output archive
|
path of the output archive
|
||||||
|
|
||||||
.SH COMMAND \fI\,'ahriman repo\-check'\/\fR
|
.SH COMMAND \fI\,'ahriman repo\-check'\/\fR
|
||||||
usage: ahriman repo\-check [\-h] [\-e] [\-\-vcs | \-\-no\-vcs] [\-y] [package ...]
|
usage: ahriman repo\-check [\-h] [\-\-changes | \-\-no\-changes] [\-e] [\-\-vcs | \-\-no\-vcs] [\-y] [package ...]
|
||||||
|
|
||||||
check for packages updates. Same as repo\-update \-\-dry\-run \-\-no\-manual
|
check for packages updates. Same as repo\-update \-\-dry\-run \-\-no\-manual
|
||||||
|
|
||||||
@ -427,6 +456,10 @@ check for packages updates. Same as repo\-update \-\-dry\-run \-\-no\-manual
|
|||||||
filter check by package base
|
filter check by package base
|
||||||
|
|
||||||
.SH OPTIONS \fI\,'ahriman repo\-check'\/\fR
|
.SH OPTIONS \fI\,'ahriman repo\-check'\/\fR
|
||||||
|
.TP
|
||||||
|
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
|
||||||
|
calculate changes from the latest known commit if available. Only applicable in dry run mode
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-e\fR, \fB\-\-exit\-code\fR
|
\fB\-e\fR, \fB\-\-exit\-code\fR
|
||||||
return non\-zero exit status if result is empty
|
return non\-zero exit status if result is empty
|
||||||
@ -450,8 +483,9 @@ usage: ahriman repo\-create\-mirrorlist [\-h]
|
|||||||
create package which contains list of available mirrors as set by configuration. Note, that this action will only create package, the package itself has to be built manually
|
create package which contains list of available mirrors as set by configuration. Note, that this action will only create package, the package itself has to be built manually
|
||||||
|
|
||||||
.SH COMMAND \fI\,'ahriman repo\-daemon'\/\fR
|
.SH COMMAND \fI\,'ahriman repo\-daemon'\/\fR
|
||||||
usage: ahriman repo\-daemon [\-h] [\-i INTERVAL] [\-\-aur | \-\-no\-aur] [\-\-dependencies | \-\-no\-dependencies]
|
usage: ahriman repo\-daemon [\-h] [\-i INTERVAL] [\-\-aur | \-\-no\-aur] [\-\-changes | \-\-no\-changes]
|
||||||
[\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-\-vcs | \-\-no\-vcs] [\-y]
|
[\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run] [\-\-local | \-\-no\-local]
|
||||||
|
[\-\-manual | \-\-no\-manual] [\-\-vcs | \-\-no\-vcs] [\-y]
|
||||||
|
|
||||||
start process which periodically will run update process
|
start process which periodically will run update process
|
||||||
|
|
||||||
@ -464,10 +498,18 @@ interval between runs in seconds
|
|||||||
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
|
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
|
||||||
enable or disable checking for AUR updates
|
enable or disable checking for AUR updates
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
|
||||||
|
calculate changes from the latest known commit if available. Only applicable in dry run mode
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
|
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
|
||||||
process missing package dependencies
|
process missing package dependencies
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB\-\-dry\-run\fR
|
||||||
|
just perform check for updates, same as check command
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-local\fR, \fB\-\-no\-local\fR
|
\fB\-\-local\fR, \fB\-\-no\-local\fR
|
||||||
enable or disable checking of local packages for updates
|
enable or disable checking of local packages for updates
|
||||||
@ -594,9 +636,9 @@ run triggers on empty build result as configured by settings
|
|||||||
instead of running all triggers as set by configuration, just process specified ones in order of mention
|
instead of running all triggers as set by configuration, just process specified ones in order of mention
|
||||||
|
|
||||||
.SH COMMAND \fI\,'ahriman repo\-update'\/\fR
|
.SH COMMAND \fI\,'ahriman repo\-update'\/\fR
|
||||||
usage: ahriman repo\-update [\-h] [\-\-aur | \-\-no\-aur] [\-\-dependencies | \-\-no\-dependencies] [\-\-dry\-run] [\-e]
|
usage: ahriman repo\-update [\-h] [\-\-aur | \-\-no\-aur] [\-\-changes | \-\-no\-changes] [\-\-dependencies | \-\-no\-dependencies]
|
||||||
[\-\-increment | \-\-no\-increment] [\-\-local | \-\-no\-local] [\-\-manual | \-\-no\-manual] [\-u USERNAME]
|
[\-\-dry\-run] [\-e] [\-\-increment | \-\-no\-increment] [\-\-local | \-\-no\-local]
|
||||||
[\-\-vcs | \-\-no\-vcs] [\-y]
|
[\-\-manual | \-\-no\-manual] [\-u USERNAME] [\-\-vcs | \-\-no\-vcs] [\-y]
|
||||||
[package ...]
|
[package ...]
|
||||||
|
|
||||||
check for packages updates and run build process if requested
|
check for packages updates and run build process if requested
|
||||||
@ -610,6 +652,10 @@ filter check by package base
|
|||||||
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
|
\fB\-\-aur\fR, \fB\-\-no\-aur\fR
|
||||||
enable or disable checking for AUR updates
|
enable or disable checking for AUR updates
|
||||||
|
|
||||||
|
.TP
|
||||||
|
\fB\-\-changes\fR, \fB\-\-no\-changes\fR
|
||||||
|
calculate changes from the latest known commit if available. Only applicable in dry run mode
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
|
\fB\-\-dependencies\fR, \fB\-\-no\-dependencies\fR
|
||||||
process missing package dependencies
|
process missing package dependencies
|
||||||
|
|||||||
@ -19,6 +19,8 @@ _shtab_ahriman_commands() {
|
|||||||
"init:create initial service configuration, requires root"
|
"init:create initial service configuration, requires root"
|
||||||
"key-import:import PGP key from public sources to the repository user"
|
"key-import:import PGP key from public sources to the repository user"
|
||||||
"package-add:add existing or new package to the build queue"
|
"package-add:add existing or new package to the build queue"
|
||||||
|
"package-changes:retrieve package changes stored in database"
|
||||||
|
"package-changes-remove:remove the package changes stored remotely"
|
||||||
"package-remove:remove package from the repository"
|
"package-remove:remove package from the repository"
|
||||||
"package-status:request status of the package"
|
"package-status:request status of the package"
|
||||||
"package-status-remove:remove the package from the status page"
|
"package-status-remove:remove the package from the status page"
|
||||||
@ -90,8 +92,8 @@ _shtab_ahriman_options=(
|
|||||||
{--report,--no-report}"[force enable or disable reporting to web service (default\: True)]:report:"
|
{--report,--no-report}"[force enable or disable reporting to web service (default\: True)]:report:"
|
||||||
{-r,--repository}"[filter by target repository (default\: None)]:repository:"
|
{-r,--repository}"[filter by target repository (default\: None)]:repository:"
|
||||||
"--unsafe[allow to run ahriman as non-ahriman user. Some actions might be unavailable (default\: False)]"
|
"--unsafe[allow to run ahriman as non-ahriman user. Some actions might be unavailable (default\: False)]"
|
||||||
"--wait-timeout[wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of zero value, the application will wait infinitely (default\: -1)]:wait_timeout:"
|
|
||||||
"(- : *)"{-V,--version}"[show program\'s version number and exit]"
|
"(- : *)"{-V,--version}"[show program\'s version number and exit]"
|
||||||
|
"--wait-timeout[wait for lock to be free. Negative value will lead to immediate application run even if there is lock file. In case of zero value, the application will wait infinitely (default\: -1)]:wait_timeout:"
|
||||||
)
|
)
|
||||||
|
|
||||||
_shtab_ahriman_add_options=(
|
_shtab_ahriman_add_options=(
|
||||||
@ -117,6 +119,7 @@ _shtab_ahriman_aur_search_options=(
|
|||||||
|
|
||||||
_shtab_ahriman_check_options=(
|
_shtab_ahriman_check_options=(
|
||||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
|
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
|
||||||
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
|
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
|
||||||
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
|
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
|
||||||
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
|
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
|
||||||
@ -149,7 +152,9 @@ _shtab_ahriman_daemon_options=(
|
|||||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
|
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
|
||||||
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
|
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
|
||||||
|
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
|
||||||
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
|
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
|
||||||
|
"--dry-run[just perform check for updates, same as check command (default\: False)]"
|
||||||
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
|
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
|
||||||
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
|
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
|
||||||
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
|
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
|
||||||
@ -210,6 +215,17 @@ _shtab_ahriman_package_add_options=(
|
|||||||
"(*):package source (base name, path to local files, remote URL):"
|
"(*):package source (base name, path to local files, remote URL):"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_shtab_ahriman_package_changes_options=(
|
||||||
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
|
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
|
||||||
|
":package base:"
|
||||||
|
)
|
||||||
|
|
||||||
|
_shtab_ahriman_package_changes_remove_options=(
|
||||||
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
|
":package base:"
|
||||||
|
)
|
||||||
|
|
||||||
_shtab_ahriman_package_remove_options=(
|
_shtab_ahriman_package_remove_options=(
|
||||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
"(*):package name or base:"
|
"(*):package name or base:"
|
||||||
@ -302,6 +318,7 @@ _shtab_ahriman_repo_backup_options=(
|
|||||||
|
|
||||||
_shtab_ahriman_repo_check_options=(
|
_shtab_ahriman_repo_check_options=(
|
||||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
|
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
|
||||||
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
|
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
|
||||||
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
|
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
|
||||||
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
|
"*"{-y,--refresh}"[download fresh package databases from the mirror before actions, -yy to force refresh even if up to date (default\: False)]"
|
||||||
@ -342,7 +359,9 @@ _shtab_ahriman_repo_daemon_options=(
|
|||||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
|
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
|
||||||
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
|
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
|
||||||
|
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
|
||||||
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
|
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
|
||||||
|
"--dry-run[just perform check for updates, same as check command (default\: False)]"
|
||||||
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
|
{--local,--no-local}"[enable or disable checking of local packages for updates (default\: True)]:local:"
|
||||||
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
|
{--manual,--no-manual}"[include or exclude manual updates (default\: True)]:manual:"
|
||||||
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
|
{--vcs,--no-vcs}"[fetch actual version of VCS packages (default\: True)]:vcs:"
|
||||||
@ -434,6 +453,7 @@ _shtab_ahriman_repo_triggers_options=(
|
|||||||
_shtab_ahriman_repo_update_options=(
|
_shtab_ahriman_repo_update_options=(
|
||||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
|
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
|
||||||
|
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
|
||||||
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
|
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
|
||||||
"--dry-run[just perform check for updates, same as check command (default\: False)]"
|
"--dry-run[just perform check for updates, same as check command (default\: False)]"
|
||||||
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
|
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
|
||||||
@ -574,6 +594,7 @@ _shtab_ahriman_sync_options=(
|
|||||||
_shtab_ahriman_update_options=(
|
_shtab_ahriman_update_options=(
|
||||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||||
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
|
{--aur,--no-aur}"[enable or disable checking for AUR updates (default\: True)]:aur:"
|
||||||
|
{--changes,--no-changes}"[calculate changes from the latest known commit if available. Only applicable in dry run mode (default\: True)]:changes:"
|
||||||
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
|
{--dependencies,--no-dependencies}"[process missing package dependencies (default\: True)]:dependencies:"
|
||||||
"--dry-run[just perform check for updates, same as check command (default\: False)]"
|
"--dry-run[just perform check for updates, same as check command (default\: False)]"
|
||||||
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
|
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
|
||||||
@ -644,6 +665,8 @@ _shtab_ahriman() {
|
|||||||
init) _arguments -C -s $_shtab_ahriman_init_options ;;
|
init) _arguments -C -s $_shtab_ahriman_init_options ;;
|
||||||
key-import) _arguments -C -s $_shtab_ahriman_key_import_options ;;
|
key-import) _arguments -C -s $_shtab_ahriman_key_import_options ;;
|
||||||
package-add) _arguments -C -s $_shtab_ahriman_package_add_options ;;
|
package-add) _arguments -C -s $_shtab_ahriman_package_add_options ;;
|
||||||
|
package-changes) _arguments -C -s $_shtab_ahriman_package_changes_options ;;
|
||||||
|
package-changes-remove) _arguments -C -s $_shtab_ahriman_package_changes_remove_options ;;
|
||||||
package-remove) _arguments -C -s $_shtab_ahriman_package_remove_options ;;
|
package-remove) _arguments -C -s $_shtab_ahriman_package_remove_options ;;
|
||||||
package-status) _arguments -C -s $_shtab_ahriman_package_status_options ;;
|
package-status) _arguments -C -s $_shtab_ahriman_package_status_options ;;
|
||||||
package-status-remove) _arguments -C -s $_shtab_ahriman_package_status_remove_options ;;
|
package-status-remove) _arguments -C -s $_shtab_ahriman_package_status_remove_options ;;
|
||||||
|
|||||||
@ -31,6 +31,7 @@ class MethodTypeOrder(StrEnum):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
Class(MethodTypeOrder): (class attribute) class method
|
Class(MethodTypeOrder): (class attribute) class method
|
||||||
|
Delete(MethodTypeOrder): (class attribute) destructor-like methods
|
||||||
Init(MethodTypeOrder): (class attribute) initialization method
|
Init(MethodTypeOrder): (class attribute) initialization method
|
||||||
Magic(MethodTypeOrder): (class attribute) other magical methods
|
Magic(MethodTypeOrder): (class attribute) other magical methods
|
||||||
New(MethodTypeOrder): (class attribute) constructor method
|
New(MethodTypeOrder): (class attribute) constructor method
|
||||||
@ -40,6 +41,7 @@ class MethodTypeOrder(StrEnum):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
Class = "classmethod"
|
Class = "classmethod"
|
||||||
|
Delete = "del"
|
||||||
Init = "init"
|
Init = "init"
|
||||||
Magic = "magic"
|
Magic = "magic"
|
||||||
New = "new"
|
New = "new"
|
||||||
@ -76,8 +78,9 @@ class DefinitionOrder(BaseRawFileChecker):
|
|||||||
"method-type-order",
|
"method-type-order",
|
||||||
{
|
{
|
||||||
"default": [
|
"default": [
|
||||||
"new",
|
|
||||||
"init",
|
"init",
|
||||||
|
"new",
|
||||||
|
"del",
|
||||||
"property",
|
"property",
|
||||||
"classmethod",
|
"classmethod",
|
||||||
"staticmethod",
|
"staticmethod",
|
||||||
@ -122,10 +125,12 @@ class DefinitionOrder(BaseRawFileChecker):
|
|||||||
MethodTypeOrder: resolved function type
|
MethodTypeOrder: resolved function type
|
||||||
"""
|
"""
|
||||||
# init methods
|
# init methods
|
||||||
if function.name in ("__new__",):
|
|
||||||
return MethodTypeOrder.New
|
|
||||||
if function.name in ("__init__", "__post_init__"):
|
if function.name in ("__init__", "__post_init__"):
|
||||||
return MethodTypeOrder.Init
|
return MethodTypeOrder.Init
|
||||||
|
if function.name in ("__new__",):
|
||||||
|
return MethodTypeOrder.New
|
||||||
|
if function.name in ("__del__",):
|
||||||
|
return MethodTypeOrder.Delete
|
||||||
|
|
||||||
# decorated methods
|
# decorated methods
|
||||||
decorators = []
|
decorators = []
|
||||||
|
|||||||
@ -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.12.0"
|
__version__ = "2.12.2"
|
||||||
|
|||||||
@ -87,11 +87,11 @@ def _parser() -> argparse.ArgumentParser:
|
|||||||
parser.add_argument("--repository-id", help=argparse.SUPPRESS)
|
parser.add_argument("--repository-id", help=argparse.SUPPRESS)
|
||||||
parser.add_argument("--unsafe", help="allow to run ahriman as non-ahriman user. Some actions might be unavailable",
|
parser.add_argument("--unsafe", help="allow to run ahriman as non-ahriman user. Some actions might be unavailable",
|
||||||
action="store_true")
|
action="store_true")
|
||||||
|
parser.add_argument("-V", "--version", action="version", version=__version__)
|
||||||
parser.add_argument("--wait-timeout", help="wait for lock to be free. Negative value will lead to "
|
parser.add_argument("--wait-timeout", help="wait for lock to be free. Negative value will lead to "
|
||||||
"immediate application run even if there is lock file. "
|
"immediate application run even if there is lock file. "
|
||||||
"In case of zero value, the application will wait infinitely",
|
"In case of zero value, the application will wait infinitely",
|
||||||
type=int, default=-1)
|
type=int, default=-1)
|
||||||
parser.add_argument("-V", "--version", action="version", version=__version__)
|
|
||||||
|
|
||||||
subparsers = parser.add_subparsers(title="command", help="command to run", dest="command")
|
subparsers = parser.add_subparsers(title="command", help="command to run", dest="command")
|
||||||
|
|
||||||
@ -101,6 +101,8 @@ def _parser() -> argparse.ArgumentParser:
|
|||||||
_set_help_updates_parser(subparsers)
|
_set_help_updates_parser(subparsers)
|
||||||
_set_help_version_parser(subparsers)
|
_set_help_version_parser(subparsers)
|
||||||
_set_package_add_parser(subparsers)
|
_set_package_add_parser(subparsers)
|
||||||
|
_set_package_changes_parser(subparsers)
|
||||||
|
_set_package_changes_remove_parser(subparsers)
|
||||||
_set_package_remove_parser(subparsers)
|
_set_package_remove_parser(subparsers)
|
||||||
_set_package_status_parser(subparsers)
|
_set_package_status_parser(subparsers)
|
||||||
_set_package_status_remove_parser(subparsers)
|
_set_package_status_remove_parser(subparsers)
|
||||||
@ -178,8 +180,8 @@ def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.Argument
|
|||||||
"""
|
"""
|
||||||
parser = root.add_parser("help-commands-unsafe", help="list unsafe commands",
|
parser = root.add_parser("help-commands-unsafe", help="list unsafe commands",
|
||||||
description="list unsafe commands as defined in default args", formatter_class=_formatter)
|
description="list unsafe commands as defined in default args", formatter_class=_formatter)
|
||||||
parser.add_argument("command", help="instead of showing commands, just test command line for unsafe subcommand "
|
parser.add_argument("subcommand", help="instead of showing commands, just test command line for unsafe subcommand "
|
||||||
"and return 0 in case if command is safe and 1 otherwise", nargs="*")
|
"and return 0 in case if command is safe and 1 otherwise", nargs="*")
|
||||||
parser.set_defaults(handler=handlers.UnsafeCommands, architecture="", lock=None, quiet=True, report=False,
|
parser.set_defaults(handler=handlers.UnsafeCommands, architecture="", lock=None, quiet=True, report=False,
|
||||||
repository="", unsafe=True, parser=_parser)
|
repository="", unsafe=True, parser=_parser)
|
||||||
return parser
|
return parser
|
||||||
@ -198,7 +200,7 @@ def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
parser = root.add_parser("help", help="show help message",
|
parser = root.add_parser("help", help="show help message",
|
||||||
description="show help message for application or command and exit",
|
description="show help message for application or command and exit",
|
||||||
formatter_class=_formatter)
|
formatter_class=_formatter)
|
||||||
parser.add_argument("command", help="show help message for specific command", nargs="?")
|
parser.add_argument("subcommand", help="show help message for specific command", nargs="?")
|
||||||
parser.set_defaults(handler=handlers.Help, architecture="", lock=None, quiet=True, report=False, repository="",
|
parser.set_defaults(handler=handlers.Help, architecture="", lock=None, quiet=True, report=False, repository="",
|
||||||
unsafe=True, parser=_parser)
|
unsafe=True, parser=_parser)
|
||||||
return parser
|
return parser
|
||||||
@ -281,6 +283,44 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def _set_package_changes_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
|
"""
|
||||||
|
add parser for package changes subcommand
|
||||||
|
|
||||||
|
Args:
|
||||||
|
root(SubParserAction): subparsers for the commands
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
argparse.ArgumentParser: created argument parser
|
||||||
|
"""
|
||||||
|
parser = root.add_parser("package-changes", help="get package changes",
|
||||||
|
description="retrieve package changes stored in database",
|
||||||
|
epilog="This feature requests package status from the web interface if it is available.",
|
||||||
|
formatter_class=_formatter)
|
||||||
|
parser.add_argument("package", help="package base")
|
||||||
|
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||||
|
parser.set_defaults(handler=handlers.Change, action=Action.List, lock=None, quiet=True, report=False, unsafe=True)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def _set_package_changes_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
|
"""
|
||||||
|
add parser for package change remove subcommand
|
||||||
|
|
||||||
|
Args:
|
||||||
|
root(SubParserAction): subparsers for the commands
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
argparse.ArgumentParser: created argument parser
|
||||||
|
"""
|
||||||
|
parser = root.add_parser("package-changes-remove", help="remove package changes",
|
||||||
|
description="remove the package changes stored remotely",
|
||||||
|
formatter_class=_formatter)
|
||||||
|
parser.add_argument("package", help="package base")
|
||||||
|
parser.set_defaults(handler=handlers.Change, action=Action.Remove, lock=None, quiet=True, report=False, unsafe=True)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def _set_package_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
def _set_package_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
"""
|
"""
|
||||||
add parser for package removal subcommand
|
add parser for package removal subcommand
|
||||||
@ -493,6 +533,9 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
description="check for packages updates. Same as repo-update --dry-run --no-manual",
|
description="check for packages updates. Same as repo-update --dry-run --no-manual",
|
||||||
formatter_class=_formatter)
|
formatter_class=_formatter)
|
||||||
parser.add_argument("package", help="filter check by package base", nargs="*")
|
parser.add_argument("package", help="filter check by package base", nargs="*")
|
||||||
|
parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
|
||||||
|
"Only applicable in dry run mode",
|
||||||
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
|
||||||
parser.add_argument("--vcs", help="fetch actual version of VCS packages",
|
parser.add_argument("--vcs", help="fetch actual version of VCS packages",
|
||||||
action=argparse.BooleanOptionalAction, default=True)
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
@ -558,8 +601,12 @@ def _set_repo_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12)
|
parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12)
|
||||||
parser.add_argument("--aur", help="enable or disable checking for AUR updates",
|
parser.add_argument("--aur", help="enable or disable checking for AUR updates",
|
||||||
action=argparse.BooleanOptionalAction, default=True)
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
|
parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
|
||||||
|
"Only applicable in dry run mode",
|
||||||
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
parser.add_argument("--dependencies", help="process missing package dependencies",
|
parser.add_argument("--dependencies", help="process missing package dependencies",
|
||||||
action=argparse.BooleanOptionalAction, default=True)
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
|
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
|
||||||
parser.add_argument("--local", help="enable or disable checking of local packages for updates",
|
parser.add_argument("--local", help="enable or disable checking of local packages for updates",
|
||||||
action=argparse.BooleanOptionalAction, default=True)
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
parser.add_argument("--manual", help="include or exclude manual updates",
|
parser.add_argument("--manual", help="include or exclude manual updates",
|
||||||
@ -569,7 +616,7 @@ def _set_repo_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
|
||||||
"-yy to force refresh even if up to date",
|
"-yy to force refresh even if up to date",
|
||||||
action="count", default=False)
|
action="count", default=False)
|
||||||
parser.set_defaults(handler=handlers.Daemon, dry_run=False, exit_code=False, package=[])
|
parser.set_defaults(handler=handlers.Daemon, exit_code=False, package=[])
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
@ -769,6 +816,9 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
parser.add_argument("package", help="filter check by package base", nargs="*")
|
parser.add_argument("package", help="filter check by package base", nargs="*")
|
||||||
parser.add_argument("--aur", help="enable or disable checking for AUR updates",
|
parser.add_argument("--aur", help="enable or disable checking for AUR updates",
|
||||||
action=argparse.BooleanOptionalAction, default=True)
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
|
parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
|
||||||
|
"Only applicable in dry run mode",
|
||||||
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
parser.add_argument("--dependencies", help="process missing package dependencies",
|
parser.add_argument("--dependencies", help="process missing package dependencies",
|
||||||
action=argparse.BooleanOptionalAction, default=True)
|
action=argparse.BooleanOptionalAction, default=True)
|
||||||
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
|
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
|
||||||
|
|||||||
@ -150,7 +150,7 @@ class Application(ApplicationPackages, ApplicationRepository):
|
|||||||
with_dependencies[package.base] = package
|
with_dependencies[package.base] = package
|
||||||
|
|
||||||
# register package in local database
|
# register package in local database
|
||||||
self.database.remote_update(package)
|
self.database.package_base_update(package)
|
||||||
self.repository.reporter.set_unknown(package)
|
self.repository.reporter.set_unknown(package)
|
||||||
|
|
||||||
return list(with_dependencies.values())
|
return list(with_dependencies.values())
|
||||||
|
|||||||
@ -65,7 +65,7 @@ class ApplicationPackages(ApplicationProperties):
|
|||||||
"""
|
"""
|
||||||
package = Package.from_aur(source, username)
|
package = Package.from_aur(source, username)
|
||||||
self.database.build_queue_insert(package)
|
self.database.build_queue_insert(package)
|
||||||
self.database.remote_update(package)
|
self.database.package_base_update(package)
|
||||||
|
|
||||||
def _add_directory(self, source: str, *_: Any) -> None:
|
def _add_directory(self, source: str, *_: Any) -> None:
|
||||||
"""
|
"""
|
||||||
@ -139,7 +139,7 @@ class ApplicationPackages(ApplicationProperties):
|
|||||||
"""
|
"""
|
||||||
package = Package.from_official(source, self.repository.pacman, username)
|
package = Package.from_official(source, self.repository.pacman, username)
|
||||||
self.database.build_queue_insert(package)
|
self.database.build_queue_insert(package)
|
||||||
self.database.remote_update(package)
|
self.database.package_base_update(package)
|
||||||
|
|
||||||
def add(self, names: Iterable[str], source: PackageSource, username: str | None = None) -> None:
|
def add(self, names: Iterable[str], source: PackageSource, username: str | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
@ -167,12 +167,16 @@ class ApplicationPackages(ApplicationProperties):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def remove(self, names: Iterable[str]) -> None:
|
def remove(self, names: Iterable[str]) -> Result:
|
||||||
"""
|
"""
|
||||||
remove packages from repository
|
remove packages from repository
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
names(Iterable[str]): list of packages (either base or name) to remove
|
names(Iterable[str]): list of packages (either base or name) to remove
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Result: removal result
|
||||||
"""
|
"""
|
||||||
self.repository.process_remove(names)
|
result = self.repository.process_remove(names)
|
||||||
self.on_result(Result())
|
self.on_result(result)
|
||||||
|
return result
|
||||||
|
|||||||
@ -18,11 +18,10 @@
|
|||||||
# 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.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from ahriman.application.application.application_properties import ApplicationProperties
|
from ahriman.application.application.application_properties import ApplicationProperties
|
||||||
|
from ahriman.application.application.workers import Updater
|
||||||
from ahriman.core.build_tools.sources import Sources
|
from ahriman.core.build_tools.sources import Sources
|
||||||
from ahriman.core.tree import Tree
|
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.packagers import Packagers
|
from ahriman.models.packagers import Packagers
|
||||||
from ahriman.models.result import Result
|
from ahriman.models.result import Result
|
||||||
@ -33,6 +32,23 @@ class ApplicationRepository(ApplicationProperties):
|
|||||||
repository control class
|
repository control class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def changes(self, packages: Iterable[Package]) -> None:
|
||||||
|
"""
|
||||||
|
generate and update package changes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages(Iterable[Package]): list of packages to retrieve changes
|
||||||
|
"""
|
||||||
|
last_commit_hashes = self.database.hashes_get()
|
||||||
|
|
||||||
|
for package in packages:
|
||||||
|
last_commit_sha = last_commit_hashes.get(package.base)
|
||||||
|
if last_commit_sha is None:
|
||||||
|
continue # skip check in case if we can't calculate diff
|
||||||
|
|
||||||
|
changes = self.repository.package_changes(package, last_commit_sha)
|
||||||
|
self.repository.reporter.package_changes_set(package.base, changes)
|
||||||
|
|
||||||
def clean(self, *, cache: bool, chroot: bool, manual: bool, packages: bool, pacman: bool) -> None:
|
def clean(self, *, cache: bool, chroot: bool, manual: bool, packages: bool, pacman: bool) -> None:
|
||||||
"""
|
"""
|
||||||
run all clean methods. Warning: some functions might not be available under non-root
|
run all clean methods. Warning: some functions might not be available under non-root
|
||||||
@ -137,26 +153,25 @@ class ApplicationRepository(ApplicationProperties):
|
|||||||
Returns:
|
Returns:
|
||||||
Result: update result
|
Result: update result
|
||||||
"""
|
"""
|
||||||
def process_update(paths: Iterable[Path], result: Result) -> None:
|
result = Result()
|
||||||
if not paths:
|
|
||||||
return # don't need to process if no update supplied
|
|
||||||
update_result = self.repository.process_update(paths, packagers)
|
|
||||||
self.on_result(result.merge(update_result))
|
|
||||||
|
|
||||||
# process built packages
|
# process already built packages if any
|
||||||
build_result = Result()
|
built_packages = self.repository.packages_built()
|
||||||
packages = self.repository.packages_built()
|
if built_packages: # speedup a bit
|
||||||
process_update(packages, build_result)
|
build_result = self.repository.process_update(built_packages, packagers)
|
||||||
|
result.merge(build_result)
|
||||||
|
self.on_result(result.merge(build_result))
|
||||||
|
|
||||||
# process manual packages
|
builder = Updater.load(self.repository_id, self.configuration, self.repository)
|
||||||
tree = Tree.resolve(updates)
|
|
||||||
for num, level in enumerate(tree):
|
|
||||||
self.logger.info("processing level #%i %s", num, [package.base for package in level])
|
|
||||||
build_result = self.repository.process_build(level, packagers, bump_pkgrel=bump_pkgrel)
|
|
||||||
packages = self.repository.packages_built()
|
|
||||||
process_update(packages, build_result)
|
|
||||||
|
|
||||||
return build_result
|
# ok so for now we split all packages into chunks and process each chunk accordingly
|
||||||
|
partitions = builder.partition(updates)
|
||||||
|
for num, partition in enumerate(partitions):
|
||||||
|
self.logger.info("processing chunk #%i %s", num, [package.base for package in partition])
|
||||||
|
build_result = builder.update(partition, packagers, bump_pkgrel=bump_pkgrel)
|
||||||
|
self.on_result(result.merge(build_result))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def updates(self, filter_packages: Iterable[str], *,
|
def updates(self, filter_packages: Iterable[str], *,
|
||||||
aur: bool, local: bool, manual: bool, vcs: bool) -> list[Package]:
|
aur: bool, local: bool, manual: bool, vcs: bool) -> list[Package]:
|
||||||
|
|||||||
20
src/ahriman/application/application/workers/__init__.py
Normal file
20
src/ahriman/application/application/workers/__init__.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#
|
||||||
|
# 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 ahriman.application.application.workers.updater import Updater
|
||||||
77
src/ahriman/application/application/workers/local_updater.py
Normal file
77
src/ahriman/application/application/workers/local_updater.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#
|
||||||
|
# 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 collections.abc import Iterable
|
||||||
|
|
||||||
|
from ahriman.application.application.workers.updater import Updater
|
||||||
|
from ahriman.core.repository import Repository
|
||||||
|
from ahriman.core.tree import Tree
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.packagers import Packagers
|
||||||
|
from ahriman.models.result import Result
|
||||||
|
|
||||||
|
|
||||||
|
class LocalUpdater(Updater):
|
||||||
|
"""
|
||||||
|
local build process implementation
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
repository(Repository): repository instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, repository: Repository) -> None:
|
||||||
|
"""
|
||||||
|
default constructor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
repository(Repository): repository instance
|
||||||
|
"""
|
||||||
|
self.repository = repository
|
||||||
|
|
||||||
|
def partition(self, packages: Iterable[Package]) -> list[list[Package]]:
|
||||||
|
"""
|
||||||
|
split packages into partitions to be processed by this worker
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages(Iterable[Package]): list of packages to partition
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[list[Package]]: packages partitioned by this worker type
|
||||||
|
"""
|
||||||
|
return Tree.resolve(packages)
|
||||||
|
|
||||||
|
def update(self, updates: Iterable[Package], packagers: Packagers | None = None, *,
|
||||||
|
bump_pkgrel: bool = False) -> Result:
|
||||||
|
"""
|
||||||
|
run package updates
|
||||||
|
|
||||||
|
Args:
|
||||||
|
updates(Iterable[Package]): list of packages to update
|
||||||
|
packagers(Packagers | None, optional): optional override of username for build process
|
||||||
|
(Default value = None)
|
||||||
|
bump_pkgrel(bool, optional): bump pkgrel in case of local version conflict (Default value = False)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Result: update result
|
||||||
|
"""
|
||||||
|
build_result = self.repository.process_build(updates, packagers, bump_pkgrel=bump_pkgrel)
|
||||||
|
packages = self.repository.packages_built()
|
||||||
|
update_result = self.repository.process_update(packages, packagers)
|
||||||
|
|
||||||
|
return build_result.merge(update_result)
|
||||||
140
src/ahriman/application/application/workers/remote_updater.py
Normal file
140
src/ahriman/application/application/workers/remote_updater.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#
|
||||||
|
# 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 collections import deque
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
|
from ahriman.application.application.workers.updater import Updater
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.http import SyncAhrimanClient
|
||||||
|
from ahriman.core.tree import Tree
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.packagers import Packagers
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
from ahriman.models.result import Result
|
||||||
|
from ahriman.models.worker import Worker
|
||||||
|
|
||||||
|
|
||||||
|
class RemoteUpdater(Updater):
|
||||||
|
"""
|
||||||
|
remote update worker
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
workers(list[Worker]): worker identifiers
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, workers: list[Worker], repository_id: RepositoryId, configuration: Configuration) -> None:
|
||||||
|
"""
|
||||||
|
default constructor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
workers(list[Worker]): worker identifiers
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
|
"""
|
||||||
|
self.workers = workers
|
||||||
|
self.repository_id = repository_id
|
||||||
|
self.configuration = configuration
|
||||||
|
|
||||||
|
self._clients: deque[tuple[Worker, SyncAhrimanClient]] = deque()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def clients(self) -> dict[Worker, SyncAhrimanClient]:
|
||||||
|
"""
|
||||||
|
extract loaded clients. Note that this method yields only workers which have been already loaded
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict[Worker, SyncAhrimanClient]: map of the worker to the related web client
|
||||||
|
"""
|
||||||
|
return dict(self._clients)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _update_url(worker: Worker) -> str:
|
||||||
|
"""
|
||||||
|
get url for updates
|
||||||
|
|
||||||
|
Args:
|
||||||
|
worker(Worker): worker identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: full url for web service to run update process
|
||||||
|
"""
|
||||||
|
return f"{worker.address}/api/v1/service/add"
|
||||||
|
|
||||||
|
def next_worker(self) -> tuple[Worker, SyncAhrimanClient]:
|
||||||
|
"""
|
||||||
|
generate next not-used web client. In case if all clients have been already used, it yields next not used client
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[Worker, SyncAhrimanClient]: worker and constructed client instance for the web
|
||||||
|
"""
|
||||||
|
# check if there is not used yet worker
|
||||||
|
worker = next((worker for worker in self.workers if worker not in self.clients), None)
|
||||||
|
if worker is not None:
|
||||||
|
client = SyncAhrimanClient(self.configuration, "status")
|
||||||
|
client.address = worker.address
|
||||||
|
else:
|
||||||
|
worker, client = self._clients.popleft()
|
||||||
|
|
||||||
|
# register worker in the queue
|
||||||
|
self._clients.append((worker, client))
|
||||||
|
|
||||||
|
return worker, client
|
||||||
|
|
||||||
|
def partition(self, packages: Iterable[Package]) -> list[list[Package]]:
|
||||||
|
"""
|
||||||
|
split packages into partitions to be processed by this worker
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages(Iterable[Package]): list of packages to partition
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[list[Package]]: packages partitioned by this worker type
|
||||||
|
"""
|
||||||
|
return Tree.partition(packages, count=len(self.workers))
|
||||||
|
|
||||||
|
def update(self, updates: Iterable[Package], packagers: Packagers | None = None, *,
|
||||||
|
bump_pkgrel: bool = False) -> Result:
|
||||||
|
"""
|
||||||
|
run package updates
|
||||||
|
|
||||||
|
Args:
|
||||||
|
updates(Iterable[Package]): list of packages to update
|
||||||
|
packagers(Packagers | None, optional): optional override of username for build process
|
||||||
|
(Default value = None)
|
||||||
|
bump_pkgrel(bool, optional): bump pkgrel in case of local version conflict (Default value = False)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Result: update result
|
||||||
|
"""
|
||||||
|
payload = {
|
||||||
|
"increment": bump_pkgrel,
|
||||||
|
"packager": packagers.default if packagers is not None else None,
|
||||||
|
"packages": [package.base for package in updates],
|
||||||
|
"patches": [], # might be used later
|
||||||
|
"refresh": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
worker, client = self.next_worker()
|
||||||
|
client.make_request("POST", self._update_url(worker), params=self.repository_id.query(), json=payload)
|
||||||
|
# we don't block here for process
|
||||||
|
|
||||||
|
return Result()
|
||||||
102
src/ahriman/application/application/workers/updater.py
Normal file
102
src/ahriman/application/application/workers/updater.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#
|
||||||
|
# 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 __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.log import LazyLogging
|
||||||
|
from ahriman.core.repository import Repository
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.packagers import Packagers
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
from ahriman.models.result import Result
|
||||||
|
from ahriman.models.worker import Worker
|
||||||
|
|
||||||
|
|
||||||
|
class Updater(LazyLogging):
|
||||||
|
"""
|
||||||
|
updater handler interface
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
split_method(Callable[[Iterable[Package]], list[list[Package]]]): method to split packages into chunks
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load(repository_id: RepositoryId, configuration: Configuration,
|
||||||
|
repository: Repository, workers: list[Worker] | None = None) -> Updater:
|
||||||
|
"""
|
||||||
|
construct updaters from parameters
|
||||||
|
|
||||||
|
Args:
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
|
repository(Repository): repository instance
|
||||||
|
workers(list[Worker] | None, optional): worker identifiers if any (Default value = None)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Updater: constructed updater worker
|
||||||
|
"""
|
||||||
|
if workers is None:
|
||||||
|
# no workers set explicitly, try to guess from configuration
|
||||||
|
workers = [Worker(address) for address in configuration.getlist("build", "workers", fallback=[])]
|
||||||
|
|
||||||
|
if workers:
|
||||||
|
# there is something we could use as remote workers
|
||||||
|
from ahriman.application.application.workers.remote_updater import RemoteUpdater
|
||||||
|
return RemoteUpdater(workers, repository_id, configuration)
|
||||||
|
|
||||||
|
# and finally no workers available, just use local service
|
||||||
|
from ahriman.application.application.workers.local_updater import LocalUpdater
|
||||||
|
return LocalUpdater(repository)
|
||||||
|
|
||||||
|
def partition(self, packages: Iterable[Package]) -> list[list[Package]]:
|
||||||
|
"""
|
||||||
|
split packages into partitions to be processed by this worker
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages(Iterable[Package]): list of packages to partition
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[list[Package]]: packages partitioned by this worker type
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotImplementedError: not implemented method
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def update(self, updates: Iterable[Package], packagers: Packagers | None = None, *,
|
||||||
|
bump_pkgrel: bool = False) -> Result:
|
||||||
|
"""
|
||||||
|
run package updates
|
||||||
|
|
||||||
|
Args:
|
||||||
|
updates(Iterable[Package]): list of packages to update
|
||||||
|
packagers(Packagers | None, optional): optional override of username for build process
|
||||||
|
(Default value = None)
|
||||||
|
bump_pkgrel(bool, optional): bump pkgrel in case of local version conflict (Default value = False)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Result: update result
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotImplementedError: not implemented method
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
@ -21,6 +21,7 @@ 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.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
|
||||||
|
|||||||
59
src/ahriman/application/handlers/change.py
Normal file
59
src/ahriman/application/handlers/change.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from ahriman.application.application import Application
|
||||||
|
from ahriman.application.handlers import Handler
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.formatters import ChangesPrinter
|
||||||
|
from ahriman.models.action import Action
|
||||||
|
from ahriman.models.changes import Changes
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
|
|
||||||
|
class Change(Handler):
|
||||||
|
"""
|
||||||
|
package changes handler
|
||||||
|
"""
|
||||||
|
|
||||||
|
ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
|
||||||
|
report: bool) -> None:
|
||||||
|
"""
|
||||||
|
callback for command line
|
||||||
|
|
||||||
|
Args:
|
||||||
|
args(argparse.Namespace): command line args
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
|
report(bool): force enable or disable reporting
|
||||||
|
"""
|
||||||
|
application = Application(repository_id, configuration, report=True)
|
||||||
|
client = application.repository.reporter
|
||||||
|
|
||||||
|
match args.action:
|
||||||
|
case Action.List:
|
||||||
|
changes = client.package_changes_get(args.package)
|
||||||
|
ChangesPrinter(changes)(verbose=True, separator="")
|
||||||
|
Change.check_if_empty(args.exit_code, changes.is_empty)
|
||||||
|
case Action.Remove:
|
||||||
|
client.package_changes_set(args.package, Changes())
|
||||||
@ -43,8 +43,10 @@ class Daemon(Handler):
|
|||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
"""
|
"""
|
||||||
from ahriman.application.handlers import Update
|
from ahriman.application.handlers import Update
|
||||||
Update.run(args, repository_id, configuration, report=report)
|
|
||||||
timer = threading.Timer(args.interval, Daemon.run, args=[args, repository_id, configuration],
|
event = threading.Event()
|
||||||
kwargs={"report": report})
|
try:
|
||||||
timer.start()
|
while not event.wait(args.interval):
|
||||||
timer.join()
|
Update.run(args, repository_id, configuration, report=report)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass # normal exit
|
||||||
|
|||||||
@ -44,7 +44,7 @@ class Help(Handler):
|
|||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
"""
|
"""
|
||||||
parser: argparse.ArgumentParser = args.parser()
|
parser: argparse.ArgumentParser = args.parser()
|
||||||
if args.command is None:
|
if args.subcommand is None:
|
||||||
parser.parse_args(["--help"])
|
parser.parse_args(["--help"])
|
||||||
else:
|
else:
|
||||||
parser.parse_args([args.command, "--help"])
|
parser.parse_args([args.subcommand, "--help"])
|
||||||
|
|||||||
@ -24,6 +24,7 @@ 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
|
||||||
|
from ahriman.models.packagers import Packagers
|
||||||
from ahriman.models.repository_id import RepositoryId
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ class Rebuild(Handler):
|
|||||||
application.print_updates(updates, log_fn=print)
|
application.print_updates(updates, log_fn=print)
|
||||||
return
|
return
|
||||||
|
|
||||||
result = application.update(updates, args.username, bump_pkgrel=args.increment)
|
result = application.update(updates, Packagers(args.username), bump_pkgrel=args.increment)
|
||||||
Rebuild.check_if_empty(args.exit_code, result.is_empty)
|
Rebuild.check_if_empty(args.exit_code, result.is_empty)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import argparse
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pwd import getpwuid
|
from pwd import getpwuid
|
||||||
|
from urllib.parse import quote_plus as urlencode
|
||||||
|
|
||||||
from ahriman.application.application import Application
|
from ahriman.application.application import Application
|
||||||
from ahriman.application.handlers import Handler
|
from ahriman.application.handlers import Handler
|
||||||
@ -128,8 +129,12 @@ class Setup(Handler):
|
|||||||
|
|
||||||
if args.web_port is not None:
|
if args.web_port is not None:
|
||||||
configuration.set_option("web", "port", str(args.web_port))
|
configuration.set_option("web", "port", str(args.web_port))
|
||||||
|
if (host := root.get("web", "host", fallback=None)) is not None:
|
||||||
|
configuration.set_option("status", "address", f"http://{host}:{args.web_port}")
|
||||||
if args.web_unix_socket is not None:
|
if args.web_unix_socket is not None:
|
||||||
configuration.set_option("web", "unix_socket", str(args.web_unix_socket))
|
unix_socket = str(args.web_unix_socket)
|
||||||
|
configuration.set_option("web", "unix_socket", unix_socket)
|
||||||
|
configuration.set_option("status", "address", f"http+unix://{urlencode(unix_socket)}")
|
||||||
|
|
||||||
if args.generate_salt:
|
if args.generate_salt:
|
||||||
configuration.set_option("auth", "salt", User.generate_password(20))
|
configuration.set_option("auth", "salt", User.generate_password(20))
|
||||||
|
|||||||
@ -46,8 +46,8 @@ class UnsafeCommands(Handler):
|
|||||||
"""
|
"""
|
||||||
parser = args.parser()
|
parser = args.parser()
|
||||||
unsafe_commands = UnsafeCommands.get_unsafe_commands(parser)
|
unsafe_commands = UnsafeCommands.get_unsafe_commands(parser)
|
||||||
if args.command:
|
if args.subcommand:
|
||||||
UnsafeCommands.check_unsafe(args.command, unsafe_commands, parser)
|
UnsafeCommands.check_unsafe(args.subcommand, unsafe_commands, parser)
|
||||||
else:
|
else:
|
||||||
for command in unsafe_commands:
|
for command in unsafe_commands:
|
||||||
StringPrinter(command)(verbose=True)
|
StringPrinter(command)(verbose=True)
|
||||||
|
|||||||
@ -47,9 +47,13 @@ class Update(Handler):
|
|||||||
"""
|
"""
|
||||||
application = Application(repository_id, configuration, report=report, refresh_pacman_database=args.refresh)
|
application = Application(repository_id, configuration, report=report, refresh_pacman_database=args.refresh)
|
||||||
application.on_start()
|
application.on_start()
|
||||||
|
|
||||||
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs)
|
packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs)
|
||||||
Update.check_if_empty(args.exit_code, not packages)
|
if args.dry_run: # some check specific actions
|
||||||
if args.dry_run:
|
if args.changes: # generate changes if requested
|
||||||
|
application.changes(packages)
|
||||||
|
|
||||||
|
Update.check_if_empty(args.exit_code, not packages) # status code check
|
||||||
return
|
return
|
||||||
|
|
||||||
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
|
packages = application.with_dependencies(packages, process_dependencies=args.dependencies)
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import aiohttp_security # type: ignore[import-untyped]
|
import aiohttp_security
|
||||||
_has_aiohttp_security = True
|
_has_aiohttp_security = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
_has_aiohttp_security = False
|
_has_aiohttp_security = False
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import shutil
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ahriman.core.exceptions import CalledProcessError
|
||||||
from ahriman.core.log import LazyLogging
|
from ahriman.core.log import LazyLogging
|
||||||
from ahriman.core.util import check_output, utcnow, walk
|
from ahriman.core.util import check_output, utcnow, walk
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
@ -42,6 +43,25 @@ class Sources(LazyLogging):
|
|||||||
DEFAULT_BRANCH = "master" # default fallback branch
|
DEFAULT_BRANCH = "master" # default fallback branch
|
||||||
DEFAULT_COMMIT_AUTHOR = ("ahriman", "ahriman@localhost")
|
DEFAULT_COMMIT_AUTHOR = ("ahriman", "ahriman@localhost")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def changes(source_dir: Path, last_commit_sha: str | None) -> str | None:
|
||||||
|
"""
|
||||||
|
extract changes from the last known commit if available
|
||||||
|
|
||||||
|
Args:
|
||||||
|
source_dir(Path): local path to directory with source files
|
||||||
|
last_commit_sha(str | None): last known commit hash
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: changes from the last commit if available or ``None`` otherwise
|
||||||
|
"""
|
||||||
|
if last_commit_sha is None:
|
||||||
|
return None # no previous reference found
|
||||||
|
|
||||||
|
instance = Sources()
|
||||||
|
instance.fetch_until(source_dir, commit_sha=last_commit_sha)
|
||||||
|
return instance.diff(source_dir, last_commit_sha)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def extend_architectures(sources_dir: Path, architecture: str) -> list[PkgbuildPatch]:
|
def extend_architectures(sources_dir: Path, architecture: str) -> list[PkgbuildPatch]:
|
||||||
"""
|
"""
|
||||||
@ -61,13 +81,16 @@ class Sources(LazyLogging):
|
|||||||
return [PkgbuildPatch("arch", list(architectures))]
|
return [PkgbuildPatch("arch", list(architectures))]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fetch(sources_dir: Path, remote: RemoteSource) -> None:
|
def fetch(sources_dir: Path, remote: RemoteSource) -> str | None:
|
||||||
"""
|
"""
|
||||||
either clone repository or update it to origin/``remote.branch``
|
either clone repository or update it to origin/``remote.branch``
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sources_dir(Path): local path to fetch
|
sources_dir(Path): local path to fetch
|
||||||
remote(RemoteSource): remote target (from where to fetch)
|
remote(RemoteSource): remote target (from where to fetch)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: current commit sha if available
|
||||||
"""
|
"""
|
||||||
instance = Sources()
|
instance = Sources()
|
||||||
# local directory exists and there is .git directory
|
# local directory exists and there is .git directory
|
||||||
@ -75,13 +98,12 @@ class Sources(LazyLogging):
|
|||||||
if is_initialized_git and not instance.has_remotes(sources_dir):
|
if is_initialized_git and not instance.has_remotes(sources_dir):
|
||||||
# there is git repository, but no remote configured so far
|
# there is git repository, but no remote configured so far
|
||||||
instance.logger.info("skip update at %s because there are no branches configured", sources_dir)
|
instance.logger.info("skip update at %s because there are no branches configured", sources_dir)
|
||||||
return
|
return instance.head(sources_dir)
|
||||||
|
|
||||||
branch = remote.branch or instance.DEFAULT_BRANCH
|
branch = remote.branch or instance.DEFAULT_BRANCH
|
||||||
if is_initialized_git:
|
if is_initialized_git:
|
||||||
instance.logger.info("update HEAD to remote at %s using branch %s", sources_dir, branch)
|
instance.logger.info("update HEAD to remote at %s using branch %s", sources_dir, branch)
|
||||||
check_output("git", "fetch", "--quiet", "--depth", "1", "origin", branch,
|
instance.fetch_until(sources_dir, branch=branch)
|
||||||
cwd=sources_dir, logger=instance.logger)
|
|
||||||
elif remote.git_url is not None:
|
elif remote.git_url is not None:
|
||||||
instance.logger.info("clone remote %s to %s using branch %s", remote.git_url, sources_dir, branch)
|
instance.logger.info("clone remote %s to %s using branch %s", remote.git_url, sources_dir, branch)
|
||||||
check_output("git", "clone", "--quiet", "--depth", "1", "--branch", branch, "--single-branch",
|
check_output("git", "clone", "--quiet", "--depth", "1", "--branch", branch, "--single-branch",
|
||||||
@ -100,6 +122,8 @@ class Sources(LazyLogging):
|
|||||||
pkgbuild_dir = remote.pkgbuild_dir or sources_dir.resolve()
|
pkgbuild_dir = remote.pkgbuild_dir or sources_dir.resolve()
|
||||||
instance.move((sources_dir / pkgbuild_dir).resolve(), sources_dir)
|
instance.move((sources_dir / pkgbuild_dir).resolve(), sources_dir)
|
||||||
|
|
||||||
|
return instance.head(sources_dir)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def has_remotes(sources_dir: Path) -> bool:
|
def has_remotes(sources_dir: Path) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -136,7 +160,7 @@ class Sources(LazyLogging):
|
|||||||
instance.commit(sources_dir)
|
instance.commit(sources_dir)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(sources_dir: Path, package: Package, patches: list[PkgbuildPatch], paths: RepositoryPaths) -> None:
|
def load(sources_dir: Path, package: Package, patches: list[PkgbuildPatch], paths: RepositoryPaths) -> str | None:
|
||||||
"""
|
"""
|
||||||
fetch sources from remote and apply patches
|
fetch sources from remote and apply patches
|
||||||
|
|
||||||
@ -145,17 +169,22 @@ class Sources(LazyLogging):
|
|||||||
package(Package): package definitions
|
package(Package): package definitions
|
||||||
patches(list[PkgbuildPatch]): optional patch to be applied
|
patches(list[PkgbuildPatch]): optional patch to be applied
|
||||||
paths(RepositoryPaths): repository paths instance
|
paths(RepositoryPaths): repository paths instance
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: current commit sha if available
|
||||||
"""
|
"""
|
||||||
instance = Sources()
|
instance = Sources()
|
||||||
if (cache_dir := paths.cache_for(package.base)).is_dir() and cache_dir != sources_dir:
|
if (cache_dir := paths.cache_for(package.base)).is_dir() and cache_dir != sources_dir:
|
||||||
# no need to clone whole repository, just copy from cache first
|
# no need to clone whole repository, just copy from cache first
|
||||||
shutil.copytree(cache_dir, sources_dir, dirs_exist_ok=True)
|
shutil.copytree(cache_dir, sources_dir, dirs_exist_ok=True)
|
||||||
instance.fetch(sources_dir, package.remote)
|
last_commit_sha = instance.fetch(sources_dir, package.remote)
|
||||||
|
|
||||||
patches.extend(instance.extend_architectures(sources_dir, paths.repository_id.architecture))
|
patches.extend(instance.extend_architectures(sources_dir, paths.repository_id.architecture))
|
||||||
for patch in patches:
|
for patch in patches:
|
||||||
instance.patch_apply(sources_dir, patch)
|
instance.patch_apply(sources_dir, patch)
|
||||||
|
|
||||||
|
return last_commit_sha
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def patch_create(sources_dir: Path, *pattern: str) -> str:
|
def patch_create(sources_dir: Path, *pattern: str) -> str:
|
||||||
"""
|
"""
|
||||||
@ -247,17 +276,47 @@ class Sources(LazyLogging):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def diff(self, sources_dir: Path) -> str:
|
def diff(self, sources_dir: Path, sha: str | None = None) -> str:
|
||||||
"""
|
"""
|
||||||
generate diff from the current version and write it to the output file
|
generate diff from the current version and write it to the output file
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sources_dir(Path): local path to git repository
|
sources_dir(Path): local path to git repository
|
||||||
|
sha(str | None, optional): optional commit sha to calculate diff (Default value = None)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: patch as plain string
|
str: patch as plain string
|
||||||
"""
|
"""
|
||||||
return check_output("git", "diff", cwd=sources_dir, logger=self.logger)
|
args = []
|
||||||
|
if sha is not None:
|
||||||
|
args.append(sha)
|
||||||
|
return check_output("git", "diff", *args, cwd=sources_dir, logger=self.logger)
|
||||||
|
|
||||||
|
def fetch_until(self, sources_dir: Path, *, branch: str | None = None, commit_sha: str | None = None) -> None:
|
||||||
|
"""
|
||||||
|
fetch repository until commit sha
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sources_dir(Path): local path to git repository
|
||||||
|
branch(str | None, optional): use specified branch (Default value = None)
|
||||||
|
commit_sha(str | None, optional): commit hash to fetch. If none set, only one will be fetched
|
||||||
|
(Default value = None)
|
||||||
|
"""
|
||||||
|
commit_sha = commit_sha or "HEAD" # if none set we just fetch the last commit
|
||||||
|
|
||||||
|
commits_count = 1
|
||||||
|
while commit_sha is not None:
|
||||||
|
command = ["git", "fetch", "--quiet", "--depth", str(commits_count)]
|
||||||
|
if branch is not None:
|
||||||
|
command += ["origin", branch]
|
||||||
|
check_output(*command, cwd=sources_dir, logger=self.logger) # fetch one more level
|
||||||
|
|
||||||
|
try:
|
||||||
|
# check if there is an object in current git directory
|
||||||
|
check_output("git", "cat-file", "-e", commit_sha, cwd=sources_dir, logger=self.logger)
|
||||||
|
commit_sha = None # reset search
|
||||||
|
except CalledProcessError:
|
||||||
|
commits_count += 1 # increase depth
|
||||||
|
|
||||||
def has_changes(self, sources_dir: Path) -> bool:
|
def has_changes(self, sources_dir: Path) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -273,6 +332,20 @@ class Sources(LazyLogging):
|
|||||||
changes = check_output("git", "diff", "--cached", "--name-only", cwd=sources_dir, logger=self.logger)
|
changes = check_output("git", "diff", "--cached", "--name-only", cwd=sources_dir, logger=self.logger)
|
||||||
return bool(changes)
|
return bool(changes)
|
||||||
|
|
||||||
|
def head(self, sources_dir: Path, ref_name: str = "HEAD") -> str:
|
||||||
|
"""
|
||||||
|
extract HEAD reference for the current git repository
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sources_dir(Path): local path to git repository
|
||||||
|
ref_name(str, optional): reference name (Default value = "HEAD")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: HEAD commit hash
|
||||||
|
"""
|
||||||
|
# we might want to parse git files instead though
|
||||||
|
return check_output("git", "rev-parse", ref_name, cwd=sources_dir)
|
||||||
|
|
||||||
def move(self, pkgbuild_dir: Path, sources_dir: Path) -> None:
|
def move(self, pkgbuild_dir: Path, sources_dir: Path) -> None:
|
||||||
"""
|
"""
|
||||||
move content from pkgbuild_dir to sources_dir
|
move content from pkgbuild_dir to sources_dir
|
||||||
|
|||||||
@ -109,7 +109,7 @@ class Task(LazyLogging):
|
|||||||
).splitlines()
|
).splitlines()
|
||||||
return [Path(package) for package in packages]
|
return [Path(package) for package in packages]
|
||||||
|
|
||||||
def init(self, sources_dir: Path, database: SQLite, local_version: str | None) -> None:
|
def init(self, sources_dir: Path, database: SQLite, local_version: str | None) -> str | None:
|
||||||
"""
|
"""
|
||||||
fetch package from git
|
fetch package from git
|
||||||
|
|
||||||
@ -118,10 +118,13 @@ class Task(LazyLogging):
|
|||||||
database(SQLite): database instance
|
database(SQLite): database instance
|
||||||
local_version(str | None): local version of the package. If set and equal to current version, it will
|
local_version(str | None): local version of the package. If set and equal to current version, it will
|
||||||
automatically bump pkgrel
|
automatically bump pkgrel
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: current commit sha if available
|
||||||
"""
|
"""
|
||||||
Sources.load(sources_dir, self.package, database.patches_get(self.package.base), self.paths)
|
last_commit_sha = Sources.load(sources_dir, self.package, database.patches_get(self.package.base), self.paths)
|
||||||
if local_version is None:
|
if local_version is None:
|
||||||
return # there is no local package or pkgrel increment is disabled
|
return last_commit_sha # there is no local package or pkgrel increment is disabled
|
||||||
|
|
||||||
# load fresh package
|
# load fresh package
|
||||||
loaded_package = Package.from_build(sources_dir, self.architecture, None)
|
loaded_package = Package.from_build(sources_dir, self.architecture, None)
|
||||||
@ -129,3 +132,5 @@ class Task(LazyLogging):
|
|||||||
self.logger.info("package %s is the same as in repo, bumping pkgrel to %s", self.package.base, pkgrel)
|
self.logger.info("package %s is the same as in repo, bumping pkgrel to %s", self.package.base, pkgrel)
|
||||||
patch = PkgbuildPatch("pkgrel", pkgrel)
|
patch = PkgbuildPatch("pkgrel", pkgrel)
|
||||||
patch.write(sources_dir / "PKGBUILD")
|
patch.write(sources_dir / "PKGBUILD")
|
||||||
|
|
||||||
|
return last_commit_sha
|
||||||
|
|||||||
@ -213,6 +213,15 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
|
|||||||
"coerce": "integer",
|
"coerce": "integer",
|
||||||
"min": 0,
|
"min": 0,
|
||||||
},
|
},
|
||||||
|
"workers": {
|
||||||
|
"type": "list",
|
||||||
|
"coerce": "list",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"empty": False,
|
||||||
|
"is_url": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -249,6 +258,37 @@ CONFIGURATION_SCHEMA: ConfigurationSchema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "dict",
|
||||||
|
"schema": {
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"coerce": "boolean",
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"type": "string",
|
||||||
|
"empty": False,
|
||||||
|
"is_url": [],
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string",
|
||||||
|
"empty": False,
|
||||||
|
},
|
||||||
|
"suppress_http_log_errors": {
|
||||||
|
"type": "boolean",
|
||||||
|
"coerce": "boolean",
|
||||||
|
},
|
||||||
|
"timeout": {
|
||||||
|
"type": "integer",
|
||||||
|
"coerce": "integer",
|
||||||
|
"min": 0,
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string",
|
||||||
|
"empty": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"web": {
|
"web": {
|
||||||
"type": "dict",
|
"type": "dict",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
|||||||
33
src/ahriman/core/database/migrations/m012_last_commit_sha.py
Normal file
33
src/ahriman/core/database/migrations/m012_last_commit_sha.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
__all__ = ["steps"]
|
||||||
|
|
||||||
|
|
||||||
|
steps = [
|
||||||
|
"""
|
||||||
|
create table package_changes (
|
||||||
|
package_base text not null,
|
||||||
|
repository text not null,
|
||||||
|
last_commit_sha text not null,
|
||||||
|
changes text,
|
||||||
|
unique (package_base, repository)
|
||||||
|
)
|
||||||
|
""",
|
||||||
|
]
|
||||||
@ -21,6 +21,7 @@ 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.logs_operations import LogsOperations
|
from ahriman.core.database.operations.logs_operations import LogsOperations
|
||||||
from ahriman.core.database.operations.package_operations import PackageOperations
|
from ahriman.core.database.operations.package_operations import PackageOperations
|
||||||
from ahriman.core.database.operations.patch_operations import PatchOperations
|
from ahriman.core.database.operations.patch_operations import PatchOperations
|
||||||
|
|||||||
143
src/ahriman/core/database/operations/changes_operations.py
Normal file
143
src/ahriman/core/database/operations/changes_operations.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#
|
||||||
|
# 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 sqlite3 import Connection
|
||||||
|
|
||||||
|
from ahriman.core.database.operations import Operations
|
||||||
|
from ahriman.models.changes import Changes
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
|
|
||||||
|
class ChangesOperations(Operations):
|
||||||
|
"""
|
||||||
|
operations for source files changes
|
||||||
|
"""
|
||||||
|
|
||||||
|
def changes_get(self, package_base: str, repository_id: RepositoryId | None = None) -> Changes:
|
||||||
|
"""
|
||||||
|
get changes for the specific package base if available
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str): package base to search
|
||||||
|
repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Changes: changes for the package base if available
|
||||||
|
"""
|
||||||
|
repository_id = repository_id or self._repository_id
|
||||||
|
|
||||||
|
def run(connection: Connection) -> Changes:
|
||||||
|
return next(
|
||||||
|
(
|
||||||
|
Changes(row["last_commit_sha"], row["changes"] or None)
|
||||||
|
for row in connection.execute(
|
||||||
|
"""
|
||||||
|
select last_commit_sha, changes from package_changes
|
||||||
|
where package_base = :package_base and repository = :repository
|
||||||
|
""",
|
||||||
|
{
|
||||||
|
"package_base": package_base,
|
||||||
|
"repository": repository_id.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Changes()
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.with_connection(run)
|
||||||
|
|
||||||
|
def changes_insert(self, package_base: str, changes: Changes, repository_id: RepositoryId | None = None) -> None:
|
||||||
|
"""
|
||||||
|
insert packages to build queue
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str): package base to insert
|
||||||
|
changes(Changes): package changes (as in patch format)
|
||||||
|
repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
|
||||||
|
"""
|
||||||
|
repository_id = repository_id or self._repository_id
|
||||||
|
|
||||||
|
def run(connection: Connection) -> None:
|
||||||
|
connection.execute(
|
||||||
|
"""
|
||||||
|
insert into package_changes
|
||||||
|
(package_base, last_commit_sha, changes, repository)
|
||||||
|
values
|
||||||
|
(:package_base, :last_commit_sha, :changes ,:repository)
|
||||||
|
on conflict (package_base, repository) do update set
|
||||||
|
last_commit_sha = :last_commit_sha, changes = :changes
|
||||||
|
""",
|
||||||
|
{
|
||||||
|
"package_base": package_base,
|
||||||
|
"last_commit_sha": changes.last_commit_sha,
|
||||||
|
"changes": changes.changes,
|
||||||
|
"repository": repository_id.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
if changes.last_commit_sha is None:
|
||||||
|
return self.changes_remove(package_base, repository_id)
|
||||||
|
return self.with_connection(run, commit=True)
|
||||||
|
|
||||||
|
def changes_remove(self, package_base: str | None, repository_id: RepositoryId | None = None) -> None:
|
||||||
|
"""
|
||||||
|
remove packages changes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str | None): optional filter by package base
|
||||||
|
repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
|
||||||
|
"""
|
||||||
|
repository_id = repository_id or self._repository_id
|
||||||
|
|
||||||
|
def run(connection: Connection) -> None:
|
||||||
|
connection.execute(
|
||||||
|
"""
|
||||||
|
delete from package_changes
|
||||||
|
where (:package_base is null or package_base = :package_base)
|
||||||
|
and repository = :repository
|
||||||
|
""",
|
||||||
|
{
|
||||||
|
"package_base": package_base,
|
||||||
|
"repository": repository_id.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
return self.with_connection(run, commit=True)
|
||||||
|
|
||||||
|
def hashes_get(self, repository_id: RepositoryId | None = None) -> dict[str, str]:
|
||||||
|
"""
|
||||||
|
extract last commit hashes if available
|
||||||
|
|
||||||
|
Args:
|
||||||
|
repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict[str, str]: map of package base to its last commit hash
|
||||||
|
"""
|
||||||
|
|
||||||
|
repository_id = repository_id or self._repository_id
|
||||||
|
|
||||||
|
def run(connection: Connection) -> dict[str, str]:
|
||||||
|
return {
|
||||||
|
row["package_base"]: row["last_commit_sha"]
|
||||||
|
for row in connection.execute(
|
||||||
|
"""select package_base, last_commit_sha from package_changes where repository = :repository""",
|
||||||
|
{"repository": repository_id.id}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.with_connection(run)
|
||||||
@ -246,6 +246,21 @@ class PackageOperations(Operations):
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def package_base_update(self, package: Package, repository_id: RepositoryId | None = None) -> None:
|
||||||
|
"""
|
||||||
|
update package base only
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package(Package): package properties
|
||||||
|
repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
|
||||||
|
"""
|
||||||
|
repository_id = repository_id or self._repository_id
|
||||||
|
|
||||||
|
def run(connection: Connection) -> None:
|
||||||
|
self._package_update_insert_base(connection, package, repository_id)
|
||||||
|
|
||||||
|
return self.with_connection(run, commit=True)
|
||||||
|
|
||||||
def package_remove(self, package_base: str, repository_id: RepositoryId | None = None) -> None:
|
def package_remove(self, package_base: str, repository_id: RepositoryId | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
remove package from database
|
remove package from database
|
||||||
@ -302,21 +317,6 @@ class PackageOperations(Operations):
|
|||||||
|
|
||||||
return self.with_connection(lambda connection: list(run(connection)))
|
return self.with_connection(lambda connection: list(run(connection)))
|
||||||
|
|
||||||
def remote_update(self, package: Package, repository_id: RepositoryId | None = None) -> None:
|
|
||||||
"""
|
|
||||||
update package remote source
|
|
||||||
|
|
||||||
Args:
|
|
||||||
package(Package): package properties
|
|
||||||
repository_id(RepositoryId, optional): repository unique identifier override (Default value = None)
|
|
||||||
"""
|
|
||||||
repository_id = repository_id or self._repository_id
|
|
||||||
|
|
||||||
def run(connection: Connection) -> None:
|
|
||||||
self._package_update_insert_base(connection, package, repository_id)
|
|
||||||
|
|
||||||
return self.with_connection(run, commit=True)
|
|
||||||
|
|
||||||
def remotes_get(self, repository_id: RepositoryId | None = None) -> dict[str, RemoteSource]:
|
def remotes_get(self, repository_id: RepositoryId | None = None) -> dict[str, RemoteSource]:
|
||||||
"""
|
"""
|
||||||
get packages remotes based on current settings
|
get packages remotes based on current settings
|
||||||
|
|||||||
@ -25,11 +25,12 @@ from typing import Self
|
|||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.database.migrations import Migrations
|
from ahriman.core.database.migrations import Migrations
|
||||||
from ahriman.core.database.operations import AuthOperations, BuildOperations, LogsOperations, PackageOperations, \
|
from ahriman.core.database.operations import AuthOperations, BuildOperations, ChangesOperations, LogsOperations, \
|
||||||
PatchOperations
|
PackageOperations, PatchOperations
|
||||||
|
|
||||||
|
|
||||||
class SQLite(AuthOperations, BuildOperations, LogsOperations, PackageOperations, PatchOperations):
|
# pylint: disable=too-many-ancestors
|
||||||
|
class SQLite(AuthOperations, BuildOperations, ChangesOperations, LogsOperations, PackageOperations, PatchOperations):
|
||||||
"""
|
"""
|
||||||
wrapper for sqlite3 database
|
wrapper for sqlite3 database
|
||||||
|
|
||||||
|
|||||||
@ -316,21 +316,6 @@ class UnknownPackageError(ValueError):
|
|||||||
ValueError.__init__(self, f"Package base {package_base} is unknown")
|
ValueError.__init__(self, f"Package base {package_base} is unknown")
|
||||||
|
|
||||||
|
|
||||||
class UnprocessedPackageStatusError(ValueError):
|
|
||||||
"""
|
|
||||||
exception for merging invalid statues
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, package_base: str) -> None:
|
|
||||||
"""
|
|
||||||
default constructor
|
|
||||||
|
|
||||||
Args:
|
|
||||||
package_base(str): package base name
|
|
||||||
"""
|
|
||||||
ValueError.__init__(self, f"Package base {package_base} had status failed, but new status is success")
|
|
||||||
|
|
||||||
|
|
||||||
class UnsafeRunError(RuntimeError):
|
class UnsafeRunError(RuntimeError):
|
||||||
"""
|
"""
|
||||||
exception which will be raised in case if user is not owner of repository
|
exception which will be raised in case if user is not owner of repository
|
||||||
|
|||||||
@ -18,16 +18,17 @@
|
|||||||
# 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.printer import Printer
|
||||||
from ahriman.core.formatters.string_printer import StringPrinter
|
|
||||||
|
|
||||||
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.configuration_paths_printer import ConfigurationPathsPrinter
|
from ahriman.core.formatters.configuration_paths_printer import ConfigurationPathsPrinter
|
||||||
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.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.tree_printer import TreePrinter
|
from ahriman.core.formatters.tree_printer import TreePrinter
|
||||||
from ahriman.core.formatters.update_printer import UpdatePrinter
|
from ahriman.core.formatters.update_printer import UpdatePrinter
|
||||||
from ahriman.core.formatters.user_printer import UserPrinter
|
from ahriman.core.formatters.user_printer import UserPrinter
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.core.util import pretty_datetime
|
from ahriman.core.util import pretty_datetime
|
||||||
from ahriman.models.aur_package import AURPackage
|
from ahriman.models.aur_package import AURPackage
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
64
src/ahriman/core/formatters/changes_printer.py
Normal file
64
src/ahriman/core/formatters/changes_printer.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#
|
||||||
|
# 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 ahriman.core.formatters import Printer
|
||||||
|
from ahriman.models.changes import Changes
|
||||||
|
from ahriman.models.property import Property
|
||||||
|
|
||||||
|
|
||||||
|
class ChangesPrinter(Printer):
|
||||||
|
"""
|
||||||
|
print content of the changes object
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
changes(Changes): package changes
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, changes: Changes) -> None:
|
||||||
|
"""
|
||||||
|
default constructor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
changes(Changes): package changes
|
||||||
|
"""
|
||||||
|
Printer.__init__(self)
|
||||||
|
self.changes = changes
|
||||||
|
|
||||||
|
def properties(self) -> list[Property]:
|
||||||
|
"""
|
||||||
|
convert content into printable data
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Property]: list of content properties
|
||||||
|
"""
|
||||||
|
if self.changes.is_empty:
|
||||||
|
return []
|
||||||
|
return [Property("", self.changes.changes, is_required=True, indent=0)]
|
||||||
|
|
||||||
|
# pylint: disable=redundant-returns-doc
|
||||||
|
def title(self) -> str | None:
|
||||||
|
"""
|
||||||
|
generate entry title from content
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str | None: content title if it can be generated and None otherwise
|
||||||
|
"""
|
||||||
|
if self.changes.is_empty:
|
||||||
|
return None
|
||||||
|
return self.changes.last_commit_sha
|
||||||
@ -19,7 +19,7 @@
|
|||||||
#
|
#
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from ahriman.core.formatters import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
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.property import Property
|
from ahriman.models.property import Property
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
|
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
from ahriman.models.repository_id import RepositoryId
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.build_status import BuildStatus
|
from ahriman.models.build_status import BuildStatus
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
|
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
|
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
from ahriman.models.user import User
|
from ahriman.models.user import User
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from ahriman.core.formatters import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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 import StringPrinter
|
from ahriman.core.formatters.string_printer import StringPrinter
|
||||||
from ahriman.models.property import Property
|
from ahriman.models.property import Property
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -17,4 +17,5 @@
|
|||||||
# 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.http.sync_ahriman_client import SyncAhrimanClient
|
||||||
from ahriman.core.http.sync_http_client import MultipartType, SyncHttpClient
|
from ahriman.core.http.sync_http_client import MultipartType, SyncHttpClient
|
||||||
|
|||||||
85
src/ahriman/core/http/sync_ahriman_client.py
Normal file
85
src/ahriman/core/http/sync_ahriman_client.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
import contextlib
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from functools import cached_property
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from ahriman import __version__
|
||||||
|
from ahriman.core.http.sync_http_client import SyncHttpClient
|
||||||
|
|
||||||
|
|
||||||
|
class SyncAhrimanClient(SyncHttpClient):
|
||||||
|
"""
|
||||||
|
wrapper for ahriman web service
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
address(str): address of the web service
|
||||||
|
"""
|
||||||
|
|
||||||
|
address: str
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def session(self) -> requests.Session:
|
||||||
|
"""
|
||||||
|
get or create session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
request.Session: created session object
|
||||||
|
"""
|
||||||
|
if urlparse(self.address).scheme == "http+unix":
|
||||||
|
import requests_unixsocket # type: ignore[import-untyped]
|
||||||
|
session: requests.Session = requests_unixsocket.Session()
|
||||||
|
session.headers["User-Agent"] = f"ahriman/{__version__}"
|
||||||
|
return session
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
|
session.headers["User-Agent"] = f"ahriman/{__version__}"
|
||||||
|
self._login(session)
|
||||||
|
|
||||||
|
return session
|
||||||
|
|
||||||
|
def _login(self, session: requests.Session) -> None:
|
||||||
|
"""
|
||||||
|
process login to the service
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session(requests.Session): request session to login
|
||||||
|
"""
|
||||||
|
if self.auth is None:
|
||||||
|
return # no auth configured
|
||||||
|
|
||||||
|
username, password = self.auth
|
||||||
|
payload = {
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
}
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
self.make_request("POST", self._login_url(), json=payload, session=session)
|
||||||
|
|
||||||
|
def _login_url(self) -> str:
|
||||||
|
"""
|
||||||
|
get url for the login api
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: full url for web service to log in
|
||||||
|
"""
|
||||||
|
return f"{self.address}/api/v1/login"
|
||||||
@ -72,7 +72,9 @@ class HttpLogHandler(logging.Handler):
|
|||||||
if (handler := next((handler for handler in root.handlers if isinstance(handler, cls)), None)) is not None:
|
if (handler := next((handler for handler in root.handlers if isinstance(handler, cls)), None)) is not None:
|
||||||
return handler # there is already registered instance
|
return handler # there is already registered instance
|
||||||
|
|
||||||
suppress_errors = configuration.getboolean("settings", "suppress_http_log_errors", fallback=False)
|
suppress_errors = configuration.getboolean( # read old-style first and then fallback to new style
|
||||||
|
"settings", "suppress_http_log_errors",
|
||||||
|
fallback=configuration.getboolean("status", "suppress_http_log_errors", fallback=False))
|
||||||
handler = cls(repository_id, configuration, report=report, suppress_errors=suppress_errors)
|
handler = cls(repository_id, configuration, report=report, suppress_errors=suppress_errors)
|
||||||
root.addHandler(handler)
|
root.addHandler(handler)
|
||||||
|
|
||||||
|
|||||||
@ -120,6 +120,6 @@ class Email(Report, JinjaTemplate):
|
|||||||
text = self.make_html(result, self.template)
|
text = self.make_html(result, self.template)
|
||||||
attachments = {}
|
attachments = {}
|
||||||
if self.template_full is not None:
|
if self.template_full is not None:
|
||||||
attachments["index.html"] = self.make_html(Result(success=packages), self.template_full)
|
attachments["index.html"] = self.make_html(Result(updated=packages), self.template_full)
|
||||||
|
|
||||||
self._send(text, attachments)
|
self._send(text, attachments)
|
||||||
|
|||||||
@ -58,5 +58,5 @@ class HTML(Report, JinjaTemplate):
|
|||||||
packages(list[Package]): list of packages to generate report
|
packages(list[Package]): list of packages to generate report
|
||||||
result(Result): build result
|
result(Result): build result
|
||||||
"""
|
"""
|
||||||
html = self.make_html(Result(success=packages), self.template)
|
html = self.make_html(Result(updated=packages), self.template)
|
||||||
self.report_path.write_text(html, encoding="utf8")
|
self.report_path.write_text(html, encoding="utf8")
|
||||||
|
|||||||
@ -81,7 +81,7 @@ class RemoteCall(Report):
|
|||||||
bool: True in case if remote process is alive and False otherwise
|
bool: True in case if remote process is alive and False otherwise
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
response = self.client.make_request("GET", f"/api/v1/service/process/{process_id}")
|
response = self.client.make_request("GET", f"{self.client.address}/api/v1/service/process/{process_id}")
|
||||||
except requests.HTTPError as ex:
|
except requests.HTTPError as ex:
|
||||||
status_code = ex.response.status_code if ex.response is not None else None
|
status_code = ex.response.status_code if ex.response is not None else None
|
||||||
if status_code == 404:
|
if status_code == 404:
|
||||||
@ -100,7 +100,7 @@ class RemoteCall(Report):
|
|||||||
Returns:
|
Returns:
|
||||||
str: remote process id
|
str: remote process id
|
||||||
"""
|
"""
|
||||||
response = self.client.make_request("POST", "/api/v1/service/update",
|
response = self.client.make_request("POST", f"{self.client.address}/api/v1/service/update",
|
||||||
params=self.repository_id.query(),
|
params=self.repository_id.query(),
|
||||||
json={
|
json={
|
||||||
"aur": self.update_aur,
|
"aur": self.update_aur,
|
||||||
|
|||||||
@ -25,45 +25,20 @@ from tempfile import TemporaryDirectory
|
|||||||
|
|
||||||
from ahriman.core.build_tools.task import Task
|
from ahriman.core.build_tools.task import Task
|
||||||
from ahriman.core.repository.cleaner import Cleaner
|
from ahriman.core.repository.cleaner import Cleaner
|
||||||
|
from ahriman.core.repository.package_info import PackageInfo
|
||||||
from ahriman.core.util import safe_filename
|
from ahriman.core.util import safe_filename
|
||||||
|
from ahriman.models.changes import Changes
|
||||||
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
|
||||||
from ahriman.models.packagers import Packagers
|
from ahriman.models.packagers import Packagers
|
||||||
from ahriman.models.result import Result
|
from ahriman.models.result import Result
|
||||||
|
|
||||||
|
|
||||||
class Executor(Cleaner):
|
class Executor(PackageInfo, Cleaner):
|
||||||
"""
|
"""
|
||||||
trait for common repository update processes
|
trait for common repository update processes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def load_archives(self, packages: Iterable[Path]) -> list[Package]:
|
|
||||||
"""
|
|
||||||
load packages from list of archives
|
|
||||||
|
|
||||||
Args:
|
|
||||||
packages(Iterable[Path]): paths to package archives
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[Package]: list of read packages
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
NotImplementedError: not implemented method
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def packages(self) -> list[Package]:
|
|
||||||
"""
|
|
||||||
generate list of repository packages
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[Package]: list of packages properties
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
NotImplementedError: not implemented method
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def process_build(self, updates: Iterable[Package], packagers: Packagers | None = None, *,
|
def process_build(self, updates: Iterable[Package], packagers: Packagers | None = None, *,
|
||||||
bump_pkgrel: bool = False) -> Result:
|
bump_pkgrel: bool = False) -> Result:
|
||||||
"""
|
"""
|
||||||
@ -78,16 +53,18 @@ class Executor(Cleaner):
|
|||||||
Returns:
|
Returns:
|
||||||
Result: build result
|
Result: build result
|
||||||
"""
|
"""
|
||||||
def build_single(package: Package, local_path: Path, packager_id: str | None) -> None:
|
def build_single(package: Package, local_path: Path, packager_id: str | None) -> str | None:
|
||||||
self.reporter.set_building(package.base)
|
self.reporter.set_building(package.base)
|
||||||
task = Task(package, self.configuration, self.architecture, self.paths)
|
task = Task(package, self.configuration, self.architecture, self.paths)
|
||||||
local_version = local_versions.get(package.base) if bump_pkgrel else None
|
local_version = local_versions.get(package.base) if bump_pkgrel else None
|
||||||
task.init(local_path, self.database, local_version)
|
commit_sha = task.init(local_path, self.database, local_version)
|
||||||
built = task.build(local_path, PACKAGER=packager_id)
|
built = task.build(local_path, PACKAGER=packager_id)
|
||||||
for src in built:
|
for src in built:
|
||||||
dst = self.paths.packages / src.name
|
dst = self.paths.packages / src.name
|
||||||
shutil.move(src, dst)
|
shutil.move(src, dst)
|
||||||
|
|
||||||
|
return commit_sha
|
||||||
|
|
||||||
packagers = packagers or Packagers()
|
packagers = packagers or Packagers()
|
||||||
local_versions = {package.base: package.version for package in self.packages()}
|
local_versions = {package.base: package.version for package in self.packages()}
|
||||||
|
|
||||||
@ -97,8 +74,10 @@ class Executor(Cleaner):
|
|||||||
TemporaryDirectory(ignore_cleanup_errors=True) as dir_name:
|
TemporaryDirectory(ignore_cleanup_errors=True) as dir_name:
|
||||||
try:
|
try:
|
||||||
packager = self.packager(packagers, single.base)
|
packager = self.packager(packagers, single.base)
|
||||||
build_single(single, Path(dir_name), packager.packager_id)
|
last_commit_sha = build_single(single, Path(dir_name), packager.packager_id)
|
||||||
result.add_success(single)
|
# clear changes and update commit hash
|
||||||
|
self.reporter.package_changes_set(single.base, Changes(last_commit_sha))
|
||||||
|
result.add_updated(single)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.reporter.set_failed(single.base)
|
self.reporter.set_failed(single.base)
|
||||||
result.add_failed(single)
|
result.add_failed(single)
|
||||||
@ -106,7 +85,7 @@ class Executor(Cleaner):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def process_remove(self, packages: Iterable[str]) -> Path:
|
def process_remove(self, packages: Iterable[str]) -> Result:
|
||||||
"""
|
"""
|
||||||
remove packages from list
|
remove packages from list
|
||||||
|
|
||||||
@ -114,7 +93,7 @@ class Executor(Cleaner):
|
|||||||
packages(Iterable[str]): list of package names or bases to remove
|
packages(Iterable[str]): list of package names or bases to remove
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Path: path to repository database
|
Result: remove result
|
||||||
"""
|
"""
|
||||||
def remove_base(package_base: str) -> None:
|
def remove_base(package_base: str) -> None:
|
||||||
try:
|
try:
|
||||||
@ -122,13 +101,14 @@ class Executor(Cleaner):
|
|||||||
self.database.build_queue_clear(package_base)
|
self.database.build_queue_clear(package_base)
|
||||||
self.database.patches_remove(package_base, [])
|
self.database.patches_remove(package_base, [])
|
||||||
self.database.logs_remove(package_base, None)
|
self.database.logs_remove(package_base, None)
|
||||||
|
self.database.changes_remove(package_base)
|
||||||
self.reporter.package_remove(package_base) # we only update status page in case of base removal
|
self.reporter.package_remove(package_base) # we only update status page in case of base removal
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.exception("could not remove base %s", package_base)
|
self.logger.exception("could not remove base %s", package_base)
|
||||||
|
|
||||||
def remove_package(package: str, fn: Path) -> None:
|
def remove_package(package: str, archive_path: Path) -> None:
|
||||||
try:
|
try:
|
||||||
self.repo.remove(package, fn) # remove the package itself
|
self.repo.remove(package, archive_path) # remove the package itself
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.exception("could not remove %s", package)
|
self.logger.exception("could not remove %s", package)
|
||||||
|
|
||||||
@ -136,6 +116,7 @@ class Executor(Cleaner):
|
|||||||
bases_to_remove: list[str] = []
|
bases_to_remove: list[str] = []
|
||||||
|
|
||||||
# build package list based on user input
|
# build package list based on user input
|
||||||
|
result = Result()
|
||||||
requested = set(packages)
|
requested = set(packages)
|
||||||
for local in self.packages():
|
for local in self.packages():
|
||||||
if local.base in packages or all(package in requested for package in local.packages):
|
if local.base in packages or all(package in requested for package in local.packages):
|
||||||
@ -145,6 +126,7 @@ class Executor(Cleaner):
|
|||||||
if properties.filepath is not None
|
if properties.filepath is not None
|
||||||
})
|
})
|
||||||
bases_to_remove.append(local.base)
|
bases_to_remove.append(local.base)
|
||||||
|
result.add_removed(local)
|
||||||
elif requested.intersection(local.packages.keys()):
|
elif requested.intersection(local.packages.keys()):
|
||||||
packages_to_remove.update({
|
packages_to_remove.update({
|
||||||
package: properties.filepath
|
package: properties.filepath
|
||||||
@ -167,7 +149,7 @@ class Executor(Cleaner):
|
|||||||
for package in bases_to_remove:
|
for package in bases_to_remove:
|
||||||
remove_base(package)
|
remove_base(package)
|
||||||
|
|
||||||
return self.repo.repo_path
|
return result
|
||||||
|
|
||||||
def process_update(self, packages: Iterable[Path], packagers: Packagers | None = None) -> Result:
|
def process_update(self, packages: Iterable[Path], packagers: Packagers | None = None) -> Result:
|
||||||
"""
|
"""
|
||||||
@ -219,7 +201,7 @@ class Executor(Cleaner):
|
|||||||
rename(description, local.base)
|
rename(description, local.base)
|
||||||
update_single(description.filename, local.base, packager.key)
|
update_single(description.filename, local.base, packager.key)
|
||||||
self.reporter.set_success(local)
|
self.reporter.set_success(local)
|
||||||
result.add_success(local)
|
result.add_updated(local)
|
||||||
|
|
||||||
current_package_archives: set[str] = set()
|
current_package_archives: set[str] = set()
|
||||||
if local.base in current_packages:
|
if local.base in current_packages:
|
||||||
|
|||||||
126
src/ahriman/core/repository/package_info.py
Normal file
126
src/ahriman/core/repository/package_info.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#
|
||||||
|
# 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 collections.abc import Iterable
|
||||||
|
from pathlib import Path
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
|
from ahriman.core.build_tools.sources import Sources
|
||||||
|
from ahriman.core.repository.repository_properties import RepositoryProperties
|
||||||
|
from ahriman.core.util import package_like
|
||||||
|
from ahriman.models.changes import Changes
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
|
||||||
|
|
||||||
|
class PackageInfo(RepositoryProperties):
|
||||||
|
"""
|
||||||
|
handler for the package information
|
||||||
|
"""
|
||||||
|
|
||||||
|
def load_archives(self, packages: Iterable[Path]) -> list[Package]:
|
||||||
|
"""
|
||||||
|
load packages from list of archives
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages(Iterable[Path]): paths to package archives
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Package]: list of read packages
|
||||||
|
"""
|
||||||
|
sources = self.database.remotes_get()
|
||||||
|
|
||||||
|
result: dict[str, Package] = {}
|
||||||
|
# we are iterating over bases, not single packages
|
||||||
|
for full_path in packages:
|
||||||
|
try:
|
||||||
|
local = Package.from_archive(full_path, self.pacman)
|
||||||
|
if (source := sources.get(local.base)) is not None:
|
||||||
|
local.remote = source
|
||||||
|
|
||||||
|
current = result.setdefault(local.base, local)
|
||||||
|
if current.version != local.version:
|
||||||
|
# force version to max of them
|
||||||
|
self.logger.warning("version of %s differs, found %s and %s",
|
||||||
|
current.base, current.version, local.version)
|
||||||
|
if current.is_outdated(local, self.paths, calculate_version=False):
|
||||||
|
current.version = local.version
|
||||||
|
current.packages.update(local.packages)
|
||||||
|
except Exception:
|
||||||
|
self.logger.exception("could not load package from %s", full_path)
|
||||||
|
return list(result.values())
|
||||||
|
|
||||||
|
def package_changes(self, package: Package, last_commit_sha: str | None) -> Changes:
|
||||||
|
"""
|
||||||
|
extract package change for the package since last commit if available
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package(Package): package properties
|
||||||
|
last_commit_sha(str | None): last known commit hash
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Changes: changes if available
|
||||||
|
"""
|
||||||
|
with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name:
|
||||||
|
dir_path = Path(dir_name)
|
||||||
|
current_commit_sha = Sources.load(dir_path, package, self.database.patches_get(package.base), self.paths)
|
||||||
|
|
||||||
|
changes: str | None = None
|
||||||
|
if current_commit_sha != last_commit_sha:
|
||||||
|
changes = Sources.changes(dir_path, last_commit_sha)
|
||||||
|
|
||||||
|
return Changes(last_commit_sha, changes)
|
||||||
|
|
||||||
|
def packages(self) -> list[Package]:
|
||||||
|
"""
|
||||||
|
generate list of repository packages
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Package]: list of packages properties
|
||||||
|
"""
|
||||||
|
return self.load_archives(filter(package_like, self.paths.repository.iterdir()))
|
||||||
|
|
||||||
|
def packages_built(self) -> list[Path]:
|
||||||
|
"""
|
||||||
|
get list of files in built packages directory
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Path]: list of filenames from the directory
|
||||||
|
"""
|
||||||
|
return list(filter(package_like, self.paths.packages.iterdir()))
|
||||||
|
|
||||||
|
def packages_depend_on(self, packages: list[Package], depends_on: Iterable[str] | None) -> list[Package]:
|
||||||
|
"""
|
||||||
|
extract list of packages which depends on specified package
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages(list[Package]): list of packages to be filtered
|
||||||
|
depends_on(Iterable[str] | None): dependencies of the packages
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Package]: list of repository packages which depend on specified packages
|
||||||
|
"""
|
||||||
|
if depends_on is None:
|
||||||
|
return packages # no list provided extract everything by default
|
||||||
|
depends_on = set(depends_on)
|
||||||
|
|
||||||
|
return [
|
||||||
|
package
|
||||||
|
for package in packages
|
||||||
|
if depends_on.intersection(package.full_depends(self.pacman, packages))
|
||||||
|
]
|
||||||
@ -17,8 +17,6 @@
|
|||||||
# 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.abc import Iterable
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Self
|
from typing import Self
|
||||||
|
|
||||||
from ahriman.core import _Context, context
|
from ahriman.core import _Context, context
|
||||||
@ -28,9 +26,7 @@ from ahriman.core.database import SQLite
|
|||||||
from ahriman.core.repository.executor import Executor
|
from ahriman.core.repository.executor import Executor
|
||||||
from ahriman.core.repository.update_handler import UpdateHandler
|
from ahriman.core.repository.update_handler import UpdateHandler
|
||||||
from ahriman.core.sign.gpg import GPG
|
from ahriman.core.sign.gpg import GPG
|
||||||
from ahriman.core.util import package_like
|
|
||||||
from ahriman.models.context_key import ContextKey
|
from ahriman.models.context_key import ContextKey
|
||||||
from ahriman.models.package import Package
|
|
||||||
from ahriman.models.pacman_synchronization import PacmanSynchronization
|
from ahriman.models.pacman_synchronization import PacmanSynchronization
|
||||||
from ahriman.models.repository_id import RepositoryId
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
@ -101,74 +97,3 @@ class Repository(Executor, UpdateHandler):
|
|||||||
ctx.set(ContextKey("repository", type(self)), self)
|
ctx.set(ContextKey("repository", type(self)), self)
|
||||||
|
|
||||||
context.set(ctx)
|
context.set(ctx)
|
||||||
|
|
||||||
def load_archives(self, packages: Iterable[Path]) -> list[Package]:
|
|
||||||
"""
|
|
||||||
load packages from list of archives
|
|
||||||
|
|
||||||
Args:
|
|
||||||
packages(Iterable[Path]): paths to package archives
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[Package]: list of read packages
|
|
||||||
"""
|
|
||||||
sources = self.database.remotes_get()
|
|
||||||
|
|
||||||
result: dict[str, Package] = {}
|
|
||||||
# we are iterating over bases, not single packages
|
|
||||||
for full_path in packages:
|
|
||||||
try:
|
|
||||||
local = Package.from_archive(full_path, self.pacman)
|
|
||||||
if (source := sources.get(local.base)) is not None:
|
|
||||||
local.remote = source
|
|
||||||
|
|
||||||
current = result.setdefault(local.base, local)
|
|
||||||
if current.version != local.version:
|
|
||||||
# force version to max of them
|
|
||||||
self.logger.warning("version of %s differs, found %s and %s",
|
|
||||||
current.base, current.version, local.version)
|
|
||||||
if current.is_outdated(local, self.paths, calculate_version=False):
|
|
||||||
current.version = local.version
|
|
||||||
current.packages.update(local.packages)
|
|
||||||
except Exception:
|
|
||||||
self.logger.exception("could not load package from %s", full_path)
|
|
||||||
return list(result.values())
|
|
||||||
|
|
||||||
def packages(self) -> list[Package]:
|
|
||||||
"""
|
|
||||||
generate list of repository packages
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[Package]: list of packages properties
|
|
||||||
"""
|
|
||||||
return self.load_archives(filter(package_like, self.paths.repository.iterdir()))
|
|
||||||
|
|
||||||
def packages_built(self) -> list[Path]:
|
|
||||||
"""
|
|
||||||
get list of files in built packages directory
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[Path]: list of filenames from the directory
|
|
||||||
"""
|
|
||||||
return list(filter(package_like, self.paths.packages.iterdir()))
|
|
||||||
|
|
||||||
def packages_depend_on(self, packages: list[Package], depends_on: Iterable[str] | None) -> list[Package]:
|
|
||||||
"""
|
|
||||||
extract list of packages which depends on specified package
|
|
||||||
|
|
||||||
Args:
|
|
||||||
packages(list[Package]): list of packages to be filtered
|
|
||||||
depends_on(Iterable[str] | None): dependencies of the packages
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[Package]: list of repository packages which depend on specified packages
|
|
||||||
"""
|
|
||||||
if depends_on is None:
|
|
||||||
return packages # no list provided extract everything by default
|
|
||||||
depends_on = set(depends_on)
|
|
||||||
|
|
||||||
return [
|
|
||||||
package
|
|
||||||
for package in packages
|
|
||||||
if depends_on.intersection(package.full_depends(self.pacman, packages))
|
|
||||||
]
|
|
||||||
|
|||||||
@ -22,28 +22,17 @@ from collections.abc import Iterable
|
|||||||
from ahriman.core.build_tools.sources import Sources
|
from ahriman.core.build_tools.sources import Sources
|
||||||
from ahriman.core.exceptions import UnknownPackageError
|
from ahriman.core.exceptions import UnknownPackageError
|
||||||
from ahriman.core.repository.cleaner import Cleaner
|
from ahriman.core.repository.cleaner import Cleaner
|
||||||
|
from ahriman.core.repository.package_info import PackageInfo
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.package_source import PackageSource
|
from ahriman.models.package_source import PackageSource
|
||||||
from ahriman.models.remote_source import RemoteSource
|
from ahriman.models.remote_source import RemoteSource
|
||||||
|
|
||||||
|
|
||||||
class UpdateHandler(Cleaner):
|
class UpdateHandler(PackageInfo, Cleaner):
|
||||||
"""
|
"""
|
||||||
trait to get package update list
|
trait to get package update list
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def packages(self) -> list[Package]:
|
|
||||||
"""
|
|
||||||
generate list of repository packages
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[Package]: list of packages properties
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
NotImplementedError: not implemented method
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def updates_aur(self, filter_packages: Iterable[str], *, vcs: bool) -> list[Package]:
|
def updates_aur(self, filter_packages: Iterable[str], *, vcs: bool) -> list[Package]:
|
||||||
"""
|
"""
|
||||||
check AUR for updates
|
check AUR for updates
|
||||||
|
|||||||
@ -174,7 +174,7 @@ class Spawn(Thread, LazyLogging):
|
|||||||
return self._spawn_process(repository_id, "service-key-import", key, **kwargs)
|
return self._spawn_process(repository_id, "service-key-import", key, **kwargs)
|
||||||
|
|
||||||
def packages_add(self, repository_id: RepositoryId, packages: Iterable[str], username: str | None, *,
|
def packages_add(self, repository_id: RepositoryId, packages: Iterable[str], username: str | None, *,
|
||||||
patches: list[PkgbuildPatch], now: bool) -> str:
|
patches: list[PkgbuildPatch], now: bool, increment: bool, refresh: bool) -> str:
|
||||||
"""
|
"""
|
||||||
add packages
|
add packages
|
||||||
|
|
||||||
@ -184,19 +184,26 @@ class Spawn(Thread, LazyLogging):
|
|||||||
username(str | None): optional override of username for build process
|
username(str | None): optional override of username for build process
|
||||||
patches(list[PkgbuildPatch]): list of patches to be passed
|
patches(list[PkgbuildPatch]): list of patches to be passed
|
||||||
now(bool): build packages now
|
now(bool): build packages now
|
||||||
|
increment(bool): increment pkgrel on conflict
|
||||||
|
refresh(bool): refresh pacman database before process
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: spawned process identifier
|
str: spawned process identifier
|
||||||
"""
|
"""
|
||||||
kwargs: dict[str, str | list[str] | None] = {"username": username}
|
kwargs: dict[str, str | list[str] | None] = {
|
||||||
|
"username": username,
|
||||||
|
"variable": [patch.serialize() for patch in patches],
|
||||||
|
self.boolean_action_argument("increment", increment): "",
|
||||||
|
}
|
||||||
if now:
|
if now:
|
||||||
kwargs["now"] = ""
|
kwargs["now"] = ""
|
||||||
if patches:
|
if refresh:
|
||||||
kwargs["variable"] = [patch.serialize() for patch in patches]
|
kwargs["refresh"] = ""
|
||||||
|
|
||||||
return self._spawn_process(repository_id, "package-add", *packages, **kwargs)
|
return self._spawn_process(repository_id, "package-add", *packages, **kwargs)
|
||||||
|
|
||||||
def packages_rebuild(self, repository_id: RepositoryId, depends_on: str, username: str | None) -> str:
|
def packages_rebuild(self, repository_id: RepositoryId, depends_on: str, username: str | None, *,
|
||||||
|
increment: bool) -> str:
|
||||||
"""
|
"""
|
||||||
rebuild packages which depend on the specified package
|
rebuild packages which depend on the specified package
|
||||||
|
|
||||||
@ -204,11 +211,16 @@ class Spawn(Thread, LazyLogging):
|
|||||||
repository_id(RepositoryId): repository unique identifier
|
repository_id(RepositoryId): repository unique identifier
|
||||||
depends_on(str): packages dependency
|
depends_on(str): packages dependency
|
||||||
username(str | None): optional override of username for build process
|
username(str | None): optional override of username for build process
|
||||||
|
increment(bool): increment pkgrel on conflict
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: spawned process identifier
|
str: spawned process identifier
|
||||||
"""
|
"""
|
||||||
kwargs = {"depends-on": depends_on, "username": username}
|
kwargs = {
|
||||||
|
"depends-on": depends_on,
|
||||||
|
"username": username,
|
||||||
|
self.boolean_action_argument("increment", increment): "",
|
||||||
|
}
|
||||||
return self._spawn_process(repository_id, "repo-rebuild", **kwargs)
|
return self._spawn_process(repository_id, "repo-rebuild", **kwargs)
|
||||||
|
|
||||||
def packages_remove(self, repository_id: RepositoryId, packages: Iterable[str]) -> str:
|
def packages_remove(self, repository_id: RepositoryId, packages: Iterable[str]) -> str:
|
||||||
@ -225,7 +237,7 @@ class Spawn(Thread, LazyLogging):
|
|||||||
return self._spawn_process(repository_id, "package-remove", *packages)
|
return self._spawn_process(repository_id, "package-remove", *packages)
|
||||||
|
|
||||||
def packages_update(self, repository_id: RepositoryId, username: str | None, *,
|
def packages_update(self, repository_id: RepositoryId, username: str | None, *,
|
||||||
aur: bool, local: bool, manual: bool) -> str:
|
aur: bool, local: bool, manual: bool, increment: bool, refresh: bool) -> str:
|
||||||
"""
|
"""
|
||||||
run full repository update
|
run full repository update
|
||||||
|
|
||||||
@ -235,6 +247,8 @@ class Spawn(Thread, LazyLogging):
|
|||||||
aur(bool): check for aur updates
|
aur(bool): check for aur updates
|
||||||
local(bool): check for local packages updates
|
local(bool): check for local packages updates
|
||||||
manual(bool): check for manual packages
|
manual(bool): check for manual packages
|
||||||
|
increment(bool): increment pkgrel on conflict
|
||||||
|
refresh(bool): refresh pacman database before process
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: spawned process identifier
|
str: spawned process identifier
|
||||||
@ -244,7 +258,11 @@ class Spawn(Thread, LazyLogging):
|
|||||||
self.boolean_action_argument("aur", aur): "",
|
self.boolean_action_argument("aur", aur): "",
|
||||||
self.boolean_action_argument("local", local): "",
|
self.boolean_action_argument("local", local): "",
|
||||||
self.boolean_action_argument("manual", manual): "",
|
self.boolean_action_argument("manual", manual): "",
|
||||||
|
self.boolean_action_argument("increment", increment): "",
|
||||||
}
|
}
|
||||||
|
if refresh:
|
||||||
|
kwargs["refresh"] = ""
|
||||||
|
|
||||||
return self._spawn_process(repository_id, "repo-update", **kwargs)
|
return self._spawn_process(repository_id, "repo-update", **kwargs)
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import logging
|
|||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||||
|
from ahriman.models.changes import Changes
|
||||||
from ahriman.models.internal_status import InternalStatus
|
from ahriman.models.internal_status import InternalStatus
|
||||||
from ahriman.models.log_record_id import LogRecordId
|
from ahriman.models.log_record_id import LogRecordId
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
@ -49,16 +50,19 @@ class Client:
|
|||||||
"""
|
"""
|
||||||
if not report:
|
if not report:
|
||||||
return Client()
|
return Client()
|
||||||
|
if not configuration.getboolean("status", "enabled", fallback=True): # global switch
|
||||||
|
return Client()
|
||||||
|
|
||||||
address = configuration.get("web", "address", fallback=None)
|
# new-style section
|
||||||
|
address = configuration.get("status", "address", fallback=None)
|
||||||
|
# old-style section
|
||||||
|
legacy_address = configuration.get("web", "address", fallback=None)
|
||||||
host = configuration.get("web", "host", fallback=None)
|
host = configuration.get("web", "host", fallback=None)
|
||||||
port = configuration.getint("web", "port", fallback=None)
|
port = configuration.getint("web", "port", fallback=None)
|
||||||
socket = configuration.get("web", "unix_socket", fallback=None)
|
socket = configuration.get("web", "unix_socket", fallback=None)
|
||||||
|
|
||||||
# basically we just check if there is something we can use for interaction with remote server
|
# basically we just check if there is something we can use for interaction with remote server
|
||||||
# at the moment (end of 2022) I think it would be much better idea to introduce flag like `enabled`,
|
if address or legacy_address or (host and port) or socket:
|
||||||
# but it will totally break used experience
|
|
||||||
if address or (host and port) or socket:
|
|
||||||
from ahriman.core.status.web_client import WebClient
|
from ahriman.core.status.web_client import WebClient
|
||||||
return WebClient(repository_id, configuration)
|
return WebClient(repository_id, configuration)
|
||||||
return Client()
|
return Client()
|
||||||
@ -72,6 +76,28 @@ class Client:
|
|||||||
status(BuildStatusEnum): current package build status
|
status(BuildStatusEnum): current package build status
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def package_changes_get(self, package_base: str) -> Changes:
|
||||||
|
"""
|
||||||
|
get package changes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str): package base to retrieve
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Changes: package changes if available and empty object otherwise
|
||||||
|
"""
|
||||||
|
del package_base
|
||||||
|
return Changes()
|
||||||
|
|
||||||
|
def package_changes_set(self, package_base: str, changes: Changes) -> None:
|
||||||
|
"""
|
||||||
|
update package changes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str): package base to update
|
||||||
|
changes(Changes): changes descriptor
|
||||||
|
"""
|
||||||
|
|
||||||
def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
|
def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
|
||||||
"""
|
"""
|
||||||
get package status
|
get package status
|
||||||
|
|||||||
@ -21,6 +21,7 @@ from ahriman.core.database import SQLite
|
|||||||
from ahriman.core.exceptions import UnknownPackageError
|
from ahriman.core.exceptions import UnknownPackageError
|
||||||
from ahriman.core.log import LazyLogging
|
from ahriman.core.log import LazyLogging
|
||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||||
|
from ahriman.models.changes import Changes
|
||||||
from ahriman.models.log_record_id import LogRecordId
|
from ahriman.models.log_record_id import LogRecordId
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
from ahriman.models.pkgbuild_patch import PkgbuildPatch
|
||||||
@ -113,6 +114,23 @@ class Watcher(LazyLogging):
|
|||||||
self._last_log_record_id = log_record_id
|
self._last_log_record_id = log_record_id
|
||||||
self.database.logs_insert(log_record_id, created, record, self.repository_id)
|
self.database.logs_insert(log_record_id, created, record, self.repository_id)
|
||||||
|
|
||||||
|
def package_changes_get(self, package_base: str) -> Changes:
|
||||||
|
"""
|
||||||
|
retrieve package changes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str): package base
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Changes: package changes if available
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
UnknownPackageError: if no package found
|
||||||
|
"""
|
||||||
|
if package_base not in self.known:
|
||||||
|
raise UnknownPackageError(package_base)
|
||||||
|
return self.database.changes_get(package_base, self.repository_id)
|
||||||
|
|
||||||
def package_get(self, package_base: str) -> tuple[Package, BuildStatus]:
|
def package_get(self, package_base: str) -> tuple[Package, BuildStatus]:
|
||||||
"""
|
"""
|
||||||
get current package base build status
|
get current package base build status
|
||||||
|
|||||||
@ -19,30 +19,26 @@
|
|||||||
#
|
#
|
||||||
import contextlib
|
import contextlib
|
||||||
import logging
|
import logging
|
||||||
import requests
|
|
||||||
|
|
||||||
from functools import cached_property
|
|
||||||
from urllib.parse import quote_plus as urlencode
|
from urllib.parse import quote_plus as urlencode
|
||||||
|
|
||||||
from ahriman import __version__
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.http import SyncHttpClient
|
from ahriman.core.http import SyncAhrimanClient
|
||||||
from ahriman.core.status.client import Client
|
from ahriman.core.status.client import Client
|
||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||||
|
from ahriman.models.changes import Changes
|
||||||
from ahriman.models.internal_status import InternalStatus
|
from ahriman.models.internal_status import InternalStatus
|
||||||
from ahriman.models.log_record_id import LogRecordId
|
from ahriman.models.log_record_id import LogRecordId
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
class WebClient(Client, SyncHttpClient):
|
class WebClient(Client, SyncAhrimanClient):
|
||||||
"""
|
"""
|
||||||
build status reporter web client
|
build status reporter web client
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
address(str): address of the web service
|
|
||||||
repository_id(RepositoryId): repository unique identifier
|
repository_id(RepositoryId): repository unique identifier
|
||||||
use_unix_socket(bool): use websocket or not
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, repository_id: RepositoryId, configuration: Configuration) -> None:
|
def __init__(self, repository_id: RepositoryId, configuration: Configuration) -> None:
|
||||||
@ -53,92 +49,52 @@ class WebClient(Client, SyncHttpClient):
|
|||||||
repository_id(RepositoryId): repository unique identifier
|
repository_id(RepositoryId): repository unique identifier
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
"""
|
"""
|
||||||
suppress_errors = configuration.getboolean("settings", "suppress_http_log_errors", fallback=False)
|
section, self.address = self.parse_address(configuration)
|
||||||
SyncHttpClient.__init__(self, configuration, "web", suppress_errors=suppress_errors)
|
suppress_errors = configuration.getboolean( # read old-style first and then fallback to new style
|
||||||
|
"settings", "suppress_http_log_errors",
|
||||||
|
fallback=configuration.getboolean("status", "suppress_http_log_errors", fallback=False))
|
||||||
|
SyncAhrimanClient.__init__(self, configuration, section, suppress_errors=suppress_errors)
|
||||||
|
|
||||||
self.repository_id = repository_id
|
self.repository_id = repository_id
|
||||||
self.address, self.use_unix_socket = self.parse_address(configuration)
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def session(self) -> requests.Session:
|
|
||||||
"""
|
|
||||||
get or create session
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
request.Session: created session object
|
|
||||||
"""
|
|
||||||
return self._create_session(use_unix_socket=self.use_unix_socket)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_address(configuration: Configuration) -> tuple[str, bool]:
|
def parse_address(configuration: Configuration) -> tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
parse address from configuration
|
parse address from legacy configuration
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple[str, bool]: tuple of server address and socket flag (True in case if unix socket must be used)
|
tuple[str, str]: tuple of section name and server address
|
||||||
"""
|
"""
|
||||||
|
# new-style section
|
||||||
|
if (address := configuration.get("status", "address", fallback=None)) is not None:
|
||||||
|
return "status", address
|
||||||
|
|
||||||
|
# legacy-style section
|
||||||
if (unix_socket := configuration.get("web", "unix_socket", fallback=None)) is not None:
|
if (unix_socket := configuration.get("web", "unix_socket", fallback=None)) is not None:
|
||||||
# special pseudo-protocol which is used for unix sockets
|
# special pseudo-protocol which is used for unix sockets
|
||||||
return f"http+unix://{urlencode(unix_socket)}", True
|
return "web", f"http+unix://{urlencode(unix_socket)}"
|
||||||
address = configuration.get("web", "address", fallback=None)
|
address = configuration.get("web", "address", fallback=None)
|
||||||
if not address:
|
if not address:
|
||||||
# build address from host and port directly
|
# build address from host and port directly
|
||||||
host = configuration.get("web", "host")
|
host = configuration.get("web", "host")
|
||||||
port = configuration.getint("web", "port")
|
port = configuration.getint("web", "port")
|
||||||
address = f"http://{host}:{port}"
|
address = f"http://{host}:{port}"
|
||||||
return address, False
|
return "web", address
|
||||||
|
|
||||||
def _create_session(self, *, use_unix_socket: bool) -> requests.Session:
|
def _changes_url(self, package_base: str) -> str:
|
||||||
"""
|
"""
|
||||||
generate new request session
|
get url for the changes api
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
use_unix_socket(bool): if set to True then unix socket session will be generated instead of native requests
|
package_base(str): package base
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
requests.Session: generated session object
|
str: full url for web service for logs
|
||||||
"""
|
"""
|
||||||
if use_unix_socket:
|
return f"{self.address}/api/v1/packages/{package_base}/changes"
|
||||||
import requests_unixsocket # type: ignore[import-untyped]
|
|
||||||
session: requests.Session = requests_unixsocket.Session()
|
|
||||||
session.headers["User-Agent"] = f"ahriman/{__version__}"
|
|
||||||
return session
|
|
||||||
|
|
||||||
session = requests.Session()
|
|
||||||
session.headers["User-Agent"] = f"ahriman/{__version__}"
|
|
||||||
self._login(session)
|
|
||||||
|
|
||||||
return session
|
|
||||||
|
|
||||||
def _login(self, session: requests.Session) -> None:
|
|
||||||
"""
|
|
||||||
process login to the service
|
|
||||||
|
|
||||||
Args:
|
|
||||||
session(requests.Session): request session to login
|
|
||||||
"""
|
|
||||||
if self.auth is None:
|
|
||||||
return # no auth configured
|
|
||||||
|
|
||||||
username, password = self.auth
|
|
||||||
payload = {
|
|
||||||
"username": username,
|
|
||||||
"password": password,
|
|
||||||
}
|
|
||||||
with contextlib.suppress(Exception):
|
|
||||||
self.make_request("POST", self._login_url(), json=payload, session=session)
|
|
||||||
|
|
||||||
def _login_url(self) -> str:
|
|
||||||
"""
|
|
||||||
get url for the login api
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: full url for web service to log in
|
|
||||||
"""
|
|
||||||
return f"{self.address}/api/v1/login"
|
|
||||||
|
|
||||||
def _logs_url(self, package_base: str) -> str:
|
def _logs_url(self, package_base: str) -> str:
|
||||||
"""
|
"""
|
||||||
@ -191,6 +147,37 @@ class WebClient(Client, SyncHttpClient):
|
|||||||
self.make_request("POST", self._package_url(package.base),
|
self.make_request("POST", self._package_url(package.base),
|
||||||
params=self.repository_id.query(), json=payload)
|
params=self.repository_id.query(), json=payload)
|
||||||
|
|
||||||
|
def package_changes_get(self, package_base: str) -> Changes:
|
||||||
|
"""
|
||||||
|
get package changes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str): package base to retrieve
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Changes: package changes if available and empty object otherwise
|
||||||
|
"""
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
response = self.make_request("GET", self._changes_url(package_base),
|
||||||
|
params=self.repository_id.query())
|
||||||
|
response_json = response.json()
|
||||||
|
|
||||||
|
return Changes.from_json(response_json)
|
||||||
|
|
||||||
|
return Changes()
|
||||||
|
|
||||||
|
def package_changes_set(self, package_base: str, changes: Changes) -> None:
|
||||||
|
"""
|
||||||
|
update package changes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str): package base to update
|
||||||
|
changes(Changes): changes descriptor
|
||||||
|
"""
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
|
self.make_request("POST", self._changes_url(package_base),
|
||||||
|
params=self.repository_id.query(), json=changes.view())
|
||||||
|
|
||||||
def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
|
def package_get(self, package_base: str | None) -> list[tuple[Package, BuildStatus]]:
|
||||||
"""
|
"""
|
||||||
get package status
|
get package status
|
||||||
|
|||||||
@ -148,6 +148,8 @@ class Tree:
|
|||||||
sorted(part, key=lambda leaf: leaf.package.base)
|
sorted(part, key=lambda leaf: leaf.package.base)
|
||||||
for part in partitions if part
|
for part in partitions if part
|
||||||
]
|
]
|
||||||
|
if not partitions: # nothing to balance
|
||||||
|
return partitions
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
min_part, max_part = minmax(partitions, key=len)
|
min_part, max_part = minmax(partitions, key=len)
|
||||||
|
|||||||
@ -65,6 +65,14 @@ class TriggerLoader(LazyLogging):
|
|||||||
self._on_stop_requested = False
|
self._on_stop_requested = False
|
||||||
self.triggers: list[Trigger] = []
|
self.triggers: list[Trigger] = []
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
"""
|
||||||
|
custom destructor object which calls on_stop in case if it was requested
|
||||||
|
"""
|
||||||
|
if not self._on_stop_requested:
|
||||||
|
return
|
||||||
|
self.on_stop()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, repository_id: RepositoryId, configuration: Configuration) -> Self:
|
def load(cls, repository_id: RepositoryId, configuration: Configuration) -> Self:
|
||||||
"""
|
"""
|
||||||
@ -257,11 +265,3 @@ class TriggerLoader(LazyLogging):
|
|||||||
for trigger in self.triggers:
|
for trigger in self.triggers:
|
||||||
with self.__execute_trigger(trigger):
|
with self.__execute_trigger(trigger):
|
||||||
trigger.on_stop()
|
trigger.on_stop()
|
||||||
|
|
||||||
def __del__(self) -> None:
|
|
||||||
"""
|
|
||||||
custom destructor object which calls on_stop in case if it was requested
|
|
||||||
"""
|
|
||||||
if not self._on_stop_requested:
|
|
||||||
return
|
|
||||||
self.on_stop()
|
|
||||||
|
|||||||
71
src/ahriman/models/changes.py
Normal file
71
src/ahriman/models/changes.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#
|
||||||
|
# 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 dataclasses import dataclass, fields
|
||||||
|
from typing import Any, Self
|
||||||
|
|
||||||
|
from ahriman.core.util import dataclass_view, filter_json
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Changes:
|
||||||
|
"""
|
||||||
|
package source files changes holder
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
last_commit_sha(str | None): last commit hash
|
||||||
|
changes(str | None): package change since the last commit if available
|
||||||
|
"""
|
||||||
|
|
||||||
|
last_commit_sha: str | None = None
|
||||||
|
changes: str | None = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_empty(self) -> bool:
|
||||||
|
"""
|
||||||
|
validate that changes are not empty
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` in case if changes are not set and ``False`` otherwise
|
||||||
|
"""
|
||||||
|
return self.changes is None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||||
|
"""
|
||||||
|
construct changes from the json dump
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dump(dict[str, Any]): json dump body
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Self: changes object
|
||||||
|
"""
|
||||||
|
# filter to only known fields
|
||||||
|
known_fields = [pair.name for pair in fields(cls)]
|
||||||
|
return cls(**filter_json(dump, known_fields))
|
||||||
|
|
||||||
|
def view(self) -> dict[str, Any]:
|
||||||
|
"""
|
||||||
|
generate json change view
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict[str, Any]: json-friendly dictionary
|
||||||
|
"""
|
||||||
|
return dataclass_view(self)
|
||||||
@ -19,28 +19,50 @@
|
|||||||
#
|
#
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable, Callable
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.exceptions import UnprocessedPackageStatusError
|
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
|
|
||||||
|
|
||||||
class Result:
|
class Result:
|
||||||
"""
|
"""
|
||||||
build result class holder
|
build result class holder
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
STATUS_PRIORITIES(list[str]): (class attribute) list of statues according to their priorities
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, success: Iterable[Package] | None = None, failed: Iterable[Package] | None = None) -> None:
|
STATUS_PRIORITIES = [
|
||||||
|
"failed",
|
||||||
|
"removed",
|
||||||
|
"updated",
|
||||||
|
"added",
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *, added: Iterable[Package] | None = None, updated: Iterable[Package] | None = None,
|
||||||
|
removed: Iterable[Package] | None = None, failed: Iterable[Package] | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
default constructor
|
default constructor
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
success(Iterable[Package] | None, optional): initial list of successes packages (Default value = None)
|
addded(Iterable[Package] | None, optional): initial list of successfully added packages
|
||||||
|
(Default value = None)
|
||||||
|
updated(Iterable[Package] | None, optional): initial list of successfully updated packages
|
||||||
|
(Default value = None)
|
||||||
|
removed(Iterable[Package] | None, optional): initial list of successfully removed packages
|
||||||
|
(Default value = None)
|
||||||
failed(Iterable[Package] | None, optional): initial list of failed packages (Default value = None)
|
failed(Iterable[Package] | None, optional): initial list of failed packages (Default value = None)
|
||||||
"""
|
"""
|
||||||
success = success or []
|
added = added or []
|
||||||
self._success = {package.base: package for package in success}
|
self._added = {package.base: package for package in added}
|
||||||
|
|
||||||
|
updated = updated or []
|
||||||
|
self._updated = {package.base: package for package in updated}
|
||||||
|
|
||||||
|
removed = removed or []
|
||||||
|
self._removed = {package.base: package for package in removed}
|
||||||
|
|
||||||
failed = failed or []
|
failed = failed or []
|
||||||
self._failed = {package.base: package for package in failed}
|
self._failed = {package.base: package for package in failed}
|
||||||
|
|
||||||
@ -62,7 +84,17 @@ class Result:
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True in case if success list is empty and False otherwise
|
bool: True in case if success list is empty and False otherwise
|
||||||
"""
|
"""
|
||||||
return not bool(self._success)
|
return not self._added and not self._updated
|
||||||
|
|
||||||
|
@property
|
||||||
|
def removed(self) -> list[Package]:
|
||||||
|
"""
|
||||||
|
get list of removed packages
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Package]: list of packages successfully removed
|
||||||
|
"""
|
||||||
|
return list(self._removed.values())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def success(self) -> list[Package]:
|
def success(self) -> list[Package]:
|
||||||
@ -72,7 +104,16 @@ class Result:
|
|||||||
Returns:
|
Returns:
|
||||||
list[Package]: list of packages with success result
|
list[Package]: list of packages with success result
|
||||||
"""
|
"""
|
||||||
return list(self._success.values())
|
return list(self._added.values()) + list(self._updated.values())
|
||||||
|
|
||||||
|
def add_added(self, package: Package) -> None:
|
||||||
|
"""
|
||||||
|
add new package to new packages list
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package(Package): package removed
|
||||||
|
"""
|
||||||
|
self._added[package.base] = package
|
||||||
|
|
||||||
def add_failed(self, package: Package) -> None:
|
def add_failed(self, package: Package) -> None:
|
||||||
"""
|
"""
|
||||||
@ -83,17 +124,26 @@ class Result:
|
|||||||
"""
|
"""
|
||||||
self._failed[package.base] = package
|
self._failed[package.base] = package
|
||||||
|
|
||||||
def add_success(self, package: Package) -> None:
|
def add_removed(self, package: Package) -> None:
|
||||||
|
"""
|
||||||
|
add new package to removed list
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package(Package): package removed
|
||||||
|
"""
|
||||||
|
self._removed[package.base] = package
|
||||||
|
|
||||||
|
def add_updated(self, package: Package) -> None:
|
||||||
"""
|
"""
|
||||||
add new package to success built
|
add new package to success built
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
package(Package): package built
|
package(Package): package built
|
||||||
"""
|
"""
|
||||||
self._success[package.base] = package
|
self._updated[package.base] = package
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
def merge(self, other: Result) -> Result:
|
def merge(self, other: Result) -> Self:
|
||||||
"""
|
"""
|
||||||
merge other result into this one. This method assumes that other has fresh info about status and override it
|
merge other result into this one. This method assumes that other has fresh info about status and override it
|
||||||
|
|
||||||
@ -101,19 +151,35 @@ class Result:
|
|||||||
other(Result): instance of the newest result
|
other(Result): instance of the newest result
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Result: updated instance
|
Self: updated instance
|
||||||
|
|
||||||
Raises:
|
|
||||||
UnprocessedPackageStatusError: if there is previously failed package which is masked as success
|
|
||||||
"""
|
"""
|
||||||
for base, package in other._failed.items():
|
for status in self.STATUS_PRIORITIES:
|
||||||
if base in self._success:
|
new_packages: Iterable[Package] = getattr(other, f"_{status}", {}).values()
|
||||||
del self._success[base]
|
insert_package: Callable[[Package], None] = getattr(self, f"add_{status}")
|
||||||
self.add_failed(package)
|
for package in new_packages:
|
||||||
for base, package in other._success.items():
|
insert_package(package)
|
||||||
if base in self._failed:
|
|
||||||
raise UnprocessedPackageStatusError(base)
|
return self.refine()
|
||||||
self.add_success(package)
|
|
||||||
|
def refine(self) -> Self:
|
||||||
|
"""
|
||||||
|
merge packages between different results (e.g. remove failed from added, etc.) removing duplicates
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Self: updated instance
|
||||||
|
"""
|
||||||
|
for index, base_status in enumerate(self.STATUS_PRIORITIES):
|
||||||
|
# extract top-level packages
|
||||||
|
base_packages: Iterable[str] = getattr(self, f"_{base_status}", {}).keys()
|
||||||
|
# extract packages for each bottom-level
|
||||||
|
for status in self.STATUS_PRIORITIES[index + 1:]:
|
||||||
|
packages: dict[str, Package] = getattr(self, f"_{status}", {})
|
||||||
|
|
||||||
|
# if there is top-level package in bottom-level, then remove it
|
||||||
|
for base in base_packages:
|
||||||
|
if base in packages:
|
||||||
|
del packages[base]
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# required for tests at least
|
# required for tests at least
|
||||||
@ -129,4 +195,7 @@ class Result:
|
|||||||
"""
|
"""
|
||||||
if not isinstance(other, Result):
|
if not isinstance(other, Result):
|
||||||
return False
|
return False
|
||||||
return self.success == other.success and self.failed == other.failed
|
return self._added == other._added \
|
||||||
|
and self._removed == other._removed \
|
||||||
|
and self._updated == other._updated \
|
||||||
|
and self._failed == other._failed
|
||||||
|
|||||||
41
src/ahriman/models/worker.py
Normal file
41
src/ahriman/models/worker.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#
|
||||||
|
# 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 dataclasses import dataclass, field
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Worker:
|
||||||
|
"""
|
||||||
|
worker descriptor
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
address(str): worker address to be reachable outside
|
||||||
|
identifier(str): worker unique identifier. If none set it will be automatically generated from the address
|
||||||
|
"""
|
||||||
|
|
||||||
|
address: str
|
||||||
|
identifier: str = field(default="", kw_only=True)
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
"""
|
||||||
|
update identifier based on settings
|
||||||
|
"""
|
||||||
|
object.__setattr__(self, "identifier", self.identifier or urlparse(self.address).netloc)
|
||||||
@ -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/>.
|
||||||
#
|
#
|
||||||
import aiohttp_security # type: ignore[import-untyped]
|
import aiohttp_security
|
||||||
import socket
|
import socket
|
||||||
import types
|
import types
|
||||||
|
|
||||||
@ -25,6 +25,7 @@ from aiohttp.web import Application, Request, StaticResource, StreamResponse, mi
|
|||||||
from aiohttp_session import setup as setup_session
|
from aiohttp_session import setup as setup_session
|
||||||
from aiohttp_session.cookie_storage import EncryptedCookieStorage
|
from aiohttp_session.cookie_storage import EncryptedCookieStorage
|
||||||
from cryptography import fernet
|
from cryptography import fernet
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
from ahriman.core.auth import Auth
|
from ahriman.core.auth import Auth
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
@ -50,6 +51,7 @@ class _AuthorizationPolicy(aiohttp_security.AbstractAuthorizationPolicy):
|
|||||||
Args:
|
Args:
|
||||||
validator(Auth): authorization module instance
|
validator(Auth): authorization module instance
|
||||||
"""
|
"""
|
||||||
|
aiohttp_security.AbstractAuthorizationPolicy.__init__(self)
|
||||||
self.validator = validator
|
self.validator = validator
|
||||||
|
|
||||||
async def authorized_userid(self, identity: str) -> str | None:
|
async def authorized_userid(self, identity: str) -> str | None:
|
||||||
@ -64,18 +66,21 @@ class _AuthorizationPolicy(aiohttp_security.AbstractAuthorizationPolicy):
|
|||||||
"""
|
"""
|
||||||
return identity if await self.validator.known_username(identity) else None
|
return identity if await self.validator.known_username(identity) else None
|
||||||
|
|
||||||
async def permits(self, identity: str, permission: UserAccess, context: str | None = None) -> bool:
|
async def permits(self, identity: str | None, permission: str | Enum, context: str | None = None) -> bool:
|
||||||
"""
|
"""
|
||||||
check user permissions
|
check user permissions
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
identity(str): username
|
identity(str | None): username
|
||||||
permission(UserAccess): requested permission level
|
permission(str | Enum): requested permission level
|
||||||
context(str | None, optional): URI request path (Default value = None)
|
context(str | None, optional): URI request path (Default value = None)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True in case if user is allowed to perform this request and False otherwise
|
bool: True in case if user is allowed to perform this request and False otherwise
|
||||||
"""
|
"""
|
||||||
|
# some methods for type checking and parent class compatibility
|
||||||
|
if identity is None or not isinstance(permission, UserAccess):
|
||||||
|
return False # no identity provided or invalid access rights requested
|
||||||
return await self.validator.verify_access(identity, permission, context)
|
return await self.validator.verify_access(identity, permission, context)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,8 @@
|
|||||||
#
|
#
|
||||||
from ahriman.web.schemas.aur_package_schema import AURPackageSchema
|
from ahriman.web.schemas.aur_package_schema import AURPackageSchema
|
||||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||||
|
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema
|
||||||
|
from ahriman.web.schemas.changes_schema import ChangesSchema
|
||||||
from ahriman.web.schemas.counters_schema import CountersSchema
|
from ahriman.web.schemas.counters_schema import CountersSchema
|
||||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||||
from ahriman.web.schemas.file_schema import FileSchema
|
from ahriman.web.schemas.file_schema import FileSchema
|
||||||
|
|||||||
36
src/ahriman/web/schemas/build_options_schema.py
Normal file
36
src/ahriman/web/schemas/build_options_schema.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#
|
||||||
|
# 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 marshmallow import Schema, fields
|
||||||
|
|
||||||
|
|
||||||
|
class BuildOptionsSchema(Schema):
|
||||||
|
"""
|
||||||
|
request build options schema
|
||||||
|
"""
|
||||||
|
|
||||||
|
increment = fields.Boolean(dump_default=True, metadata={
|
||||||
|
"description": "Increment pkgrel on conflicts",
|
||||||
|
})
|
||||||
|
packager = fields.String(metadata={
|
||||||
|
"description": "Packager identity if applicable",
|
||||||
|
})
|
||||||
|
refresh = fields.Boolean(dump_default=True, metadata={
|
||||||
|
"description": "Refresh pacman database"
|
||||||
|
})
|
||||||
34
src/ahriman/web/schemas/changes_schema.py
Normal file
34
src/ahriman/web/schemas/changes_schema.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#
|
||||||
|
# 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 marshmallow import Schema, fields
|
||||||
|
|
||||||
|
|
||||||
|
class ChangesSchema(Schema):
|
||||||
|
"""
|
||||||
|
response package changes schema
|
||||||
|
"""
|
||||||
|
|
||||||
|
last_commit_sha = fields.String(metadata={
|
||||||
|
"description": "Last recorded commit hash",
|
||||||
|
"example": "f1875edca1eb8fc0e55c41d1cae5fa05b6b7c6",
|
||||||
|
})
|
||||||
|
changes = fields.String(metadata={
|
||||||
|
"description": "Package changes in patch format",
|
||||||
|
})
|
||||||
@ -17,10 +17,12 @@
|
|||||||
# 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 marshmallow import Schema, fields
|
from marshmallow import fields
|
||||||
|
|
||||||
|
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema
|
||||||
|
|
||||||
|
|
||||||
class PackageNamesSchema(Schema):
|
class PackageNamesSchema(BuildOptionsSchema):
|
||||||
"""
|
"""
|
||||||
request package names schema
|
request package names schema
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -17,20 +17,22 @@
|
|||||||
# 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 marshmallow import Schema, fields
|
from marshmallow import fields
|
||||||
|
|
||||||
|
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema
|
||||||
|
|
||||||
|
|
||||||
class UpdateFlagsSchema(Schema):
|
class UpdateFlagsSchema(BuildOptionsSchema):
|
||||||
"""
|
"""
|
||||||
update flags request schema
|
update flags request schema
|
||||||
"""
|
"""
|
||||||
|
|
||||||
aur = fields.Bool(dump_default=True, metadata={
|
aur = fields.Boolean(dump_default=True, metadata={
|
||||||
"description": "Check AUR for updates",
|
"description": "Check AUR for updates",
|
||||||
})
|
})
|
||||||
local = fields.Bool(dump_default=True, metadata={
|
local = fields.Boolean(dump_default=True, metadata={
|
||||||
"description": "Check local packages for updates",
|
"description": "Check local packages for updates",
|
||||||
})
|
})
|
||||||
manual = fields.Bool(dump_default=True, metadata={
|
manual = fields.Boolean(dump_default=True, metadata={
|
||||||
"description": "Check manually built packages",
|
"description": "Check manually built packages",
|
||||||
})
|
})
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
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 aiohttp.web import HTTPBadRequest, HTTPNotFound, Request, StreamResponse, View
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable
|
||||||
from typing import Any, TypeVar
|
from typing import TypeVar
|
||||||
|
|
||||||
from ahriman.core.auth import Auth
|
from ahriman.core.auth import Auth
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
@ -138,49 +138,8 @@ class BaseView(View, CorsViewMixin):
|
|||||||
raise KeyError(f"Key {key} is missing or empty") from None
|
raise KeyError(f"Key {key} is missing or empty") from None
|
||||||
return value
|
return value
|
||||||
|
|
||||||
async def data_as_json(self, list_keys: list[str]) -> dict[str, Any]:
|
|
||||||
"""
|
|
||||||
extract form data and convert it to json object
|
|
||||||
|
|
||||||
Args:
|
|
||||||
list_keys(list[str]): list of keys which must be forced to list from form data
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict[str, Any]: form data converted to json. In case if a key is found multiple times
|
|
||||||
it will be returned as list
|
|
||||||
"""
|
|
||||||
raw = await self.request.post()
|
|
||||||
json: dict[str, Any] = {}
|
|
||||||
for key, value in raw.items():
|
|
||||||
if key in json and isinstance(json[key], list):
|
|
||||||
json[key].append(value)
|
|
||||||
elif key in json:
|
|
||||||
json[key] = [json[key], value]
|
|
||||||
elif key in list_keys:
|
|
||||||
json[key] = [value]
|
|
||||||
else:
|
|
||||||
json[key] = value
|
|
||||||
return json
|
|
||||||
|
|
||||||
async def extract_data(self, list_keys: list[str] | None = None) -> dict[str, Any]:
|
|
||||||
"""
|
|
||||||
extract json data from either json or form data
|
|
||||||
|
|
||||||
Args:
|
|
||||||
list_keys(list[str] | None, optional): optional list of keys which must be forced to list from form data
|
|
||||||
(Default value = None)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict[str, Any]: raw json object or form data converted to json
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
json: dict[str, Any] = await self.request.json()
|
|
||||||
return json
|
|
||||||
except ValueError:
|
|
||||||
return await self.data_as_json(list_keys or [])
|
|
||||||
|
|
||||||
# pylint: disable=not-callable,protected-access
|
# pylint: disable=not-callable,protected-access
|
||||||
async def head(self) -> StreamResponse: # type: ignore[return]
|
async def head(self) -> StreamResponse:
|
||||||
"""
|
"""
|
||||||
HEAD method implementation based on the result of GET method
|
HEAD method implementation based on the result of GET method
|
||||||
|
|
||||||
@ -263,6 +222,13 @@ class BaseView(View, CorsViewMixin):
|
|||||||
Returns:
|
Returns:
|
||||||
str | None: authorized username if any and None otherwise (e.g. if authorization is disabled)
|
str | None: authorized username if any and None otherwise (e.g. if authorization is disabled)
|
||||||
"""
|
"""
|
||||||
|
try: # try to read from payload
|
||||||
|
data: dict[str, str] = await self.request.json() # technically it is not, but we only need str here
|
||||||
|
if (packager := data.get("packager")) is not None:
|
||||||
|
return packager
|
||||||
|
except Exception:
|
||||||
|
self.request.app.logger.exception("could not extract json data for packager")
|
||||||
|
|
||||||
policy = self.request.app.get("identity")
|
policy = self.request.app.get("identity")
|
||||||
if policy is not None:
|
if policy is not None:
|
||||||
identity: str = await policy.identify(self.request)
|
identity: str = await policy.identify(self.request)
|
||||||
|
|||||||
@ -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 aiohttp.web import HTTPFound
|
from aiohttp.web import HTTPFound, HTTPNotFound
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.base import BaseView
|
from ahriman.web.views.base import BaseView
|
||||||
@ -40,5 +40,8 @@ class StaticView(BaseView):
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
HTTPFound: on success response
|
HTTPFound: on success response
|
||||||
|
HTTPNotFound: if path is invalid or unknown
|
||||||
"""
|
"""
|
||||||
raise HTTPFound(f"/static{self.request.path}")
|
if self.request.path in self.ROUTES: # explicit validation
|
||||||
|
raise HTTPFound(f"/static{self.request.path}")
|
||||||
|
raise HTTPNotFound
|
||||||
|
|||||||
@ -66,7 +66,7 @@ class AddView(BaseView):
|
|||||||
HTTPBadRequest: if bad data is supplied
|
HTTPBadRequest: if bad data is supplied
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
data = await self.extract_data(["packages", "patches"])
|
data = await self.request.json()
|
||||||
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
|
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
|
||||||
patches = [PkgbuildPatch(patch["key"], patch.get("value", "")) for patch in data.get("patches", [])]
|
patches = [PkgbuildPatch(patch["key"], patch.get("value", "")) for patch in data.get("patches", [])]
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -74,6 +74,14 @@ class AddView(BaseView):
|
|||||||
|
|
||||||
repository_id = self.repository_id()
|
repository_id = self.repository_id()
|
||||||
username = await self.username()
|
username = await self.username()
|
||||||
process_id = self.spawner.packages_add(repository_id, packages, username, patches=patches, now=True)
|
process_id = self.spawner.packages_add(
|
||||||
|
repository_id,
|
||||||
|
packages,
|
||||||
|
username,
|
||||||
|
patches=patches,
|
||||||
|
now=True,
|
||||||
|
increment=data.get("increment", True),
|
||||||
|
refresh=data.get("refresh", False),
|
||||||
|
)
|
||||||
|
|
||||||
return json_response({"process_id": process_id})
|
return json_response({"process_id": process_id})
|
||||||
|
|||||||
@ -104,9 +104,8 @@ class PGPView(BaseView):
|
|||||||
Raises:
|
Raises:
|
||||||
HTTPBadRequest: if bad data is supplied
|
HTTPBadRequest: if bad data is supplied
|
||||||
"""
|
"""
|
||||||
data = await self.extract_data()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
data = await self.request.json()
|
||||||
key = self.get_non_empty(data.get, "key")
|
key = self.get_non_empty(data.get, "key")
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
raise HTTPBadRequest(reason=str(ex))
|
raise HTTPBadRequest(reason=str(ex))
|
||||||
|
|||||||
@ -65,7 +65,7 @@ class RebuildView(BaseView):
|
|||||||
HTTPBadRequest: if bad data is supplied
|
HTTPBadRequest: if bad data is supplied
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
data = await self.extract_data(["packages"])
|
data = await self.request.json()
|
||||||
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
|
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
|
||||||
depends_on = next(iter(packages))
|
depends_on = next(iter(packages))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -73,6 +73,11 @@ class RebuildView(BaseView):
|
|||||||
|
|
||||||
repository_id = self.repository_id()
|
repository_id = self.repository_id()
|
||||||
username = await self.username()
|
username = await self.username()
|
||||||
process_id = self.spawner.packages_rebuild(repository_id, depends_on, username)
|
process_id = self.spawner.packages_rebuild(
|
||||||
|
repository_id,
|
||||||
|
depends_on,
|
||||||
|
username,
|
||||||
|
increment=data.get("increment", True),
|
||||||
|
)
|
||||||
|
|
||||||
return json_response({"process_id": process_id})
|
return json_response({"process_id": process_id})
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user