mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-01-13 17:53:42 +00:00
Compare commits
39 Commits
2.18.3
...
49cf91ea52
| Author | SHA1 | Date | |
|---|---|---|---|
| 49cf91ea52 | |||
| 4a8430dc67 | |||
| 46af782db2 | |||
| 6443e02352 | |||
| 999ad39d6f | |||
| dfab5f56b2 | |||
| 10798b9ba3 | |||
| 358e3dc4d2 | |||
| c13cd029bc | |||
| ae32cc8fbb | |||
| dff5b775a9 | |||
| db3f20546e | |||
| 53368468a4 | |||
| 228c2cce51 | |||
| f5aec4e5c1 | |||
| 9217c8c759 | |||
| 6392520e06 | |||
| c6306631e6 | |||
| 97b906c536 | |||
| 435375721d | |||
| 4c5caba6b7 | |||
| b83df9d2c5 | |||
| f2ea76aab9 | |||
| 471b1c1331 | |||
| bd770aac2f | |||
| 6abe35ef8c | |||
| fdc27a9ebf | |||
| b729096a25 | |||
| 390b9da29e | |||
| 256376df85 | |||
| 939a94d889 | |||
| 2b1b17a1a3 | |||
| 9e6705056a | |||
| b3a3a81f70 | |||
| 3e5dbbd6cd | |||
| f41e44895d | |||
| 765bbf486f | |||
| a3c54afb82 | |||
| 7f223ecc0a |
1
.github/workflows/docker.yml
vendored
1
.github/workflows/docker.yml
vendored
@ -7,6 +7,7 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
- '!*rc*'
|
- '!*rc*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@ -13,7 +13,15 @@ jobs:
|
|||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
container:
|
||||||
|
image: archlinux:base
|
||||||
|
options: -w /build
|
||||||
|
volumes:
|
||||||
|
- ${{ github.workspace }}:/build
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- run: pacman --noconfirm -Syu base-devel git python-tox
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Extract version
|
- name: Extract version
|
||||||
@ -27,10 +35,6 @@ jobs:
|
|||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
filter: 'Release \d+\.\d+\.\d+'
|
filter: 'Release \d+\.\d+\.\d+'
|
||||||
|
|
||||||
- uses: ConorMacBride/install-package@v1.1.0
|
|
||||||
with:
|
|
||||||
apt: tox
|
|
||||||
|
|
||||||
- name: Create archive
|
- name: Create archive
|
||||||
run: tox -e archive
|
run: tox -e archive
|
||||||
env:
|
env:
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
[tool.pylint.main]
|
[tool.pylint.main]
|
||||||
init-hook = "sys.path.append('pylint_plugins')"
|
init-hook = "sys.path.append('tools')"
|
||||||
load-plugins = [
|
load-plugins = [
|
||||||
"pylint.extensions.docparams",
|
"pylint.extensions.docparams",
|
||||||
"pylint.extensions.bad_builtin",
|
"pylint.extensions.bad_builtin",
|
||||||
"definition_order",
|
"pylint_plugins.definition_order",
|
||||||
"import_order",
|
"pylint_plugins.import_order",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.pylint.classes]
|
[tool.pylint.classes]
|
||||||
|
|||||||
5
.pytest.ini
Normal file
5
.pytest.ini
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[pytest]
|
||||||
|
addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec
|
||||||
|
asyncio_default_fixture_loop_scope = function
|
||||||
|
asyncio_mode = auto
|
||||||
|
spec_test_format = {result} {docstring_summary}
|
||||||
9
docs/_static/architecture.dot
vendored
9
docs/_static/architecture.dot
vendored
@ -64,7 +64,7 @@ digraph G {
|
|||||||
ahriman_core_alpm_remote_aur [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nalpm\.\nremote\.\naur",shape="box"];
|
ahriman_core_alpm_remote_aur [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nalpm\.\nremote\.\naur",shape="box"];
|
||||||
ahriman_core_alpm_remote_official [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nalpm\.\nremote\.\nofficial",shape="box"];
|
ahriman_core_alpm_remote_official [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nalpm\.\nremote\.\nofficial",shape="box"];
|
||||||
ahriman_core_alpm_remote_official_syncdb [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nalpm\.\nremote\.\nofficial_syncdb",shape="box"];
|
ahriman_core_alpm_remote_official_syncdb [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nalpm\.\nremote\.\nofficial_syncdb",shape="box"];
|
||||||
ahriman_core_alpm_remote_remote [fillcolor="#ae441e",fontcolor="#ffffff",label="ahriman\.\ncore\.\nalpm\.\nremote\.\nremote"];
|
ahriman_core_alpm_remote_remote [fillcolor="#a5401d",fontcolor="#ffffff",label="ahriman\.\ncore\.\nalpm\.\nremote\.\nremote"];
|
||||||
ahriman_core_alpm_repo [fillcolor="#994d33",fontcolor="#ffffff",label="ahriman\.\ncore\.\nalpm\.\nrepo"];
|
ahriman_core_alpm_repo [fillcolor="#994d33",fontcolor="#ffffff",label="ahriman\.\ncore\.\nalpm\.\nrepo"];
|
||||||
ahriman_core_auth [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nauth",shape="box"];
|
ahriman_core_auth [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nauth",shape="box"];
|
||||||
ahriman_core_auth_auth [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nauth\.\nauth",shape="box"];
|
ahriman_core_auth_auth [fillcolor="blue",fontcolor="white",label="ahriman\.\ncore\.\nauth\.\nauth",shape="box"];
|
||||||
@ -509,9 +509,9 @@ digraph G {
|
|||||||
ahriman_core_alpm_remote_official -> ahriman_core_alpm_remote [fillcolor="blue",minlen="0",weight="4"];
|
ahriman_core_alpm_remote_official -> ahriman_core_alpm_remote [fillcolor="blue",minlen="0",weight="4"];
|
||||||
ahriman_core_alpm_remote_official -> ahriman_core_alpm_remote_official_syncdb [fillcolor="blue",minlen="0",weight="4"];
|
ahriman_core_alpm_remote_official -> ahriman_core_alpm_remote_official_syncdb [fillcolor="blue",minlen="0",weight="4"];
|
||||||
ahriman_core_alpm_remote_official_syncdb -> ahriman_core_alpm_remote [fillcolor="blue",minlen="0",weight="4"];
|
ahriman_core_alpm_remote_official_syncdb -> ahriman_core_alpm_remote [fillcolor="blue",minlen="0",weight="4"];
|
||||||
ahriman_core_alpm_remote_remote -> ahriman_core_alpm_remote [fillcolor="#ae441e",minlen="0",weight="4"];
|
ahriman_core_alpm_remote_remote -> ahriman_core_alpm_remote [fillcolor="#a5401d",minlen="0",weight="4"];
|
||||||
ahriman_core_alpm_remote_remote -> ahriman_core_alpm_remote_aur [fillcolor="#ae441e",minlen="0",weight="4"];
|
ahriman_core_alpm_remote_remote -> ahriman_core_alpm_remote_aur [fillcolor="#a5401d",minlen="0",weight="4"];
|
||||||
ahriman_core_alpm_remote_remote -> ahriman_core_alpm_remote_official [fillcolor="#ae441e",minlen="0",weight="4"];
|
ahriman_core_alpm_remote_remote -> ahriman_core_alpm_remote_official [fillcolor="#a5401d",minlen="0",weight="4"];
|
||||||
ahriman_core_alpm_repo -> ahriman_core_repository_repository_properties [fillcolor="#994d33",minlen="2",weight="2"];
|
ahriman_core_alpm_repo -> ahriman_core_repository_repository_properties [fillcolor="#994d33",minlen="2",weight="2"];
|
||||||
ahriman_core_auth -> ahriman_web_keys [fillcolor="blue",minlen="2"];
|
ahriman_core_auth -> ahriman_web_keys [fillcolor="blue",minlen="2"];
|
||||||
ahriman_core_auth -> ahriman_web_middlewares_auth_handler [fillcolor="blue",minlen="3"];
|
ahriman_core_auth -> ahriman_web_middlewares_auth_handler [fillcolor="blue",minlen="3"];
|
||||||
@ -710,6 +710,7 @@ digraph G {
|
|||||||
ahriman_core_exceptions -> ahriman_core_alpm_remote_aur [fillcolor="#ef4306",minlen="2",weight="2"];
|
ahriman_core_exceptions -> ahriman_core_alpm_remote_aur [fillcolor="#ef4306",minlen="2",weight="2"];
|
||||||
ahriman_core_exceptions -> ahriman_core_alpm_remote_official [fillcolor="#ef4306",minlen="2",weight="2"];
|
ahriman_core_exceptions -> ahriman_core_alpm_remote_official [fillcolor="#ef4306",minlen="2",weight="2"];
|
||||||
ahriman_core_exceptions -> ahriman_core_alpm_remote_official_syncdb [fillcolor="#ef4306",minlen="2",weight="2"];
|
ahriman_core_exceptions -> ahriman_core_alpm_remote_official_syncdb [fillcolor="#ef4306",minlen="2",weight="2"];
|
||||||
|
ahriman_core_exceptions -> ahriman_core_alpm_remote_remote [fillcolor="#ef4306",minlen="2",weight="2"];
|
||||||
ahriman_core_exceptions -> ahriman_core_alpm_repo [fillcolor="#ef4306",minlen="2",weight="2"];
|
ahriman_core_exceptions -> ahriman_core_alpm_repo [fillcolor="#ef4306",minlen="2",weight="2"];
|
||||||
ahriman_core_exceptions -> ahriman_core_auth_oauth [fillcolor="#ef4306",minlen="2",weight="2"];
|
ahriman_core_exceptions -> ahriman_core_auth_oauth [fillcolor="#ef4306",minlen="2",weight="2"];
|
||||||
ahriman_core_exceptions -> ahriman_core_auth_pam [fillcolor="#ef4306",minlen="2",weight="2"];
|
ahriman_core_exceptions -> ahriman_core_auth_pam [fillcolor="#ef4306",minlen="2",weight="2"];
|
||||||
|
|||||||
@ -100,6 +100,14 @@ ahriman.application.handlers.rebuild module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.application.handlers.reload module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.application.handlers.reload
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.application.handlers.remove module
|
ahriman.application.handlers.remove module
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
|
|||||||
21
docs/ahriman.core.housekeeping.rst
Normal file
21
docs/ahriman.core.housekeeping.rst
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
ahriman.core.housekeeping package
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
ahriman.core.housekeeping.logs\_rotation\_trigger module
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.housekeeping.logs_rotation_trigger
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.housekeeping
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@ -15,6 +15,7 @@ Subpackages
|
|||||||
ahriman.core.distributed
|
ahriman.core.distributed
|
||||||
ahriman.core.formatters
|
ahriman.core.formatters
|
||||||
ahriman.core.gitremote
|
ahriman.core.gitremote
|
||||||
|
ahriman.core.housekeeping
|
||||||
ahriman.core.http
|
ahriman.core.http
|
||||||
ahriman.core.log
|
ahriman.core.log
|
||||||
ahriman.core.report
|
ahriman.core.report
|
||||||
|
|||||||
@ -44,6 +44,14 @@ ahriman.web.schemas.changes\_schema module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.schemas.configuration\_schema module
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.schemas.configuration_schema
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.schemas.counters\_schema module
|
ahriman.web.schemas.counters\_schema module
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
@ -140,6 +148,14 @@ ahriman.web.schemas.logs\_schema module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.schemas.logs\_search\_schema module
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.schemas.logs_search_schema
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.schemas.oauth2\_schema module
|
ahriman.web.schemas.oauth2\_schema module
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,14 @@ ahriman.web.views.v1.service.add module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.config module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.config
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.views.v1.service.logs module
|
ahriman.web.views.v1.service.logs module
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,7 @@ This package contains everything required for the most of application actions an
|
|||||||
* ``ahriman.core.distributed`` package with triggers and helpers for distributed build system.
|
* ``ahriman.core.distributed`` package with triggers and helpers for distributed build system.
|
||||||
* ``ahriman.core.formatters`` package provides ``Printer`` sub-classes for printing data (e.g. package properties) to stdout which are used by some handlers.
|
* ``ahriman.core.formatters`` package provides ``Printer`` sub-classes for printing data (e.g. package properties) to stdout which are used by some handlers.
|
||||||
* ``ahriman.core.gitremote`` is a package with remote PKGBUILD triggers. Should not be called directly.
|
* ``ahriman.core.gitremote`` is a package with remote PKGBUILD triggers. Should not be called directly.
|
||||||
|
* ``ahriman.core.housekeeping`` package provides few triggers for removing old data.
|
||||||
* ``ahriman.core.http`` package provides HTTP clients which can be used later by other classes.
|
* ``ahriman.core.http`` package provides HTTP clients which can be used later by other classes.
|
||||||
* ``ahriman.core.log`` is a log utils package. It includes logger loader class, custom HTTP based logger and some wrappers.
|
* ``ahriman.core.log`` is a log utils package. It includes logger loader class, custom HTTP based logger and some wrappers.
|
||||||
* ``ahriman.core.report`` is a package with reporting triggers. Should not be called directly.
|
* ``ahriman.core.report`` is a package with reporting triggers. Should not be called directly.
|
||||||
|
|||||||
@ -65,6 +65,8 @@ will try to read value from ``SECRET`` environment variable. In case if the requ
|
|||||||
|
|
||||||
will eventually lead ``key`` option in section ``section1`` to be set to the value of ``HOME`` environment variable (if available).
|
will eventually lead ``key`` option in section ``section1`` to be set to the value of ``HOME`` environment variable (if available).
|
||||||
|
|
||||||
|
Moreover, configuration can be read from environment variables directly by following the same naming convention, e.g. in the example above, one can have environment variable named ``section1:key`` (e.g. ``section1:key=$HOME``) and it will be substituted to the configuration with the highest priority.
|
||||||
|
|
||||||
There is also additional subcommand which will allow to validate configuration and print found errors. In order to do so, run ``service-config-validate`` subcommand, e.g.:
|
There is also additional subcommand which will allow to validate configuration and print found errors. In order to do so, run ``service-config-validate`` subcommand, e.g.:
|
||||||
|
|
||||||
.. code-block:: shell
|
.. code-block:: shell
|
||||||
@ -81,7 +83,6 @@ Base configuration settings.
|
|||||||
* ``apply_migrations`` - perform database migrations on the application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations manually.
|
* ``apply_migrations`` - perform database migrations on the application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations manually.
|
||||||
* ``database`` - path to the application SQLite database, string, required.
|
* ``database`` - path to the application SQLite database, string, required.
|
||||||
* ``include`` - path to directory with configuration files overrides, string, optional. Files will be read in alphabetical order.
|
* ``include`` - path to directory with configuration files overrides, string, optional. Files will be read in alphabetical order.
|
||||||
* ``keep_last_logs`` - amount of build logs to be kept for each package, integer, optional ,default ``0``. Logs will be cleared at the end of each process.
|
|
||||||
* ``logging`` - path to logging configuration, string, required. Check ``logging.ini`` for reference.
|
* ``logging`` - path to logging configuration, string, required. Check ``logging.ini`` for reference.
|
||||||
|
|
||||||
``alpm:*`` groups
|
``alpm:*`` groups
|
||||||
@ -138,6 +139,8 @@ Build related configuration. Group name can refer to architecture, e.g. ``build:
|
|||||||
|
|
||||||
Base repository settings.
|
Base repository settings.
|
||||||
|
|
||||||
|
* ``architecture`` - repository architecture, string. This field is read-only and generated automatically from run options if possible.
|
||||||
|
* ``name`` - repository name, string. This field is read-only and generated automatically from run options if possible.
|
||||||
* ``root`` - root path for application, string, required.
|
* ``root`` - root path for application, string, required.
|
||||||
|
|
||||||
``sign:*`` groups
|
``sign:*`` groups
|
||||||
@ -166,6 +169,7 @@ Reporting to web service related settings. In most cases there is fallback to we
|
|||||||
Web server settings. This feature requires ``aiohttp`` libraries to be installed.
|
Web server settings. This feature requires ``aiohttp`` libraries to be installed.
|
||||||
|
|
||||||
* ``address`` - optional address in form ``proto://host:port`` (``port`` can be omitted in case of default ``proto`` ports), will be used instead of ``http://{host}:{port}`` in case if set, string, optional. This option is required in case if ``OAuth`` provider is used.
|
* ``address`` - optional address in form ``proto://host:port`` (``port`` can be omitted in case of default ``proto`` ports), will be used instead of ``http://{host}:{port}`` in case if set, string, optional. This option is required in case if ``OAuth`` provider is used.
|
||||||
|
* ``autorefresh_intervals`` - enable page auto refresh options, space separated list of integers, optional. The first defined interval will be used as default. If no intervals set, the auto refresh buttons will be disabled. If first element of the list equals ``0``, auto refresh will be disabled by default.
|
||||||
* ``enable_archive_upload`` - allow to upload packages via HTTP (i.e. call of ``/api/v1/service/upload`` uri), boolean, optional, default ``no``.
|
* ``enable_archive_upload`` - allow to upload packages via HTTP (i.e. call of ``/api/v1/service/upload`` uri), boolean, optional, default ``no``.
|
||||||
* ``host`` - host to bind, string, optional.
|
* ``host`` - host to bind, string, optional.
|
||||||
* ``index_url`` - full URL of the repository index page, string, optional.
|
* ``index_url`` - full URL of the repository index page, string, optional.
|
||||||
@ -179,7 +183,7 @@ Web server settings. This feature requires ``aiohttp`` libraries to be installed
|
|||||||
* ``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, integer, optional.
|
||||||
|
|
||||||
``keyring`` group
|
``keyring`` group
|
||||||
--------------------
|
-----------------
|
||||||
|
|
||||||
Keyring package generator plugin.
|
Keyring package generator plugin.
|
||||||
|
|
||||||
@ -197,6 +201,13 @@ Keyring generator plugin
|
|||||||
* ``revoked`` - list of revoked packagers keys, space separated list of strings, optional.
|
* ``revoked`` - list of revoked packagers keys, space separated list of strings, optional.
|
||||||
* ``trusted`` - list of master keys, space separated list of strings, optional, if not set, the ``key`` option from ``sign`` group will be used.
|
* ``trusted`` - list of master keys, space separated list of strings, optional, if not set, the ``key`` option from ``sign`` group will be used.
|
||||||
|
|
||||||
|
``housekeeping`` group
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
This section describes settings for the ``ahriman.core.housekeeping.LogsRotationTrigger`` plugin.
|
||||||
|
|
||||||
|
* ``keep_last_logs`` - amount of build logs to be kept for each package, integer, optional ,default ``0``. Logs will be cleared at the end of each process.
|
||||||
|
|
||||||
``mirrorlist`` group
|
``mirrorlist`` group
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
pkgbase='ahriman'
|
pkgbase='ahriman'
|
||||||
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
|
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
|
||||||
pkgver=2.18.3
|
pkgver=2.19.0
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="ArcH linux ReposItory MANager"
|
pkgdesc="ArcH linux ReposItory MANager"
|
||||||
arch=('any')
|
arch=('any')
|
||||||
@ -40,6 +40,7 @@ package_ahriman-core() {
|
|||||||
'rsync: sync by using rsync')
|
'rsync: sync by using rsync')
|
||||||
install="$pkgbase.install"
|
install="$pkgbase.install"
|
||||||
backup=('etc/ahriman.ini'
|
backup=('etc/ahriman.ini'
|
||||||
|
'etc/ahriman.ini.d/00-housekeeping.ini'
|
||||||
'etc/ahriman.ini.d/logging.ini')
|
'etc/ahriman.ini.d/logging.ini')
|
||||||
|
|
||||||
cd "$pkgbase-$pkgver"
|
cd "$pkgbase-$pkgver"
|
||||||
@ -49,6 +50,7 @@ package_ahriman-core() {
|
|||||||
|
|
||||||
# keep usr/share configs as reference and copy them to /etc
|
# keep usr/share configs as reference and copy them to /etc
|
||||||
install -Dm644 "$pkgdir/usr/share/$pkgbase/settings/ahriman.ini" "$pkgdir/etc/ahriman.ini"
|
install -Dm644 "$pkgdir/usr/share/$pkgbase/settings/ahriman.ini" "$pkgdir/etc/ahriman.ini"
|
||||||
|
install -Dm644 "$pkgdir/usr/share/$pkgbase/settings/ahriman.ini.d/00-housekeeping.ini" "$pkgdir/etc/ahriman.ini.d/00-housekeeping.ini"
|
||||||
install -Dm644 "$pkgdir/usr/share/$pkgbase/settings/ahriman.ini.d/logging.ini" "$pkgdir/etc/ahriman.ini.d/logging.ini"
|
install -Dm644 "$pkgdir/usr/share/$pkgbase/settings/ahriman.ini.d/logging.ini" "$pkgdir/etc/ahriman.ini.d/logging.ini"
|
||||||
|
|
||||||
install -Dm644 "$srcdir/$pkgbase.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgbase.conf"
|
install -Dm644 "$srcdir/$pkgbase.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgbase.conf"
|
||||||
|
|||||||
@ -5,6 +5,7 @@ After=network.target
|
|||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart=/usr/bin/ahriman web
|
ExecStart=/usr/bin/ahriman web
|
||||||
|
ExecReload=/usr/bin/ahriman web-reload
|
||||||
User=ahriman
|
User=ahriman
|
||||||
Group=ahriman
|
Group=ahriman
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,6 @@ logging = ahriman.ini.d/logging.ini
|
|||||||
;apply_migrations = yes
|
;apply_migrations = yes
|
||||||
; Path to the application SQLite database.
|
; Path to the application SQLite database.
|
||||||
database = ${repository:root}/ahriman.db
|
database = ${repository:root}/ahriman.db
|
||||||
; Keep last build logs for each package
|
|
||||||
keep_last_logs = 5
|
|
||||||
|
|
||||||
[alpm]
|
[alpm]
|
||||||
; Path to pacman system database cache.
|
; Path to pacman system database cache.
|
||||||
@ -45,9 +43,11 @@ triggers[] = ahriman.core.gitremote.RemotePullTrigger
|
|||||||
triggers[] = ahriman.core.report.ReportTrigger
|
triggers[] = ahriman.core.report.ReportTrigger
|
||||||
triggers[] = ahriman.core.upload.UploadTrigger
|
triggers[] = ahriman.core.upload.UploadTrigger
|
||||||
triggers[] = ahriman.core.gitremote.RemotePushTrigger
|
triggers[] = ahriman.core.gitremote.RemotePushTrigger
|
||||||
|
triggers[] = ahriman.core.housekeeping.LogsRotationTrigger
|
||||||
; List of well-known triggers. Used only for configuration purposes.
|
; List of well-known triggers. Used only for configuration purposes.
|
||||||
triggers_known[] = ahriman.core.gitremote.RemotePullTrigger
|
triggers_known[] = ahriman.core.gitremote.RemotePullTrigger
|
||||||
triggers_known[] = ahriman.core.gitremote.RemotePushTrigger
|
triggers_known[] = ahriman.core.gitremote.RemotePushTrigger
|
||||||
|
triggers_known[] = ahriman.core.housekeeping.LogsRotationTrigger
|
||||||
triggers_known[] = ahriman.core.report.ReportTrigger
|
triggers_known[] = ahriman.core.report.ReportTrigger
|
||||||
triggers_known[] = ahriman.core.upload.UploadTrigger
|
triggers_known[] = ahriman.core.upload.UploadTrigger
|
||||||
; Maximal age in seconds of the VCS packages before their version will be updated with its remote source.
|
; Maximal age in seconds of the VCS packages before their version will be updated with its remote source.
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
[logs-rotation]
|
||||||
|
; Keep last build logs for each package
|
||||||
|
keep_last_logs = 5
|
||||||
@ -28,6 +28,10 @@ allow_read_only = yes
|
|||||||
; External address of the web service. Will be used for some features like OAuth. If none set will be generated as
|
; External address of the web service. Will be used for some features like OAuth. If none set will be generated as
|
||||||
; address = http://${web:host}:${web:port}
|
; address = http://${web:host}:${web:port}
|
||||||
;address = http://${web:host}:${web:port}
|
;address = http://${web:host}:${web:port}
|
||||||
|
; Enable page auto refresh. Intervals are given in seconds. Default interval is always the first element of the list.
|
||||||
|
; If no intervals set, auto refresh will be disabled. 0 can only be the first element and will disable auto refresh
|
||||||
|
; by default.
|
||||||
|
autorefresh_intervals = 5 1 10 30 60
|
||||||
; Enable file upload endpoint used by some triggers.
|
; Enable file upload endpoint used by some triggers.
|
||||||
;enable_archive_upload = no
|
;enable_archive_upload = no
|
||||||
; Address to bind the server.
|
; Address to bind the server.
|
||||||
|
|||||||
@ -55,6 +55,11 @@
|
|||||||
<i class="bi bi-play"></i> update
|
<i class="bi bi-play"></i> update
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<button id="update-repositories-button" class="btn dropdown-item" onclick="refreshDatabases()">
|
||||||
|
<i class="bi bi-arrow-down-circle"></i> update pacman databases
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button id="package-rebuild-button" class="btn dropdown-item" data-bs-toggle="modal" data-bs-target="#package-rebuild-modal">
|
<button id="package-rebuild-button" class="btn dropdown-item" data-bs-toggle="modal" data-bs-target="#package-rebuild-modal">
|
||||||
<i class="bi bi-arrow-clockwise"></i> rebuild
|
<i class="bi bi-arrow-clockwise"></i> rebuild
|
||||||
@ -75,10 +80,28 @@
|
|||||||
<button type="button" class="btn btn-secondary" onclick="reload()">
|
<button type="button" class="btn btn-secondary" onclick="reload()">
|
||||||
<i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span>
|
<i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{% if autorefresh_intervals %}
|
||||||
|
<div class="btn-group">
|
||||||
|
<input id="table-autoreload-button" type="checkbox" class="btn-check" autocomplete="off" onclick="toggleTableAutoReload()" checked>
|
||||||
|
<label for="table-autoreload-button" class="btn btn-outline-secondary" title="toggle auto reload"><i class="bi bi-clock"></i></label>
|
||||||
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<span class="visually-hidden">select interval</span>
|
||||||
|
</button>
|
||||||
|
<ul id="table-autoreload-input" class="dropdown-menu">
|
||||||
|
{% for interval in autorefresh_intervals %}
|
||||||
|
<li><a class="dropdown-item {{ "active" if interval.is_active }}" onclick="toggleTableAutoReload({{ interval.interval }})" data-interval="{{ interval.interval }}">{{ interval.text }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table id="packages"
|
<table id="packages"
|
||||||
data-classes="table table-hover"
|
data-classes="table table-hover"
|
||||||
|
data-cookie="true"
|
||||||
|
data-cookie-id-table="ahriman-packages"
|
||||||
|
data-cookie-storage="localStorage"
|
||||||
data-export-options='{"fileName": "packages"}'
|
data-export-options='{"fileName": "packages"}'
|
||||||
data-filter-control="true"
|
data-filter-control="true"
|
||||||
data-filter-control-visible="false"
|
data-filter-control-visible="false"
|
||||||
@ -97,8 +120,8 @@
|
|||||||
data-sortable="true"
|
data-sortable="true"
|
||||||
data-sort-name="base"
|
data-sort-name="base"
|
||||||
data-sort-order="asc"
|
data-sort-order="asc"
|
||||||
data-toggle="table"
|
data-toolbar="#toolbar"
|
||||||
data-toolbar="#toolbar">
|
data-unique-id="id">
|
||||||
<thead class="table-primary">
|
<thead class="table-primary">
|
||||||
<tr>
|
<tr>
|
||||||
<th data-checkbox="true"></th>
|
<th data-checkbox="true"></th>
|
||||||
|
|||||||
@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
function createAlert(title, message, clz, action, id) {
|
function createAlert(title, message, clz, action, id) {
|
||||||
id ??= md5(title + message); // MD5 id from the content
|
id ??= md5(title + message); // MD5 id from the content
|
||||||
if (alertPlaceholder.querySelector(`#alert-${id}`)) return; // check if there are duplicates
|
if (alertPlaceholder.querySelector(`#alert-${id}`)) {
|
||||||
|
return; // check if there are duplicates
|
||||||
|
}
|
||||||
|
|
||||||
const wrapper = document.createElement("div");
|
const wrapper = document.createElement("div");
|
||||||
wrapper.id = `alert-${id}`;
|
wrapper.id = `alert-${id}`;
|
||||||
|
|||||||
@ -51,6 +51,87 @@
|
|||||||
const dashboardPackagesCountChartCanvas = document.getElementById("dashboard-packages-count-chart");
|
const dashboardPackagesCountChartCanvas = document.getElementById("dashboard-packages-count-chart");
|
||||||
let dashboardPackagesCountChart = null;
|
let dashboardPackagesCountChart = null;
|
||||||
|
|
||||||
|
function statusLoad() {
|
||||||
|
const badgeClass = status => {
|
||||||
|
if (status === "pending") return "btn-outline-warning";
|
||||||
|
if (status === "building") return "btn-outline-warning";
|
||||||
|
if (status === "failed") return "btn-outline-danger";
|
||||||
|
if (status === "success") return "btn-outline-success";
|
||||||
|
return "btn-outline-secondary";
|
||||||
|
};
|
||||||
|
|
||||||
|
makeRequest(
|
||||||
|
"/api/v1/status",
|
||||||
|
{
|
||||||
|
query: {
|
||||||
|
architecture: repository.architecture,
|
||||||
|
repository: repository.repository,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
|
},
|
||||||
|
data => {
|
||||||
|
versionBadge.innerHTML = `<i class="bi bi-github"></i> ahriman ${safe(data.version)}`;
|
||||||
|
|
||||||
|
dashboardButton.classList.remove(...dashboardButton.classList);
|
||||||
|
dashboardButton.classList.add("btn");
|
||||||
|
dashboardButton.classList.add(badgeClass(data.status.status));
|
||||||
|
|
||||||
|
dashboardModalHeader.classList.remove(...dashboardModalHeader.classList);
|
||||||
|
dashboardModalHeader.classList.add("modal-header");
|
||||||
|
headerClass(data.status.status).forEach(clz => dashboardModalHeader.classList.add(clz));
|
||||||
|
|
||||||
|
dashboardName.textContent = data.repository;
|
||||||
|
dashboardArchitecture.textContent = data.architecture;
|
||||||
|
dashboardStatus.textContent = data.status.status;
|
||||||
|
dashboardStatusTimestamp.textContent = new Date(1000 * data.status.timestamp).toISOStringShort();
|
||||||
|
|
||||||
|
if (dashboardPackagesStatusesChart) {
|
||||||
|
const labels = [
|
||||||
|
"unknown",
|
||||||
|
"pending",
|
||||||
|
"building",
|
||||||
|
"failed",
|
||||||
|
"success",
|
||||||
|
];
|
||||||
|
dashboardPackagesStatusesChart.config.data = {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
label: "packages in status",
|
||||||
|
data: labels.map(label => data.packages[label]),
|
||||||
|
backgroundColor: [
|
||||||
|
"rgb(55, 58, 60)",
|
||||||
|
"rgb(255, 117, 24)",
|
||||||
|
"rgb(255, 117, 24)",
|
||||||
|
"rgb(255, 0, 57)",
|
||||||
|
"rgb(63, 182, 24)", // copy-paste from current style
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
dashboardPackagesStatusesChart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dashboardPackagesCountChart) {
|
||||||
|
dashboardPackagesCountChart.config.data = {
|
||||||
|
labels: ["packages"],
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "archives",
|
||||||
|
data: [data.stats.packages],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "bases",
|
||||||
|
data: [data.stats.bases],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
dashboardPackagesCountChart.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
dashboardCanvas.hidden = data.status.total > 0;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ready(_ => {
|
ready(_ => {
|
||||||
dashboardPackagesStatusesChart = new Chart(dashboardPackagesStatusesChartCanvas, {
|
dashboardPackagesStatusesChart = new Chart(dashboardPackagesStatusesChartCanvas, {
|
||||||
type: "pie",
|
type: "pie",
|
||||||
|
|||||||
@ -24,6 +24,13 @@
|
|||||||
<datalist id="package-add-known-packages-dlist"></datalist>
|
<datalist id="package-add-known-packages-dlist"></datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-3 col-form-label"></label>
|
||||||
|
<div class="col-9">
|
||||||
|
<input id="package-add-refresh-input" type="checkbox" class="form-check-input" value="" checked>
|
||||||
|
<label for="package-add-refresh-input" class="form-check-label">update pacman databases</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button id="package-add-variable-button" type="button" class="form-control btn btn-light rounded" onclick="packageAddVariableInputCreate()"><i class="bi bi-plus"></i> add environment variable </button>
|
<button id="package-add-variable-button" type="button" class="form-control btn btn-light rounded" onclick="packageAddVariableInputCreate()"><i class="bi bi-plus"></i> add environment variable </button>
|
||||||
@ -50,6 +57,8 @@
|
|||||||
|
|
||||||
const packageAddVariablesDiv = document.getElementById("package-add-variables-div");
|
const packageAddVariablesDiv = document.getElementById("package-add-variables-div");
|
||||||
|
|
||||||
|
const packageAddRefreshInput = document.getElementById("package-add-refresh-input");
|
||||||
|
|
||||||
function packageAddVariableInputCreate() {
|
function packageAddVariableInputCreate() {
|
||||||
const variableInput = document.createElement("div");
|
const variableInput = document.createElement("div");
|
||||||
variableInput.classList.add("input-group");
|
variableInput.classList.add("input-group");
|
||||||
@ -99,16 +108,18 @@
|
|||||||
return {patches: patches};
|
return {patches: patches};
|
||||||
}
|
}
|
||||||
|
|
||||||
function packagesAdd(packages, patches, repository) {
|
function packagesAdd(packages, patches, repository, data) {
|
||||||
packages = packages ?? packageAddInput.value;
|
packages = packages ?? packageAddInput.value;
|
||||||
patches = patches ?? patchesParse();
|
patches = patches ?? patchesParse();
|
||||||
repository = repository ?? getRepositorySelector(packageAddRepositoryInput);
|
repository = repository ?? getRepositorySelector(packageAddRepositoryInput);
|
||||||
|
data = data ?? {refresh: packageAddRefreshInput.checked};
|
||||||
|
|
||||||
if (packages) {
|
if (packages) {
|
||||||
bootstrap.Modal.getOrCreateInstance(packageAddModal).hide();
|
bootstrap.Modal.getOrCreateInstance(packageAddModal).hide();
|
||||||
const onSuccess = update => `Packages ${update} have been added`;
|
const onSuccess = update => `Packages ${update} have been added`;
|
||||||
const onFailure = error => `Package addition failed: ${error}`;
|
const onFailure = error => `Package addition failed: ${error}`;
|
||||||
doPackageAction("/api/v1/service/add", [packages], repository, onSuccess, onFailure, patches);
|
const parameters = Object.assign({}, data, patches);
|
||||||
|
doPackageAction("/api/v1/service/add", [packages], repository, onSuccess, onFailure, parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,8 +148,19 @@
|
|||||||
|
|
||||||
packageAddInput.addEventListener("keyup", _ => {
|
packageAddInput.addEventListener("keyup", _ => {
|
||||||
clearTimeout(packageAddInput.requestTimeout);
|
clearTimeout(packageAddInput.requestTimeout);
|
||||||
packageAddInput.requestTimeout = setTimeout(_ => {
|
|
||||||
|
// do not update datalist if search string didn't change yet
|
||||||
const value = packageAddInput.value;
|
const value = packageAddInput.value;
|
||||||
|
const previousValue = packageAddInput.dataset.previousValue;
|
||||||
|
if (value === previousValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store current search string in attributes
|
||||||
|
packageAddInput.dataset.previousValue = value;
|
||||||
|
|
||||||
|
// perform data list update
|
||||||
|
packageAddInput.requestTimeout = setTimeout(_ => {
|
||||||
|
|
||||||
if (value.length >= 3) {
|
if (value.length >= 3) {
|
||||||
makeRequest(
|
makeRequest(
|
||||||
|
|||||||
@ -80,8 +80,7 @@
|
|||||||
data-classes="table table-hover"
|
data-classes="table table-hover"
|
||||||
data-sortable="true"
|
data-sortable="true"
|
||||||
data-sort-name="timestamp"
|
data-sort-name="timestamp"
|
||||||
data-sort-order="desc"
|
data-sort-order="desc">
|
||||||
data-toggle="table">
|
|
||||||
<thead class="table-primary">
|
<thead class="table-primary">
|
||||||
<tr>
|
<tr>
|
||||||
<th data-align="right" data-field="timestamp">date</th>
|
<th data-align="right" data-field="timestamp">date</th>
|
||||||
@ -95,10 +94,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{% if not auth.enabled or auth.username is not none %}
|
{% if not auth.enabled or auth.username is not none %}
|
||||||
<button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()" data-bs-dismiss="modal"><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button>
|
<input id="package-info-refresh-input" type="checkbox" class="form-check-input" value="" checked>
|
||||||
|
<label for="package-info-refresh-input" class="form-check-label">update pacman databases</label>
|
||||||
|
|
||||||
|
<button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()"><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button>
|
||||||
<button id="package-info-remove-button" type="submit" class="btn btn-danger" onclick="packageInfoRemove()" data-bs-dismiss="modal"><i class="bi bi-trash"></i><span class="d-none d-sm-inline"> remove</span></button>
|
<button id="package-info-remove-button" type="submit" class="btn btn-danger" onclick="packageInfoRemove()" data-bs-dismiss="modal"><i class="bi bi-trash"></i><span class="d-none d-sm-inline"> remove</span></button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if autorefresh_intervals %}
|
||||||
<button type="button" class="btn btn-secondary" onclick="showPackageInfo()"><i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span></button>
|
<button type="button" class="btn btn-secondary" onclick="showPackageInfo()"><i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span></button>
|
||||||
|
<div class="btn-group dropup">
|
||||||
|
<input id="package-info-autoreload-button" type="checkbox" class="btn-check" autocomplete="off" onclick="togglePackageInfoAutoReload()" checked>
|
||||||
|
<label for="package-info-autoreload-button" class="btn btn-outline-secondary" title="toggle auto reload"><i class="bi bi-clock"></i></label>
|
||||||
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<span class="visually-hidden">select interval</span>
|
||||||
|
</button>
|
||||||
|
<ul id="package-info-autoreload-input" class="dropdown-menu">
|
||||||
|
{% for interval in autorefresh_intervals %}
|
||||||
|
<li><a class="dropdown-item {{ "active" if interval.is_active }}" onclick="togglePackageInfoAutoReload({{ interval.interval }})" data-interval="{{ interval.interval }}">{{ interval.text }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i><span class="d-none d-sm-inline"> close</span></button>
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i><span class="d-none d-sm-inline"> close</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -135,6 +151,12 @@
|
|||||||
const packageInfoVariablesBlock = document.getElementById("package-info-variables-block");
|
const packageInfoVariablesBlock = document.getElementById("package-info-variables-block");
|
||||||
const packageInfoVariablesDiv = document.getElementById("package-info-variables-div");
|
const packageInfoVariablesDiv = document.getElementById("package-info-variables-div");
|
||||||
|
|
||||||
|
const packageInfoRefreshInput = document.getElementById("package-info-refresh-input");
|
||||||
|
|
||||||
|
const packageInfoAutoReloadButton = document.getElementById("package-info-autoreload-button");
|
||||||
|
const packageInfoAutoReloadInput = document.getElementById("package-info-autoreload-input");
|
||||||
|
let packageInfoAutoReloadTask = null;
|
||||||
|
|
||||||
function clearChart() {
|
function clearChart() {
|
||||||
packageInfoEventsUpdateChartCanvas.hidden = true;
|
packageInfoEventsUpdateChartCanvas.hidden = true;
|
||||||
if (packageInfoEventsUpdateChart) {
|
if (packageInfoEventsUpdateChart) {
|
||||||
@ -143,6 +165,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertLogs(data, filter) {
|
||||||
|
return data
|
||||||
|
.filter((filter || Boolean))
|
||||||
|
.map(log_record => `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`)
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
async function copyChanges() {
|
async function copyChanges() {
|
||||||
const changes = packageInfoChangesInput.textContent;
|
const changes = packageInfoChangesInput.textContent;
|
||||||
await copyToClipboard(changes, packageInfoChangesCopyButton);
|
await copyToClipboard(changes, packageInfoChangesCopyButton);
|
||||||
@ -286,6 +315,69 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadLogs(packageBase, onFailure) {
|
function loadLogs(packageBase, onFailure) {
|
||||||
|
const sortFn = (left, right) => left.process_id.localeCompare(right.process_id) || left.version.localeCompare(right.version);
|
||||||
|
const compareFn = (left, right) => left.process_id === right.process_id && left.version === right.version;
|
||||||
|
|
||||||
|
makeRequest(
|
||||||
|
`/api/v2/packages/${packageBase}/logs`,
|
||||||
|
{
|
||||||
|
query: {
|
||||||
|
architecture: repository.architecture,
|
||||||
|
head: true,
|
||||||
|
repository: repository.repository,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
|
},
|
||||||
|
data => {
|
||||||
|
const currentVersions = Array.from(packageInfoLogsVersions.children)
|
||||||
|
.map(el => {
|
||||||
|
return {
|
||||||
|
process_id: el.dataset.processId,
|
||||||
|
version: el.dataset.version,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort(sortFn);
|
||||||
|
const newVersions = data
|
||||||
|
.map(el => {
|
||||||
|
return {
|
||||||
|
process_id: el.process_id,
|
||||||
|
version: el.version,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort(sortFn);
|
||||||
|
|
||||||
|
if (currentVersions.equals(newVersions, compareFn))
|
||||||
|
loadLogsActive(packageBase);
|
||||||
|
else
|
||||||
|
loadLogsAll(packageBase, onFailure);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLogsActive(packageBase) {
|
||||||
|
const activeLogSelector = packageInfoLogsVersions.querySelector(".active");
|
||||||
|
|
||||||
|
if (activeLogSelector) {
|
||||||
|
makeRequest(
|
||||||
|
`/api/v2/packages/${packageBase}/logs`,
|
||||||
|
{
|
||||||
|
query: {
|
||||||
|
architecture: repository.architecture,
|
||||||
|
repository: repository.repository,
|
||||||
|
version: activeLogSelector.dataset.version,
|
||||||
|
process_id: activeLogSelector.dataset.processId,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
|
},
|
||||||
|
data => {
|
||||||
|
activeLogSelector.dataset.logs = convertLogs(data);
|
||||||
|
activeLogSelector.click();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLogsAll(packageBase, onFailure) {
|
||||||
makeRequest(
|
makeRequest(
|
||||||
`/api/v2/packages/${packageBase}/logs`,
|
`/api/v2/packages/${packageBase}/logs`,
|
||||||
{
|
{
|
||||||
@ -314,15 +406,19 @@
|
|||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
link.classList.add("dropdown-item");
|
link.classList.add("dropdown-item");
|
||||||
|
|
||||||
|
link.dataset.version = version.version;
|
||||||
|
link.dataset.processId = version.process_id;
|
||||||
|
link.dataset.logs = convertLogs(data, log_record => log_record.version === version.version && log_record.process_id === version.process_id);
|
||||||
|
|
||||||
link.textContent = new Date(1000 * version.created).toISOStringShort();
|
link.textContent = new Date(1000 * version.created).toISOStringShort();
|
||||||
link.href = "#";
|
link.href = "#";
|
||||||
link.onclick = _ => {
|
link.onclick = _ => {
|
||||||
const logs = data
|
// check if we are at the bottom of the code block
|
||||||
.filter(log_record => log_record.version === version.version && log_record.process_id === version.process_id)
|
const isScrolledToBottom = packageInfoLogsInput.scrollTop + packageInfoLogsInput.clientHeight >= packageInfoLogsInput.scrollHeight;
|
||||||
.map(log_record => `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`);
|
packageInfoLogsInput.textContent = link.dataset.logs;
|
||||||
|
|
||||||
packageInfoLogsInput.textContent = logs.join("\n");
|
|
||||||
highlight(packageInfoLogsInput);
|
highlight(packageInfoLogsInput);
|
||||||
|
if (isScrolledToBottom)
|
||||||
|
packageInfoLogsInput.scrollTop = packageInfoLogsInput.scrollHeight; // scroll to the new end
|
||||||
|
|
||||||
Array.from(packageInfoLogsVersions.children).forEach(el => el.classList.remove("active"));
|
Array.from(packageInfoLogsVersions.children).forEach(el => el.classList.remove("active"));
|
||||||
link.classList.add("active");
|
link.classList.add("active");
|
||||||
@ -398,23 +494,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function packageInfoRemove() {
|
function packageInfoRemove() {
|
||||||
const packageBase = packageInfoModal.package;
|
const packageBase = packageInfoModal.dataset.package;
|
||||||
packagesRemove([packageBase]);
|
packagesRemove([packageBase]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function packageInfoUpdate() {
|
function packageInfoUpdate() {
|
||||||
const packageBase = packageInfoModal.package;
|
const packageBase = packageInfoModal.dataset.package;
|
||||||
packagesAdd(packageBase, [], repository);
|
packagesAdd(packageBase, [], repository, {refresh: packageInfoRefreshInput.checked});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showPackageInfo(packageBase) {
|
function showPackageInfo(packageBase) {
|
||||||
const isPackageBaseSet = packageBase !== undefined;
|
const isPackageBaseSet = packageBase !== undefined;
|
||||||
if (isPackageBaseSet) {
|
if (isPackageBaseSet) {
|
||||||
// set package base as currently used
|
// set package base as currently used
|
||||||
packageInfoModal.package = packageBase;
|
packageInfoModal.dataset.package = packageBase;
|
||||||
} else {
|
} else {
|
||||||
// read package base from the current window attribute
|
// read package base from the current window attribute
|
||||||
packageBase = packageInfoModal.package;
|
packageBase = packageInfoModal.dataset.package;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFailure = error => {
|
const onFailure = error => {
|
||||||
@ -433,10 +529,27 @@
|
|||||||
|
|
||||||
if (isPackageBaseSet) {
|
if (isPackageBaseSet) {
|
||||||
bootstrap.Modal.getOrCreateInstance(packageInfoModal).show();
|
bootstrap.Modal.getOrCreateInstance(packageInfoModal).show();
|
||||||
|
{% if autorefresh_intervals %}
|
||||||
|
togglePackageInfoAutoReload();
|
||||||
|
{% endif %}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function togglePackageInfoAutoReload(interval) {
|
||||||
|
clearInterval(packageInfoAutoReloadTask);
|
||||||
|
packageInfoAutoReloadTask = toggleAutoReload(packageInfoAutoReloadButton, interval, packageInfoAutoReloadInput, _ => {
|
||||||
|
if (!hasActiveSelection()) {
|
||||||
|
const packageBase = packageInfoModal.dataset.package;
|
||||||
|
// we only poll status and logs here
|
||||||
|
loadPackage(packageBase);
|
||||||
|
loadLogs(packageBase);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ready(_ => {
|
ready(_ => {
|
||||||
|
packageInfoEventsTable.bootstrapTable({});
|
||||||
|
|
||||||
packageInfoEventsUpdateChart = new Chart(packageInfoEventsUpdateChartCanvas, {
|
packageInfoEventsUpdateChart = new Chart(packageInfoEventsUpdateChartCanvas, {
|
||||||
type: "line",
|
type: "line",
|
||||||
data: {},
|
data: {},
|
||||||
@ -463,6 +576,11 @@
|
|||||||
packageInfoChangesInput.textContent = "";
|
packageInfoChangesInput.textContent = "";
|
||||||
packageInfoEventsTable.bootstrapTable("load", []);
|
packageInfoEventsTable.bootstrapTable("load", []);
|
||||||
clearChart();
|
clearChart();
|
||||||
|
|
||||||
|
clearInterval(packageInfoAutoReloadTask);
|
||||||
|
packageInfoAutoReloadTask = null; // not really required (?) but lets clear everything
|
||||||
});
|
});
|
||||||
|
|
||||||
|
restoreAutoReloadSettings(packageInfoAutoReloadButton, packageInfoAutoReloadInput);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -10,6 +10,10 @@
|
|||||||
const dashboardButton = document.getElementById("dashboard-button");
|
const dashboardButton = document.getElementById("dashboard-button");
|
||||||
const versionBadge = document.getElementById("badge-version");
|
const versionBadge = document.getElementById("badge-version");
|
||||||
|
|
||||||
|
const tableAutoReloadButton = document.getElementById("table-autoreload-button");
|
||||||
|
const tableAutoReloadInput = document.getElementById("table-autoreload-input");
|
||||||
|
let tableAutoReloadTask = null;
|
||||||
|
|
||||||
function doPackageAction(uri, packages, repository, successText, failureText, data) {
|
function doPackageAction(uri, packages, repository, successText, failureText, data) {
|
||||||
makeRequest(
|
makeRequest(
|
||||||
uri,
|
uri,
|
||||||
@ -55,6 +59,41 @@
|
|||||||
return table.bootstrapTable("getSelections").map(row => row.id);
|
return table.bootstrapTable("getSelections").map(row => row.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function packagesLoad(onFailure) {
|
||||||
|
makeRequest(
|
||||||
|
"/api/v1/packages",
|
||||||
|
{
|
||||||
|
query: {
|
||||||
|
architecture: repository.architecture,
|
||||||
|
repository: repository.repository,
|
||||||
|
},
|
||||||
|
convert: response => response.json(),
|
||||||
|
},
|
||||||
|
data => {
|
||||||
|
const payload = data
|
||||||
|
.map(description => {
|
||||||
|
const package_base = description.package.base;
|
||||||
|
const web_url = description.package.remote.web_url;
|
||||||
|
return {
|
||||||
|
id: package_base,
|
||||||
|
base: web_url ? safeLink(web_url, package_base, package_base).outerHTML : safe(package_base),
|
||||||
|
version: safe(description.package.version),
|
||||||
|
packager: description.package.packager ? safe(description.package.packager) : "",
|
||||||
|
packages: listToTable(Object.keys(description.package.packages)),
|
||||||
|
groups: listToTable(extractListProperties(description.package, "groups")),
|
||||||
|
licenses: listToTable(extractListProperties(description.package, "licenses")),
|
||||||
|
timestamp: new Date(1000 * description.status.timestamp).toISOStringShort(),
|
||||||
|
status: description.status.status,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
updateTable(table, payload);
|
||||||
|
table.bootstrapTable("hideLoading");
|
||||||
|
},
|
||||||
|
onFailure,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function packagesRemove(packages) {
|
function packagesRemove(packages) {
|
||||||
packages = packages ?? getSelection();
|
packages = packages ?? getSelection();
|
||||||
const onSuccess = update => `Packages ${update} have been removed`;
|
const onSuccess = update => `Packages ${update} have been removed`;
|
||||||
@ -73,48 +112,22 @@
|
|||||||
doPackageAction(url, currentSelection, repository, onSuccess, onFailure);
|
doPackageAction(url, currentSelection, repository, onSuccess, onFailure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshDatabases() {
|
||||||
|
const onSuccess = _ => "Pacman database update has been requested";
|
||||||
|
const onFailure = error => `Could not update pacman databases: ${error}`;
|
||||||
|
const parameters = {
|
||||||
|
refresh: true,
|
||||||
|
aur: false,
|
||||||
|
local: false,
|
||||||
|
manual: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
doPackageAction("/api/v1/service/update", [], repository, onSuccess, onFailure, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
table.bootstrapTable("showLoading");
|
table.bootstrapTable("showLoading");
|
||||||
|
const onFailure = error => {
|
||||||
const badgeClass = status => {
|
|
||||||
if (status === "pending") return "btn-outline-warning";
|
|
||||||
if (status === "building") return "btn-outline-warning";
|
|
||||||
if (status === "failed") return "btn-outline-danger";
|
|
||||||
if (status === "success") return "btn-outline-success";
|
|
||||||
return "btn-outline-secondary";
|
|
||||||
};
|
|
||||||
|
|
||||||
makeRequest(
|
|
||||||
"/api/v1/packages",
|
|
||||||
{
|
|
||||||
query: {
|
|
||||||
architecture: repository.architecture,
|
|
||||||
repository: repository.repository,
|
|
||||||
},
|
|
||||||
convert: response => response.json(),
|
|
||||||
},
|
|
||||||
data => {
|
|
||||||
const payload = data.map(description => {
|
|
||||||
const package_base = description.package.base;
|
|
||||||
const web_url = description.package.remote.web_url;
|
|
||||||
return {
|
|
||||||
id: package_base,
|
|
||||||
base: web_url ? safeLink(web_url, package_base, package_base).outerHTML : safe(package_base),
|
|
||||||
version: safe(description.package.version),
|
|
||||||
packager: description.package.packager ? safe(description.package.packager) : "",
|
|
||||||
packages: listToTable(Object.keys(description.package.packages)),
|
|
||||||
groups: listToTable(extractListProperties(description.package, "groups")),
|
|
||||||
licenses: listToTable(extractListProperties(description.package, "licenses")),
|
|
||||||
timestamp: new Date(1000 * description.status.timestamp).toISOStringShort(),
|
|
||||||
status: description.status.status,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
table.bootstrapTable("load", payload);
|
|
||||||
table.bootstrapTable("uncheckAll");
|
|
||||||
table.bootstrapTable("hideLoading");
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
if ((error.status === 401) || (error.status === 403)) {
|
if ((error.status === 401) || (error.status === 403)) {
|
||||||
// authorization error
|
// authorization error
|
||||||
const text = "In order to see statuses you must login first.";
|
const text = "In order to see statuses you must login first.";
|
||||||
@ -126,79 +139,10 @@
|
|||||||
const message = details => `Could not load list of packages: ${details}`;
|
const message = details => `Could not load list of packages: ${details}`;
|
||||||
showFailure("Load failure", message, error);
|
showFailure("Load failure", message, error);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
makeRequest(
|
|
||||||
"/api/v1/status",
|
|
||||||
{
|
|
||||||
query: {
|
|
||||||
architecture: repository.architecture,
|
|
||||||
repository: repository.repository,
|
|
||||||
},
|
|
||||||
convert: response => response.json(),
|
|
||||||
},
|
|
||||||
data => {
|
|
||||||
versionBadge.innerHTML = `<i class="bi bi-github"></i> ahriman ${safe(data.version)}`;
|
|
||||||
|
|
||||||
dashboardButton.classList.remove(...dashboardButton.classList);
|
|
||||||
dashboardButton.classList.add("btn");
|
|
||||||
dashboardButton.classList.add(badgeClass(data.status.status));
|
|
||||||
|
|
||||||
dashboardModalHeader.classList.remove(...dashboardModalHeader.classList);
|
|
||||||
dashboardModalHeader.classList.add("modal-header");
|
|
||||||
headerClass(data.status.status).forEach(clz => dashboardModalHeader.classList.add(clz));
|
|
||||||
|
|
||||||
dashboardName.textContent = data.repository;
|
|
||||||
dashboardArchitecture.textContent = data.architecture;
|
|
||||||
dashboardStatus.textContent = data.status.status;
|
|
||||||
dashboardStatusTimestamp.textContent = new Date(1000 * data.status.timestamp).toISOStringShort();
|
|
||||||
|
|
||||||
if (dashboardPackagesStatusesChart) {
|
|
||||||
const labels = [
|
|
||||||
"unknown",
|
|
||||||
"pending",
|
|
||||||
"building",
|
|
||||||
"failed",
|
|
||||||
"success",
|
|
||||||
];
|
|
||||||
dashboardPackagesStatusesChart.config.data = {
|
|
||||||
labels: labels,
|
|
||||||
datasets: [{
|
|
||||||
label: "packages in status",
|
|
||||||
data: labels.map(label => data.packages[label]),
|
|
||||||
backgroundColor: [
|
|
||||||
"rgb(55, 58, 60)",
|
|
||||||
"rgb(255, 117, 24)",
|
|
||||||
"rgb(255, 117, 24)",
|
|
||||||
"rgb(255, 0, 57)",
|
|
||||||
"rgb(63, 182, 24)", // copy-paste from current style
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
};
|
};
|
||||||
dashboardPackagesStatusesChart.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dashboardPackagesCountChart) {
|
packagesLoad(onFailure);
|
||||||
dashboardPackagesCountChart.config.data = {
|
statusLoad();
|
||||||
labels: ["packages"],
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "archives",
|
|
||||||
data: [data.stats.packages],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "bases",
|
|
||||||
data: [data.stats.bases],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
dashboardPackagesCountChart.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
dashboardCanvas.hidden = data.status.total > 0;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectRepository() {
|
function selectRepository() {
|
||||||
@ -217,7 +161,24 @@
|
|||||||
return {classes: cellClass(value)};
|
return {classes: cellClass(value)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleTableAutoReload(interval) {
|
||||||
|
clearInterval(tableAutoReloadTask);
|
||||||
|
tableAutoReloadTask = toggleAutoReload(tableAutoReloadButton, interval, tableAutoReloadInput, _ => {
|
||||||
|
if (!hasActiveModal() &&
|
||||||
|
!hasActiveDropdown()) {
|
||||||
|
packagesLoad();
|
||||||
|
statusLoad();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
ready(_ => {
|
ready(_ => {
|
||||||
|
const onCheckFunction = function () {
|
||||||
|
if (packageRemoveButton) {
|
||||||
|
packageRemoveButton.disabled = !getSelection().length;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
document.querySelectorAll("#repositories a").forEach(element => {
|
document.querySelectorAll("#repositories a").forEach(element => {
|
||||||
element.onclick = _ => {
|
element.onclick = _ => {
|
||||||
repository = {
|
repository = {
|
||||||
@ -232,18 +193,16 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table", _ => {
|
table.bootstrapTable({
|
||||||
if (packageRemoveButton) {
|
onCheck: onCheckFunction,
|
||||||
packageRemoveButton.disabled = !table.bootstrapTable("getSelections").length;
|
onCheckAll: onCheckFunction,
|
||||||
}
|
onClickRow: (data, row, cell) => {
|
||||||
});
|
|
||||||
table.on("click-row.bs.table", (self, data, row, cell) => {
|
|
||||||
if (0 === cell || "base" === cell) {
|
if (0 === cell || "base" === cell) {
|
||||||
const method = data[0] === true ? "uncheckBy" : "checkBy"; // fck javascript
|
const method = data[0] === true ? "uncheckBy" : "checkBy"; // fck javascript
|
||||||
table.bootstrapTable(method, {field: "id", values: [data.id]});
|
table.bootstrapTable(method, {field: "id", values: [data.id]});
|
||||||
} else showPackageInfo(data.id);
|
} else showPackageInfo(data.id);
|
||||||
});
|
},
|
||||||
table.on("created-controls.bs.table", _ => {
|
onCreatedControls: _ => {
|
||||||
new easepick.create({
|
new easepick.create({
|
||||||
element: document.querySelector(".bootstrap-table-filter-control-timestamp"),
|
element: document.querySelector(".bootstrap-table-filter-control-timestamp"),
|
||||||
css: [
|
css: [
|
||||||
@ -273,8 +232,16 @@
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
onUncheck: onCheckFunction,
|
||||||
|
onUncheckAll: onCheckFunction,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
restoreAutoReloadSettings(tableAutoReloadButton, tableAutoReloadInput);
|
||||||
|
|
||||||
selectRepository();
|
selectRepository();
|
||||||
|
{% if autorefresh_intervals %}
|
||||||
|
toggleTableAutoReload();
|
||||||
|
{% endif %}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -53,8 +53,7 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
|
|||||||
data-show-search-clear-button="true"
|
data-show-search-clear-button="true"
|
||||||
data-sortable="true"
|
data-sortable="true"
|
||||||
data-sort-name="base"
|
data-sort-name="base"
|
||||||
data-sort-order="asc"
|
data-sort-order="asc">
|
||||||
data-toggle="table">
|
|
||||||
<thead class="table-primary">
|
<thead class="table-primary">
|
||||||
<tr>
|
<tr>
|
||||||
<th data-sortable="true" data-switchable="false" data-field="name" data-filter-control="input" data-filter-control-placeholder="(any package)">package</th>
|
<th data-sortable="true" data-switchable="false" data-field="name" data-filter-control="input" data-filter-control-placeholder="(any package)">package</th>
|
||||||
@ -128,7 +127,8 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
|
|||||||
}
|
}
|
||||||
|
|
||||||
ready(_ => {
|
ready(_ => {
|
||||||
table.on("created-controls.bs.table", _ => {
|
table.bootstrapTable({
|
||||||
|
onCreatedControls: _ => {
|
||||||
new easepick.create({
|
new easepick.create({
|
||||||
element: document.querySelector(".bootstrap-table-filter-control-timestamp"),
|
element: document.querySelector(".bootstrap-table-filter-control-timestamp"),
|
||||||
css: [
|
css: [
|
||||||
@ -158,6 +158,7 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,23 +1,24 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/js-md5@0.8.3/src/md5.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/js-md5@0.8.3/src/md5.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/tableexport.jquery.plugin@1.30.0/tableExport.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/tableexport.jquery.plugin@1.33.0/tableExport.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/bootstrap-table.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.24.1/dist/bootstrap-table.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/extensions/export/bootstrap-table-export.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.24.1/dist/extensions/export/bootstrap-table-export.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/extensions/resizable/bootstrap-table-resizable.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.24.1/dist/extensions/resizable/bootstrap-table-resizable.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/extensions/filter-control/bootstrap-table-filter-control.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.24.1/dist/extensions/filter-control/bootstrap-table-filter-control.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.24.1/dist/extensions/cookie/bootstrap-table-cookie.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.umd.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.10.0/build/highlight.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.5.0/dist/chart.umd.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function copyToClipboard(text, button) {
|
async function copyToClipboard(text, button) {
|
||||||
@ -58,6 +59,20 @@
|
|||||||
return value.includes(dataList[index].toLowerCase());
|
return value.includes(dataList[index].toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasActiveSelection() {
|
||||||
|
return !document.getSelection().isCollapsed; // not sure if it is a valid way, but I guess so
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasActiveDropdown() {
|
||||||
|
return Array.from(document.querySelectorAll(".dropdown-menu"))
|
||||||
|
.some(el => el.classList.contains("show"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasActiveModal() {
|
||||||
|
return Array.from(document.querySelectorAll(".modal"))
|
||||||
|
.some(el => el.classList.contains("show"));
|
||||||
|
}
|
||||||
|
|
||||||
function headerClass(status) {
|
function headerClass(status) {
|
||||||
if (status === "pending") return ["bg-warning"];
|
if (status === "pending") return ["bg-warning"];
|
||||||
if (status === "building") return ["bg-warning"];
|
if (status === "building") return ["bg-warning"];
|
||||||
@ -106,6 +121,12 @@
|
|||||||
.catch(error => onFailure && onFailure(error));
|
.catch(error => onFailure && onFailure(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readOptional(extractor, callback) {
|
||||||
|
for (let value = extractor(); !!value; value = null) {
|
||||||
|
callback(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function ready(fn) {
|
function ready(fn) {
|
||||||
if (document.readyState === "complete" || document.readyState === "interactive") {
|
if (document.readyState === "complete" || document.readyState === "interactive") {
|
||||||
setTimeout(fn, 1);
|
setTimeout(fn, 1);
|
||||||
@ -114,6 +135,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function restoreAutoReloadSettings(toggle, intervalSelector) {
|
||||||
|
readOptional(() => localStorage.getItem(`ahriman-${toggle.id}-refresh-enabled`), value => toggle.checked = value === "true");
|
||||||
|
readOptional(() => localStorage.getItem(`ahriman-${toggle.id}-refresh-interval`), value => toggleActiveElement(intervalSelector, "interval", value));
|
||||||
|
}
|
||||||
|
|
||||||
function safe(string) {
|
function safe(string) {
|
||||||
return String(string)
|
return String(string)
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
@ -133,7 +159,86 @@
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
Date.prototype.toISOStringShort = function() {
|
function toggleActiveElement(selector, dataType, value) {
|
||||||
|
const targetElement = selector.querySelector(`a[data-${dataType}="${value}"]`);
|
||||||
|
if (targetElement?.classList?.contains("active")) {
|
||||||
|
return; // element is already active, skip processing
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.from(selector.children).forEach(il => {
|
||||||
|
Array.from(il.children).forEach(el => el.classList.remove("active"));
|
||||||
|
});
|
||||||
|
targetElement?.classList?.add("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAutoReload(toggle, interval, intervalSelector, callback) {
|
||||||
|
if (interval) {
|
||||||
|
toggle.checked = true; // toggle reload
|
||||||
|
} else {
|
||||||
|
interval = intervalSelector.querySelector(".active")?.dataset?.interval; // find active element
|
||||||
|
}
|
||||||
|
|
||||||
|
let intervalId = null;
|
||||||
|
if (interval) {
|
||||||
|
if (toggle.checked) {
|
||||||
|
// refresh UI
|
||||||
|
toggleActiveElement(intervalSelector, "interval", interval);
|
||||||
|
// finally create timer task
|
||||||
|
intervalId = setInterval(callback, interval);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toggle.checked = false; // no active interval found, disable toggle
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem(`ahriman-${toggle.id}-refresh-enabled`, toggle.checked);
|
||||||
|
localStorage.setItem(`ahriman-${toggle.id}-refresh-interval`, interval);
|
||||||
|
return intervalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTable(table, rows) {
|
||||||
|
// instead of using load method here, we just update rows manually to avoid table reinitialization
|
||||||
|
const currentData = table.bootstrapTable("getData").reduce((accumulator, row) => {
|
||||||
|
accumulator[row.id] = row["0"];
|
||||||
|
return accumulator;
|
||||||
|
}, {});
|
||||||
|
// insert or update rows
|
||||||
|
rows.forEach(row => {
|
||||||
|
if (Object.hasOwn(currentData, row.id)) {
|
||||||
|
row["0"] = currentData[row.id]; // copy checkbox state
|
||||||
|
table.bootstrapTable("updateByUniqueId", {
|
||||||
|
id: row.id,
|
||||||
|
row: row,
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
table.bootstrapTable("insertRow", {index: 0, row: row});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// remove old rows
|
||||||
|
const newData = rows.map(value => value.id);
|
||||||
|
Object.keys(currentData).forEach(id => {
|
||||||
|
if (!newData.includes(id)) {
|
||||||
|
table.bootstrapTable("removeByUniqueId", id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.equals = function (right, comparator) {
|
||||||
|
let index = this.length;
|
||||||
|
if (index !== right.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (index--) {
|
||||||
|
if (!comparator(this[index], right[index])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Date.prototype.toISOStringShort = function () {
|
||||||
const pad = number => String(number).padStart(2, "0");
|
const pad = number => String(number).padStart(2, "0");
|
||||||
return `${this.getFullYear()}-${pad(this.getMonth() + 1)}-${pad(this.getDate())} ${pad(this.getHours())}:${pad(this.getMinutes())}:${pad(this.getSeconds())}`;
|
return `${this.getFullYear()}-${pad(this.getMonth() + 1)}-${pad(this.getDate())} ${pad(this.getHours())}:${pad(this.getMinutes())}:${pad(this.getSeconds())}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" crossorigin="anonymous" type="text/css">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/bootstrap-table.min.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.24.1/dist/bootstrap-table.min.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.2/dist/extensions/filter-control/bootstrap-table-filter-control.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-table@1.24.1/dist/extensions/filter-control/bootstrap-table-filter-control.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.3/dist/cosmo/bootstrap.min.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.7/dist/cosmo/bootstrap.min.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.10.0/build/styles/github.min.css" crossorigin="anonymous" type="text/css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/github.min.css" crossorigin="anonymous" type="text/css">
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.pre-scrollable {
|
.pre-scrollable {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
.TH AHRIMAN "1" "2025\-06\-16" "ahriman" "Generated Python Manual"
|
.TH AHRIMAN "1" "2025\-06\-29" "ahriman 2.19.0" "ArcH linux ReposItory MANager"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ahriman
|
ahriman \- ArcH linux ReposItory MANager
|
||||||
.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] [-V] [--wait-timeout WAIT_TIMEOUT] {add,aur-search,check,clean,config,config-validate,copy,daemon,help,help-commands-unsafe,help-updates,help-version,init,key-import,package-add,package-changes,package-changes-remove,package-copy,package-remove,package-status,package-status-remove,package-status-update,package-update,patch-add,patch-list,patch-remove,patch-set-add,rebuild,remove,remove-unknown,repo-backup,repo-check,repo-clean,repo-config,repo-config-validate,repo-create-keyring,repo-create-mirrorlist,repo-daemon,repo-init,repo-rebuild,repo-remove-unknown,repo-report,repo-restore,repo-setup,repo-sign,repo-statistics,repo-status-update,repo-sync,repo-tree,repo-triggers,repo-update,report,run,search,service-clean,service-config,service-config-validate,service-key-import,service-repositories,service-run,service-setup,service-shell,service-tree-migrate,setup,shell,sign,status,status-update,sync,update,user-add,user-list,user-remove,version,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] {add,aur-search,check,clean,config,config-validate,copy,daemon,help,help-commands-unsafe,help-updates,help-version,init,key-import,package-add,package-changes,package-changes-remove,package-copy,package-remove,package-status,package-status-remove,package-status-update,package-update,patch-add,patch-list,patch-remove,patch-set-add,rebuild,remove,remove-unknown,repo-backup,repo-check,repo-clean,repo-config,repo-config-validate,repo-create-keyring,repo-create-mirrorlist,repo-daemon,repo-init,repo-rebuild,repo-remove-unknown,repo-report,repo-restore,repo-setup,repo-sign,repo-statistics,repo-status-update,repo-sync,repo-tree,repo-triggers,repo-update,report,run,search,service-clean,service-config,service-config-validate,service-key-import,service-repositories,service-run,service-setup,service-shell,service-tree-migrate,setup,shell,sign,status,status-update,sync,update,user-add,user-list,user-remove,version,web} ...
|
||||||
|
|||||||
@ -58,23 +58,23 @@ web = [
|
|||||||
"aiohttp_cors",
|
"aiohttp_cors",
|
||||||
"aiohttp_jinja2",
|
"aiohttp_jinja2",
|
||||||
]
|
]
|
||||||
web_api-docs = [
|
web-auth = [
|
||||||
"ahriman[web]",
|
|
||||||
"aiohttp-apispec",
|
|
||||||
"setuptools", # required by aiohttp-apispec
|
|
||||||
]
|
|
||||||
web_auth = [
|
|
||||||
"ahriman[web]",
|
"ahriman[web]",
|
||||||
"aiohttp_session",
|
"aiohttp_session",
|
||||||
"aiohttp_security",
|
"aiohttp_security",
|
||||||
"cryptography",
|
"cryptography",
|
||||||
]
|
]
|
||||||
web_metrics = [
|
web-docs = [
|
||||||
|
"ahriman[web]",
|
||||||
|
"aiohttp-apispec",
|
||||||
|
"setuptools", # required by aiohttp-apispec
|
||||||
|
]
|
||||||
|
web-metrics = [
|
||||||
"ahriman[web]",
|
"ahriman[web]",
|
||||||
"aiohttp-openmetrics",
|
"aiohttp-openmetrics",
|
||||||
]
|
]
|
||||||
web_oauth2 = [
|
web-oauth2 = [
|
||||||
"ahriman[web_auth]",
|
"ahriman[web-auth]",
|
||||||
"aioauth-client",
|
"aioauth-client",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ services:
|
|||||||
AHRIMAN_OUTPUT: console
|
AHRIMAN_OUTPUT: console
|
||||||
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
||||||
AHRIMAN_PORT: 8080
|
AHRIMAN_PORT: 8080
|
||||||
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
||||||
AHRIMAN_REPOSITORY: ahriman-demo
|
AHRIMAN_REPOSITORY: ahriman-demo
|
||||||
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ services:
|
|||||||
AHRIMAN_OUTPUT: console
|
AHRIMAN_OUTPUT: console
|
||||||
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
||||||
AHRIMAN_PORT: 8080
|
AHRIMAN_PORT: 8080
|
||||||
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
||||||
AHRIMAN_REPOSITORY: ahriman-demo
|
AHRIMAN_REPOSITORY: ahriman-demo
|
||||||
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ services:
|
|||||||
AHRIMAN_OUTPUT: console
|
AHRIMAN_OUTPUT: console
|
||||||
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
||||||
AHRIMAN_PORT: 8080
|
AHRIMAN_PORT: 8080
|
||||||
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
||||||
AHRIMAN_REPOSITORY: ahriman-demo
|
AHRIMAN_REPOSITORY: ahriman-demo
|
||||||
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ services:
|
|||||||
AHRIMAN_OUTPUT: console
|
AHRIMAN_OUTPUT: console
|
||||||
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
||||||
AHRIMAN_PORT: 8080
|
AHRIMAN_PORT: 8080
|
||||||
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
||||||
AHRIMAN_REPOSITORY: ahriman-demo
|
AHRIMAN_REPOSITORY: ahriman-demo
|
||||||
AHRIMAN_REPOSITORY_SERVER: http://frontend/repo/$$repo/$$arch
|
AHRIMAN_REPOSITORY_SERVER: http://frontend/repo/$$repo/$$arch
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ services:
|
|||||||
AHRIMAN_PACMAN_MIRROR: https://de.mirror.archlinux32.org/$$arch/$$repo
|
AHRIMAN_PACMAN_MIRROR: https://de.mirror.archlinux32.org/$$arch/$$repo
|
||||||
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
||||||
AHRIMAN_PORT: 8080
|
AHRIMAN_PORT: 8080
|
||||||
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
||||||
AHRIMAN_REPOSITORY: ahriman-demo
|
AHRIMAN_REPOSITORY: ahriman-demo
|
||||||
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
||||||
|
|
||||||
|
|||||||
@ -8,8 +8,8 @@ services:
|
|||||||
AHRIMAN_OUTPUT: console
|
AHRIMAN_OUTPUT: console
|
||||||
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
||||||
AHRIMAN_PORT: 8080
|
AHRIMAN_PORT: 8080
|
||||||
AHRIMAN_POSTSETUP_COMMAND: ahriman --architecture x86_64 --repository another-demo service-setup --build-as-user ahriman --packager 'ahriman bot <ahriman@example.com>'
|
AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
||||||
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
AHRIMAN_PRESETUP_COMMAND: ahriman --architecture x86_64 --repository another-demo service-setup --build-as-user ahriman --packager 'ahriman bot <ahriman@example.com>'
|
||||||
AHRIMAN_REPOSITORY: ahriman-demo
|
AHRIMAN_REPOSITORY: ahriman-demo
|
||||||
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ services:
|
|||||||
AHRIMAN_OAUTH_CLIENT_SECRET: ${AHRIMAN_OAUTH_CLIENT_SECRET}
|
AHRIMAN_OAUTH_CLIENT_SECRET: ${AHRIMAN_OAUTH_CLIENT_SECRET}
|
||||||
AHRIMAN_OUTPUT: console
|
AHRIMAN_OUTPUT: console
|
||||||
AHRIMAN_PORT: 8080
|
AHRIMAN_PORT: 8080
|
||||||
AHRIMAN_PRESETUP_COMMAND: sudo -u ahriman ahriman user-add ${AHRIMAN_OAUTH_USER} -R full -p ""
|
AHRIMAN_POSTSETUP_COMMAND: sudo -u ahriman ahriman user-add ${AHRIMAN_OAUTH_USER} -R full -p ""
|
||||||
AHRIMAN_REPOSITORY: ahriman-demo
|
AHRIMAN_REPOSITORY: ahriman-demo
|
||||||
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AHRIMAN_DEBUG: yes
|
AHRIMAN_DEBUG: yes
|
||||||
AHRIMAN_OUTPUT: console
|
AHRIMAN_OUTPUT: console
|
||||||
AHRIMAN_PRESETUP_COMMAND: sudo -u ahriman gpg --import /run/secrets/key
|
AHRIMAN_POSTSETUP_COMMAND: sudo -u ahriman gpg --import /run/secrets/key
|
||||||
AHRIMAN_REPOSITORY: ahriman-demo
|
AHRIMAN_REPOSITORY: ahriman-demo
|
||||||
|
|
||||||
configs:
|
configs:
|
||||||
|
|||||||
@ -8,7 +8,7 @@ services:
|
|||||||
AHRIMAN_OUTPUT: console
|
AHRIMAN_OUTPUT: console
|
||||||
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
AHRIMAN_PASSWORD: ${AHRIMAN_PASSWORD}
|
||||||
AHRIMAN_PORT: 8080
|
AHRIMAN_PORT: 8080
|
||||||
AHRIMAN_PRESETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
AHRIMAN_POSTSETUP_COMMAND: (cat /run/secrets/password; echo; cat /run/secrets/password) | sudo -u ahriman ahriman user-add demo -R full
|
||||||
AHRIMAN_REPOSITORY: ahriman-demo
|
AHRIMAN_REPOSITORY: ahriman-demo
|
||||||
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman.sock
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -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.18.3"
|
__version__ = "2.19.0"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -133,18 +133,18 @@ class Application(ApplicationPackages, ApplicationRepository):
|
|||||||
if not process_dependencies or not packages:
|
if not process_dependencies or not packages:
|
||||||
return packages
|
return packages
|
||||||
|
|
||||||
def missing_dependencies(source: Iterable[Package]) -> dict[str, str | None]:
|
def missing_dependencies(sources: Iterable[Package]) -> dict[str, str | None]:
|
||||||
# append list of known packages with packages which are in current sources
|
# append list of known packages with packages which are in current sources
|
||||||
satisfied_packages = known_packages | {
|
satisfied_packages = known_packages | {
|
||||||
single
|
single
|
||||||
for package in source
|
for source in sources
|
||||||
for single in package.packages_full
|
for single in source.packages_full
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dependency: package.packager
|
dependency: source.packager
|
||||||
for package in source
|
for source in sources
|
||||||
for dependency in package.depends_build
|
for dependency in source.depends_build
|
||||||
if dependency not in satisfied_packages
|
if dependency not in satisfied_packages
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ class Application(ApplicationPackages, ApplicationRepository):
|
|||||||
# there is local cache, load package from it
|
# there is local cache, load package from it
|
||||||
leaf = Package.from_build(source_dir, self.repository.architecture, packager)
|
leaf = Package.from_build(source_dir, self.repository.architecture, packager)
|
||||||
else:
|
else:
|
||||||
leaf = Package.from_aur(package_name, packager)
|
leaf = Package.from_aur(package_name, packager, include_provides=True)
|
||||||
portion[leaf.base] = leaf
|
portion[leaf.base] = leaf
|
||||||
|
|
||||||
# register package in the database
|
# register package in the database
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
70
src/ahriman/application/handlers/reload.py
Normal file
70
src/ahriman/application/handlers/reload.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2026 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.handler import Handler, SubParserAction
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
|
|
||||||
|
class Reload(Handler):
|
||||||
|
"""
|
||||||
|
web server reload handler
|
||||||
|
"""
|
||||||
|
|
||||||
|
ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
|
||||||
|
|
||||||
|
@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
|
||||||
|
client.configuration_reload()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _set_web_reload_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
|
"""
|
||||||
|
add parser for web reload subcommand
|
||||||
|
|
||||||
|
Args:
|
||||||
|
root(SubParserAction): subparsers for the commands
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
argparse.ArgumentParser: created argument parser
|
||||||
|
"""
|
||||||
|
parser = root.add_parser("web-reload", help="reload configuration",
|
||||||
|
description="reload web server configuration",
|
||||||
|
epilog="This method forces the web server to reload its configuration. "
|
||||||
|
"Note, however, that this method does not apply all configuration changes "
|
||||||
|
"(like ports, authentication, etc)")
|
||||||
|
parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
arguments = [_set_web_reload_parser]
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -28,6 +28,7 @@ from ahriman.core.alpm.remote import AUR, Official
|
|||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.exceptions import OptionError
|
from ahriman.core.exceptions import OptionError
|
||||||
from ahriman.core.formatters import AurPrinter
|
from ahriman.core.formatters import AurPrinter
|
||||||
|
from ahriman.core.types import Comparable
|
||||||
from ahriman.models.aur_package import AURPackage
|
from ahriman.models.aur_package import AURPackage
|
||||||
from ahriman.models.repository_id import RepositoryId
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
@ -115,7 +116,7 @@ class Search(Handler):
|
|||||||
raise OptionError(sort_by)
|
raise OptionError(sort_by)
|
||||||
# always sort by package name at the last
|
# always sort by package name at the last
|
||||||
# well technically it is not a string, but we can deal with it
|
# well technically it is not a string, but we can deal with it
|
||||||
comparator: Callable[[AURPackage], tuple[str, str]] =\
|
comparator: Callable[[AURPackage], Comparable] = \
|
||||||
lambda package: (getattr(package, sort_by), package.name)
|
lambda package: (getattr(package, sort_by), package.name)
|
||||||
return sorted(packages, key=comparator)
|
return sorted(packages, key=comparator)
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -72,6 +72,7 @@ class Setup(Handler):
|
|||||||
|
|
||||||
application = Application(repository_id, configuration, report=report)
|
application = Application(repository_id, configuration, report=report)
|
||||||
|
|
||||||
|
with application.repository.paths.preserve_owner():
|
||||||
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths)
|
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths)
|
||||||
Setup.executable_create(application.repository.paths, repository_id)
|
Setup.executable_create(application.repository.paths, repository_id)
|
||||||
repository_server = f"file://{application.repository.paths.repository}" if args.server is None else args.server
|
repository_server = f"file://{application.repository.paths.repository}" if args.server is None else args.server
|
||||||
@ -280,6 +281,5 @@ class Setup(Handler):
|
|||||||
command = Setup.build_command(paths.root, repository_id)
|
command = Setup.build_command(paths.root, repository_id)
|
||||||
command.unlink(missing_ok=True)
|
command.unlink(missing_ok=True)
|
||||||
command.symlink_to(Setup.ARCHBUILD_COMMAND_PATH)
|
command.symlink_to(Setup.ARCHBUILD_COMMAND_PATH)
|
||||||
paths.chown(command) # we would like to keep owner inside ahriman's home
|
|
||||||
|
|
||||||
arguments = [_set_service_setup_parser]
|
arguments = [_set_service_setup_parser]
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -25,6 +25,7 @@ from ahriman.application.application import Application
|
|||||||
from ahriman.application.handlers.handler import Handler, SubParserAction
|
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.formatters import PackagePrinter, StatusPrinter
|
from ahriman.core.formatters import PackagePrinter, StatusPrinter
|
||||||
|
from ahriman.core.types import Comparable
|
||||||
from ahriman.core.utils import enum_values
|
from ahriman.core.utils import enum_values
|
||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
@ -64,7 +65,7 @@ class Status(Handler):
|
|||||||
|
|
||||||
Status.check_status(args.exit_code, packages)
|
Status.check_status(args.exit_code, packages)
|
||||||
|
|
||||||
comparator: Callable[[tuple[Package, BuildStatus]], str] = lambda item: item[0].base
|
comparator: Callable[[tuple[Package, BuildStatus]], Comparable] = lambda item: item[0].base
|
||||||
filter_fn: Callable[[tuple[Package, BuildStatus]], bool] =\
|
filter_fn: Callable[[tuple[Package, BuildStatus]], bool] =\
|
||||||
lambda item: args.status is None or item[1].status == args.status
|
lambda item: args.status is None or item[1].status == args.status
|
||||||
for package, package_status in sorted(filter(filter_fn, packages), key=comparator):
|
for package, package_status in sorted(filter(filter_fn, packages), key=comparator):
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -52,7 +52,7 @@ class Validate(Handler):
|
|||||||
"""
|
"""
|
||||||
from ahriman.core.configuration.validator import Validator
|
from ahriman.core.configuration.validator import Validator
|
||||||
|
|
||||||
schema = Validate.schema(repository_id, configuration)
|
schema = Validate.schema(configuration)
|
||||||
validator = Validator(configuration=configuration, schema=schema)
|
validator = Validator(configuration=configuration, schema=schema)
|
||||||
|
|
||||||
if validator.validate(configuration.dump()):
|
if validator.validate(configuration.dump()):
|
||||||
@ -83,12 +83,11 @@ class Validate(Handler):
|
|||||||
return parser
|
return parser
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def schema(repository_id: RepositoryId, configuration: Configuration) -> ConfigurationSchema:
|
def schema(configuration: Configuration) -> ConfigurationSchema:
|
||||||
"""
|
"""
|
||||||
get schema with triggers
|
get schema with triggers
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
repository_id(RepositoryId): repository unique identifier
|
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -107,12 +106,12 @@ class Validate(Handler):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# default settings if any
|
# default settings if any
|
||||||
for schema_name, schema in trigger_class.configuration_schema(repository_id, None).items():
|
for schema_name, schema in trigger_class.configuration_schema(None).items():
|
||||||
erased = Validate.schema_erase_required(copy.deepcopy(schema))
|
erased = Validate.schema_erase_required(copy.deepcopy(schema))
|
||||||
root[schema_name] = Validate.schema_merge(root.get(schema_name, {}), erased)
|
root[schema_name] = Validate.schema_merge(root.get(schema_name, {}), erased)
|
||||||
|
|
||||||
# settings according to enabled triggers
|
# settings according to enabled triggers
|
||||||
for schema_name, schema in trigger_class.configuration_schema(repository_id, configuration).items():
|
for schema_name, schema in trigger_class.configuration_schema(configuration).items():
|
||||||
root[schema_name] = Validate.schema_merge(root.get(schema_name, {}), copy.deepcopy(schema))
|
root[schema_name] = Validate.schema_merge(root.get(schema_name, {}), copy.deepcopy(schema))
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -130,8 +130,8 @@ class Pacman(LazyLogging):
|
|||||||
return # database for some reason deos not exist
|
return # database for some reason deos not exist
|
||||||
|
|
||||||
self.logger.info("copy pacman database %s from operating system root to ahriman's home %s", src, dst)
|
self.logger.info("copy pacman database %s from operating system root to ahriman's home %s", src, dst)
|
||||||
|
with self.repository_paths.preserve_owner(dst.parent):
|
||||||
shutil.copy(src, dst)
|
shutil.copy(src, dst)
|
||||||
self.repository_paths.chown(dst)
|
|
||||||
|
|
||||||
def database_init(self, handle: Handle, repository: str, architecture: str) -> DB:
|
def database_init(self, handle: Handle, repository: str, architecture: str) -> DB:
|
||||||
"""
|
"""
|
||||||
@ -255,3 +255,20 @@ class Pacman(LazyLogging):
|
|||||||
result.update(trim_package(provides) for provides in package.provides)
|
result.update(trim_package(provides) for provides in package.provides)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def provided_by(self, package_name: str) -> Generator[Package, None, None]:
|
||||||
|
"""
|
||||||
|
search through databases and emit packages which provides the ``package_name``
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_name(str): package name to search
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
Package: list of packages which were returned by the query
|
||||||
|
"""
|
||||||
|
def is_package_provided(package: Package) -> bool:
|
||||||
|
provides = [trim_package(name) for name in package.provides]
|
||||||
|
return package_name in provides
|
||||||
|
|
||||||
|
for database in self.handle.get_syncdbs():
|
||||||
|
yield from filter(is_package_provided, database.search(package_name))
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -97,20 +97,17 @@ class AUR(Remote):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: response parsed to package list
|
list[AURPackage]: response parsed to package list
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
PackageInfoError: if multiple arguments are passed
|
||||||
"""
|
"""
|
||||||
query: list[tuple[str, str]] = [
|
if len(args) != 1:
|
||||||
("type", request_type),
|
raise PackageInfoError("AUR API requires exactly one argument to search")
|
||||||
("v", self.DEFAULT_RPC_VERSION),
|
|
||||||
]
|
|
||||||
|
|
||||||
arg_query = "arg[]" if len(args) > 1 else "arg"
|
url = f"{self.DEFAULT_RPC_URL}/v{self.DEFAULT_RPC_VERSION}/{request_type}/{args[0]}"
|
||||||
for arg in args:
|
query = list(kwargs.items())
|
||||||
query.append((arg_query, arg))
|
|
||||||
|
|
||||||
for key, value in kwargs.items():
|
response = self.make_request("GET", url, params=query)
|
||||||
query.append((key, value))
|
|
||||||
|
|
||||||
response = self.make_request("GET", self.DEFAULT_RPC_URL, params=query)
|
|
||||||
return self.parse_response(response.json())
|
return self.parse_response(response.json())
|
||||||
|
|
||||||
def package_info(self, package_name: str, *, pacman: Pacman | None) -> AURPackage:
|
def package_info(self, package_name: str, *, pacman: Pacman | None) -> AURPackage:
|
||||||
@ -133,15 +130,36 @@ class AUR(Remote):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise UnknownPackageError(package_name) from None
|
raise UnknownPackageError(package_name) from None
|
||||||
|
|
||||||
def package_search(self, *keywords: str, pacman: Pacman | None) -> list[AURPackage]:
|
def package_provided_by(self, package_name: str, *, pacman: Pacman | None) -> list[AURPackage]:
|
||||||
|
"""
|
||||||
|
get package list which provide the specified package name
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_name(str): package name to search
|
||||||
|
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[AURPackage]: list of packages which match the criteria
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
package
|
||||||
|
# search api provides reduced models
|
||||||
|
for stub in self.package_search(package_name, pacman=pacman, search_by="provides")
|
||||||
|
# verity that found package actually provides it
|
||||||
|
if package_name in (package := self.package_info(stub.name, pacman=pacman)).provides
|
||||||
|
]
|
||||||
|
|
||||||
|
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
search package in AUR web
|
search package in AUR web
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*keywords(str): keywords to search
|
*keywords(str): keywords to search
|
||||||
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
||||||
|
search_by(str | None): search by keywords
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages which match the criteria
|
list[AURPackage]: list of packages which match the criteria
|
||||||
"""
|
"""
|
||||||
return self.aur_request("search", *keywords, by="name-desc")
|
search_by = search_by or "name-desc"
|
||||||
|
return self.aur_request("search", *keywords, by=search_by)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -127,15 +127,17 @@ class Official(Remote):
|
|||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise UnknownPackageError(package_name) from None
|
raise UnknownPackageError(package_name) from None
|
||||||
|
|
||||||
def package_search(self, *keywords: str, pacman: Pacman | None) -> list[AURPackage]:
|
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
search package in AUR web
|
search package in AUR web
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*keywords(str): keywords to search
|
*keywords(str): keywords to search
|
||||||
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
||||||
|
search_by(str | None): search by keywords
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages which match the criteria
|
list[AURPackage]: list of packages which match the criteria
|
||||||
"""
|
"""
|
||||||
return self.arch_request(*keywords, by="q")
|
search_by = search_by or "q"
|
||||||
|
return self.arch_request(*keywords, by=search_by)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -59,3 +59,22 @@ class OfficialSyncdb(Official):
|
|||||||
return next(AURPackage.from_pacman(package) for package in pacman.package(package_name))
|
return next(AURPackage.from_pacman(package) for package in pacman.package(package_name))
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise UnknownPackageError(package_name) from None
|
raise UnknownPackageError(package_name) from None
|
||||||
|
|
||||||
|
def package_provided_by(self, package_name: str, *, pacman: Pacman | None) -> list[AURPackage]:
|
||||||
|
"""
|
||||||
|
get package list which provide the specified package name
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_name(str): package name to search
|
||||||
|
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[AURPackage]: list of packages which match the criteria
|
||||||
|
"""
|
||||||
|
if pacman is None:
|
||||||
|
return []
|
||||||
|
|
||||||
|
return [
|
||||||
|
AURPackage.from_pacman(package)
|
||||||
|
for package in pacman.provided_by(package_name)
|
||||||
|
]
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
@ -18,6 +18,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
from ahriman.core.alpm.pacman import Pacman
|
||||||
|
from ahriman.core.exceptions import UnknownPackageError
|
||||||
from ahriman.core.http import SyncHttpClient
|
from ahriman.core.http import SyncHttpClient
|
||||||
from ahriman.models.aur_package import AURPackage
|
from ahriman.models.aur_package import AURPackage
|
||||||
|
|
||||||
@ -41,22 +42,36 @@ class Remote(SyncHttpClient):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def info(cls, package_name: str, *, pacman: Pacman | None = None) -> AURPackage:
|
def info(cls, package_name: str, *, pacman: Pacman | None = None, include_provides: bool = False) -> AURPackage:
|
||||||
"""
|
"""
|
||||||
get package info by its name
|
get package info by its name. If ``include_provides`` is set to ``True``, then, in addition, this method
|
||||||
|
will perform search by :attr:`ahriman.models.aur_package.AURPackage.provides` and return first package found.
|
||||||
|
Note, however, that in this case some implementation might not provide this method and search result will might
|
||||||
|
not be stable
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
package_name(str): package name to search
|
package_name(str): package name to search
|
||||||
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
||||||
(Default value = None)
|
(Default value = None)
|
||||||
|
include_provides(bool, optional): search by provides if no exact match found (Default value = False)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
AURPackage: package which match the package name
|
AURPackage: package which match the package name
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
UnknownPackageError: if requested package not found
|
||||||
"""
|
"""
|
||||||
return cls().package_info(package_name, pacman=pacman)
|
instance = cls()
|
||||||
|
try:
|
||||||
|
return instance.package_info(package_name, pacman=pacman)
|
||||||
|
except UnknownPackageError:
|
||||||
|
if include_provides and (provided_by := instance.package_provided_by(package_name, pacman=pacman)):
|
||||||
|
return next(iter(provided_by))
|
||||||
|
raise
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def multisearch(cls, *keywords: str, pacman: Pacman | None = None) -> list[AURPackage]:
|
def multisearch(cls, *keywords: str, pacman: Pacman | None = None,
|
||||||
|
search_by: str | None = None) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
search in remote repository by using API with multiple words. This method is required in order to handle
|
search in remote repository by using API with multiple words. This method is required in order to handle
|
||||||
https://bugs.archlinux.org/task/49133. In addition, short words will be dropped
|
https://bugs.archlinux.org/task/49133. In addition, short words will be dropped
|
||||||
@ -65,6 +80,7 @@ class Remote(SyncHttpClient):
|
|||||||
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
||||||
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
||||||
(Default value = None)
|
(Default value = None)
|
||||||
|
search_by(str | None, optional): search by keywords (Default value = None)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages each of them matches all search terms
|
list[AURPackage]: list of packages each of them matches all search terms
|
||||||
@ -72,12 +88,21 @@ class Remote(SyncHttpClient):
|
|||||||
instance = cls()
|
instance = cls()
|
||||||
packages: dict[str, AURPackage] = {}
|
packages: dict[str, AURPackage] = {}
|
||||||
for term in filter(lambda word: len(word) >= 3, keywords):
|
for term in filter(lambda word: len(word) >= 3, keywords):
|
||||||
portion = instance.search(term, pacman=pacman)
|
portion = instance.package_search(term, pacman=pacman, search_by=search_by)
|
||||||
packages = {
|
packages = {
|
||||||
package.name: package # not mistake to group them by name
|
package.name: package # not mistake to group them by name
|
||||||
for package in portion
|
for package in portion
|
||||||
if package.name in packages or not packages
|
if package.name in packages or not packages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# simple check for duplicates. This method will remove all packages under base if there is
|
||||||
|
# a package named exactly as its base
|
||||||
|
packages = {
|
||||||
|
package.name: package
|
||||||
|
for package in packages.values()
|
||||||
|
if package.package_base not in packages or package.package_base == package.name
|
||||||
|
}
|
||||||
|
|
||||||
return list(packages.values())
|
return list(packages.values())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -114,7 +139,7 @@ class Remote(SyncHttpClient):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search(cls, *keywords: str, pacman: Pacman | None = None) -> list[AURPackage]:
|
def search(cls, *keywords: str, pacman: Pacman | None = None, search_by: str | None = None) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
search package in AUR web
|
search package in AUR web
|
||||||
|
|
||||||
@ -122,11 +147,12 @@ class Remote(SyncHttpClient):
|
|||||||
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
*keywords(str): search terms, e.g. "ahriman", "is", "cool"
|
||||||
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None, optional): alpm wrapper instance, required for official repositories search
|
||||||
(Default value = None)
|
(Default value = None)
|
||||||
|
search_by(str | None, optional): search by keywords (Default value = None)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages which match the criteria
|
list[AURPackage]: list of packages which match the criteria
|
||||||
"""
|
"""
|
||||||
return cls().package_search(*keywords, pacman=pacman)
|
return cls().package_search(*keywords, pacman=pacman, search_by=search_by)
|
||||||
|
|
||||||
def package_info(self, package_name: str, *, pacman: Pacman | None) -> AURPackage:
|
def package_info(self, package_name: str, *, pacman: Pacman | None) -> AURPackage:
|
||||||
"""
|
"""
|
||||||
@ -144,13 +170,28 @@ class Remote(SyncHttpClient):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def package_search(self, *keywords: str, pacman: Pacman | None) -> list[AURPackage]:
|
def package_provided_by(self, package_name: str, *, pacman: Pacman | None) -> list[AURPackage]:
|
||||||
|
"""
|
||||||
|
get package list which provide the specified package name
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_name(str): package name to search
|
||||||
|
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[AURPackage]: list of packages which match the criteria
|
||||||
|
"""
|
||||||
|
del package_name, pacman
|
||||||
|
return []
|
||||||
|
|
||||||
|
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
search package in AUR web
|
search package in AUR web
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
*keywords(str): keywords to search
|
*keywords(str): keywords to search
|
||||||
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
pacman(Pacman | None): alpm wrapper instance, required for official repositories search
|
||||||
|
search_by(str | None): search by keywords
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[AURPackage]: list of packages which match the criteria
|
list[AURPackage]: list of packages which match the criteria
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2021-2025 ahriman team.
|
# Copyright (c) 2021-2026 ahriman team.
|
||||||
#
|
#
|
||||||
# This file is part of ahriman
|
# This file is part of ahriman
|
||||||
# (see https://github.com/arcan1s/ahriman).
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user