mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-01-15 18:53:43 +00:00
Compare commits
39 Commits
7d32bc1217
...
feature/tr
| Author | SHA1 | Date | |
|---|---|---|---|
| 401d7caf1c | |||
| 53524483a1 | |||
| b670943507 | |||
| 950e9c0a07 | |||
| c429b7427b | |||
| 66c962cbfd | |||
| 29e9ed62d6 | |||
| 0a8d34217f | |||
| 0b69a1bb77 | |||
| ca18ac936d | |||
| 9f25e7a6ef | |||
| 8a1722a1ea | |||
| 0afec3fa6e | |||
| f63b61f413 | |||
| 4574ee7685 | |||
| 19eb0e19e9 | |||
| c366c4289c | |||
| 46af782db2 | |||
| 6443e02352 | |||
| 999ad39d6f | |||
| dfab5f56b2 | |||
| 10798b9ba3 | |||
| 358e3dc4d2 | |||
| c13cd029bc | |||
| ae32cc8fbb | |||
| dff5b775a9 | |||
| db3f20546e | |||
| 53368468a4 | |||
| 228c2cce51 | |||
| f5aec4e5c1 | |||
| 9217c8c759 | |||
| 6392520e06 | |||
| c6306631e6 | |||
| 97b906c536 | |||
| 435375721d | |||
| 4c5caba6b7 | |||
| b83df9d2c5 | |||
| f2ea76aab9 | |||
| 471b1c1331 |
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
|
||||||
|
|||||||
@ -165,6 +165,11 @@ Again, the most checks can be performed by `tox` command, though some additional
|
|||||||
|
|
||||||
# Blank line again and package imports
|
# Blank line again and package imports
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
|
# Multiline import example
|
||||||
|
from ahriman.core.database.operations import (
|
||||||
|
AuthOperations,
|
||||||
|
BuildOperations,
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
* One file should define only one class, exception is class satellites in case if file length remains less than 400 lines.
|
* One file should define only one class, exception is class satellites in case if file length remains less than 400 lines.
|
||||||
|
|||||||
@ -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
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
|
|||||||
29
docs/ahriman.core.archive.rst
Normal file
29
docs/ahriman.core.archive.rst
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
ahriman.core.archive package
|
||||||
|
============================
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
ahriman.core.archive.archive\_tree module
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.archive.archive_tree
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.core.archive.archive\_trigger module
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.archive.archive_trigger
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.archive
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@ -132,6 +132,14 @@ ahriman.core.database.migrations.m015\_logs\_process\_id module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.core.database.migrations.m016\_archive module
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.database.migrations.m016_archive
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
Module contents
|
Module contents
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|||||||
29
docs/ahriman.core.housekeeping.rst
Normal file
29
docs/ahriman.core.housekeeping.rst
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
ahriman.core.housekeeping package
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
ahriman.core.housekeeping.archive\_rotation\_trigger module
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.core.housekeeping.archive_rotation_trigger
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
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:
|
||||||
@ -8,6 +8,7 @@ Subpackages
|
|||||||
:maxdepth: 4
|
:maxdepth: 4
|
||||||
|
|
||||||
ahriman.core.alpm
|
ahriman.core.alpm
|
||||||
|
ahriman.core.archive
|
||||||
ahriman.core.auth
|
ahriman.core.auth
|
||||||
ahriman.core.build_tools
|
ahriman.core.build_tools
|
||||||
ahriman.core.configuration
|
ahriman.core.configuration
|
||||||
@ -15,6 +16,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
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
@ -96,6 +97,13 @@ libalpm and AUR related configuration. Group name can refer to architecture, e.g
|
|||||||
* ``sync_files_database`` - download files database from mirror, boolean, required.
|
* ``sync_files_database`` - download files database from mirror, boolean, required.
|
||||||
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands). If set to ``no``, databases must be synchronized manually.
|
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands). If set to ``no``, databases must be synchronized manually.
|
||||||
|
|
||||||
|
``archive`` group
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Describes settings for packages archives management extensions.
|
||||||
|
|
||||||
|
* ``keep_built_packages`` - keep this amount of built packages with different versions, integer, required. ``0`` (or negative number) will effectively disable archives removal.
|
||||||
|
|
||||||
``auth`` group
|
``auth`` group
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
@ -138,6 +146,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
|
||||||
@ -180,7 +190,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.
|
||||||
|
|
||||||
@ -198,6 +208,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
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
|||||||
@ -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,13 @@ 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
|
||||||
|
triggers[] = ahriman.core.housekeeping.ArchiveRotationTrigger
|
||||||
; 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.ArchiveRotationTrigger
|
||||||
|
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,7 @@
|
|||||||
|
[archive]
|
||||||
|
; Keep amount of last built packages in archive. 0 means keep all packages
|
||||||
|
keep_built_packages = 1
|
||||||
|
|
||||||
|
[logs-rotation]
|
||||||
|
; Keep last build logs for each package
|
||||||
|
keep_last_logs = 5
|
||||||
@ -1,5 +1,6 @@
|
|||||||
[build]
|
[build]
|
||||||
; List of well-known triggers. Used only for configuration purposes.
|
; List of well-known triggers. Used only for configuration purposes.
|
||||||
|
triggers_known[] = ahriman.core.archive.ArchiveTrigger
|
||||||
triggers_known[] = ahriman.core.distributed.WorkerLoaderTrigger
|
triggers_known[] = ahriman.core.distributed.WorkerLoaderTrigger
|
||||||
triggers_known[] = ahriman.core.distributed.WorkerTrigger
|
triggers_known[] = ahriman.core.distributed.WorkerTrigger
|
||||||
triggers_known[] = ahriman.core.support.KeyringTrigger
|
triggers_known[] = ahriman.core.support.KeyringTrigger
|
||||||
|
|||||||
@ -99,6 +99,9 @@
|
|||||||
|
|
||||||
<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"
|
||||||
@ -117,7 +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-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",
|
||||||
|
|||||||
@ -148,8 +148,19 @@
|
|||||||
|
|
||||||
packageAddInput.addEventListener("keyup", _ => {
|
packageAddInput.addEventListener("keyup", _ => {
|
||||||
clearTimeout(packageAddInput.requestTimeout);
|
clearTimeout(packageAddInput.requestTimeout);
|
||||||
|
|
||||||
|
// do not update datalist if search string didn't change yet
|
||||||
|
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(_ => {
|
packageAddInput.requestTimeout = setTimeout(_ => {
|
||||||
const value = packageAddInput.value;
|
|
||||||
|
|
||||||
if (value.length >= 3) {
|
if (value.length >= 3) {
|
||||||
makeRequest(
|
makeRequest(
|
||||||
|
|||||||
@ -97,7 +97,7 @@
|
|||||||
<input id="package-info-refresh-input" type="checkbox" class="form-check-input" value="" checked>
|
<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>
|
<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()" data-bs-dismiss="modal"><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button>
|
<button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()"><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 %}
|
{% if autorefresh_intervals %}
|
||||||
@ -315,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`,
|
||||||
{
|
{
|
||||||
@ -440,29 +503,6 @@
|
|||||||
packagesAdd(packageBase, [], repository, {refresh: packageInfoRefreshInput.checked});
|
packagesAdd(packageBase, [], repository, {refresh: packageInfoRefreshInput.checked});
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadActiveLogs(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 showPackageInfo(packageBase) {
|
function showPackageInfo(packageBase) {
|
||||||
const isPackageBaseSet = packageBase !== undefined;
|
const isPackageBaseSet = packageBase !== undefined;
|
||||||
if (isPackageBaseSet) {
|
if (isPackageBaseSet) {
|
||||||
@ -502,7 +542,7 @@
|
|||||||
const packageBase = packageInfoModal.dataset.package;
|
const packageBase = packageInfoModal.dataset.package;
|
||||||
// we only poll status and logs here
|
// we only poll status and logs here
|
||||||
loadPackage(packageBase);
|
loadPackage(packageBase);
|
||||||
reloadActiveLogs(packageBase);
|
loadLogs(packageBase);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,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`;
|
||||||
@ -90,136 +125,24 @@
|
|||||||
doPackageAction("/api/v1/service/update", [], repository, onSuccess, onFailure, parameters);
|
doPackageAction("/api/v1/service/update", [], repository, onSuccess, onFailure, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
function reload(silent) {
|
function reload() {
|
||||||
if (!silent) {
|
table.bootstrapTable("showLoading");
|
||||||
table.bootstrapTable("showLoading");
|
const onFailure = error => {
|
||||||
}
|
if ((error.status === 401) || (error.status === 403)) {
|
||||||
|
// authorization error
|
||||||
const badgeClass = status => {
|
const text = "In order to see statuses you must login first.";
|
||||||
if (status === "pending") return "btn-outline-warning";
|
table.find("tr.unauthorized").remove();
|
||||||
if (status === "building") return "btn-outline-warning";
|
table.find("tbody").append(`<tr class="unauthorized"><td colspan="100%">${safe(text)}</td></tr>`);
|
||||||
if (status === "failed") return "btn-outline-danger";
|
table.bootstrapTable("hideLoading");
|
||||||
if (status === "success") return "btn-outline-success";
|
} else {
|
||||||
return "btn-outline-secondary";
|
// other errors
|
||||||
|
const message = details => `Could not load list of packages: ${details}`;
|
||||||
|
showFailure("Load failure", message, error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
makeRequest(
|
packagesLoad(onFailure);
|
||||||
"/api/v1/packages",
|
statusLoad();
|
||||||
{
|
|
||||||
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 (!silent) {
|
|
||||||
if ((error.status === 401) || (error.status === 403)) {
|
|
||||||
// authorization error
|
|
||||||
const text = "In order to see statuses you must login first.";
|
|
||||||
table.find("tr.unauthorized").remove();
|
|
||||||
table.find("tbody").append(`<tr class="unauthorized"><td colspan="100%">${safe(text)}</td></tr>`);
|
|
||||||
table.bootstrapTable("hideLoading");
|
|
||||||
} else {
|
|
||||||
// other errors
|
|
||||||
const message = details => `Could not load list of packages: ${details}`;
|
|
||||||
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) {
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectRepository() {
|
function selectRepository() {
|
||||||
@ -241,10 +164,10 @@
|
|||||||
function toggleTableAutoReload(interval) {
|
function toggleTableAutoReload(interval) {
|
||||||
clearInterval(tableAutoReloadTask);
|
clearInterval(tableAutoReloadTask);
|
||||||
tableAutoReloadTask = toggleAutoReload(tableAutoReloadButton, interval, tableAutoReloadInput, _ => {
|
tableAutoReloadTask = toggleAutoReload(tableAutoReloadButton, interval, tableAutoReloadInput, _ => {
|
||||||
if ((getSelection().length === 0) &&
|
if (!hasActiveModal() &&
|
||||||
(table.bootstrapTable("getOptions").pageNumber === 1) &&
|
!hasActiveDropdown()) {
|
||||||
(!dashboardModal.classList.contains("show"))) {
|
packagesLoad();
|
||||||
reload(true);
|
statusLoad();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -310,7 +233,6 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onPageChange: (_, size) => { Cookies.set(`ahriman-${table.attr("id")}-page-size`, size); },
|
|
||||||
onUncheck: onCheckFunction,
|
onUncheck: onCheckFunction,
|
||||||
onUncheckAll: onCheckFunction,
|
onUncheckAll: onCheckFunction,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,25 +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 src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js" crossorigin="anonymous" type="application/javascript"></script>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
async function copyToClipboard(text, button) {
|
async function copyToClipboard(text, button) {
|
||||||
@ -64,6 +63,16 @@
|
|||||||
return !document.getSelection().isCollapsed; // not sure if it is a valid way, but I guess so
|
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"];
|
||||||
@ -112,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);
|
||||||
@ -121,16 +136,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function restoreAutoReloadSettings(toggle, intervalSelector) {
|
function restoreAutoReloadSettings(toggle, intervalSelector) {
|
||||||
for (let refreshEnabled = Cookies.get(`ahriman-${toggle.id}-refresh-enabled`);
|
readOptional(() => localStorage.getItem(`ahriman-${toggle.id}-refresh-enabled`), value => toggle.checked = value === "true");
|
||||||
!!refreshEnabled;
|
readOptional(() => localStorage.getItem(`ahriman-${toggle.id}-refresh-interval`), value => toggleActiveElement(intervalSelector, "interval", value));
|
||||||
refreshEnabled = undefined) {
|
|
||||||
toggle.checked = refreshEnabled === "true";
|
|
||||||
}
|
|
||||||
for (let refreshInterval = Cookies.get(`ahriman-${toggle.id}-refresh-interval`);
|
|
||||||
!!refreshInterval;
|
|
||||||
refreshInterval = undefined) {
|
|
||||||
toggleActiveElement(intervalSelector, "interval", refreshInterval);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function safe(string) {
|
function safe(string) {
|
||||||
@ -183,12 +190,55 @@
|
|||||||
toggle.checked = false; // no active interval found, disable toggle
|
toggle.checked = false; // no active interval found, disable toggle
|
||||||
}
|
}
|
||||||
|
|
||||||
Cookies.set(`ahriman-${toggle.id}-refresh-enabled`, toggle.checked);
|
localStorage.setItem(`ahriman-${toggle.id}-refresh-enabled`, toggle.checked);
|
||||||
Cookies.set(`ahriman-${toggle.id}-refresh-interval`, interval);
|
localStorage.setItem(`ahriman-${toggle.id}-refresh-interval`, interval);
|
||||||
return intervalId;
|
return intervalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
Date.prototype.toISOStringShort = function() {
|
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 {
|
||||||
|
|||||||
@ -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).
|
||||||
|
|||||||
@ -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).
|
||||||
|
|||||||
@ -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,16 +72,17 @@ class Setup(Handler):
|
|||||||
|
|
||||||
application = Application(repository_id, configuration, report=report)
|
application = Application(repository_id, configuration, report=report)
|
||||||
|
|
||||||
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths)
|
with application.repository.paths.preserve_owner():
|
||||||
Setup.executable_create(application.repository.paths, repository_id)
|
Setup.configuration_create_makepkg(args.packager, args.makeflags_jobs, application.repository.paths)
|
||||||
repository_server = f"file://{application.repository.paths.repository}" if args.server is None else args.server
|
Setup.executable_create(application.repository.paths, repository_id)
|
||||||
Setup.configuration_create_devtools(
|
repository_server = f"file://{application.repository.paths.repository}" if args.server is None else args.server
|
||||||
repository_id, args.from_configuration, args.mirror, args.multilib, repository_server)
|
Setup.configuration_create_devtools(
|
||||||
Setup.configuration_create_sudo(application.repository.paths, repository_id)
|
repository_id, args.from_configuration, args.mirror, args.multilib, repository_server)
|
||||||
|
Setup.configuration_create_sudo(application.repository.paths, repository_id)
|
||||||
|
|
||||||
application.repository.repo.init()
|
application.repository.repo.init()
|
||||||
# lazy database sync
|
# lazy database sync
|
||||||
application.repository.pacman.handle # pylint: disable=pointless-statement
|
application.repository.pacman.handle # pylint: disable=pointless-statement
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _set_service_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
def _set_service_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
@ -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,8 +65,8 @@ 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):
|
||||||
PackagePrinter(package, package_status)(verbose=args.info)
|
PackagePrinter(package, package_status)(verbose=args.info)
|
||||||
|
|||||||
@ -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).
|
||||||
@ -21,6 +21,7 @@ import argparse
|
|||||||
|
|
||||||
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.utils import walk
|
||||||
from ahriman.models.repository_id import RepositoryId
|
from ahriman.models.repository_id import RepositoryId
|
||||||
from ahriman.models.repository_paths import RepositoryPaths
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ class TreeMigrate(Handler):
|
|||||||
target_tree.tree_create()
|
target_tree.tree_create()
|
||||||
# perform migration
|
# perform migration
|
||||||
TreeMigrate.tree_move(current_tree, target_tree)
|
TreeMigrate.tree_move(current_tree, target_tree)
|
||||||
|
TreeMigrate.fix_symlinks(target_tree)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _set_service_tree_migrate_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
def _set_service_tree_migrate_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
@ -66,6 +68,22 @@ class TreeMigrate(Handler):
|
|||||||
parser.set_defaults(lock=None, quiet=True, report=False)
|
parser.set_defaults(lock=None, quiet=True, report=False)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fix_symlinks(paths: RepositoryPaths) -> None:
|
||||||
|
"""
|
||||||
|
fix packages archives symlinks
|
||||||
|
|
||||||
|
Args:
|
||||||
|
paths(RepositoryPaths): new repository paths
|
||||||
|
"""
|
||||||
|
archives = {path.name: path for path in walk(paths.archive)}
|
||||||
|
for symlink in walk(paths.repository):
|
||||||
|
if symlink.exists(): # no need to check for symlinks as we have just walked through the tree
|
||||||
|
continue
|
||||||
|
if (source_archive := archives.get(symlink.name)) is not None:
|
||||||
|
symlink.unlink()
|
||||||
|
symlink.symlink_to(source_archive.relative_to(symlink.parent, walk_up=True))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def tree_move(from_tree: RepositoryPaths, to_tree: RepositoryPaths) -> None:
|
def tree_move(from_tree: RepositoryPaths, to_tree: RepositoryPaths) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -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)
|
||||||
shutil.copy(src, dst)
|
with self.repository_paths.preserve_owner(dst.parent):
|
||||||
self.repository_paths.chown(dst)
|
shutil.copy(src, dst)
|
||||||
|
|
||||||
def database_init(self, handle: Handle, repository: str, architecture: str) -> DB:
|
def database_init(self, handle: Handle, repository: str, architecture: str) -> DB:
|
||||||
"""
|
"""
|
||||||
@ -267,7 +267,8 @@ class Pacman(LazyLogging):
|
|||||||
Package: list of packages which were returned by the query
|
Package: list of packages which were returned by the query
|
||||||
"""
|
"""
|
||||||
def is_package_provided(package: Package) -> bool:
|
def is_package_provided(package: Package) -> bool:
|
||||||
return package_name in package.provides
|
provides = [trim_package(name) for name in package.provides]
|
||||||
|
return package_name in provides
|
||||||
|
|
||||||
for database in self.handle.get_syncdbs():
|
for database in self.handle.get_syncdbs():
|
||||||
yield from filter(is_package_provided, database.search(package_name))
|
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).
|
||||||
@ -44,7 +44,7 @@ class AUR(Remote):
|
|||||||
"""
|
"""
|
||||||
generate remote git url from the package base
|
generate remote git url from the package base
|
||||||
|
|
||||||
Args
|
Args:
|
||||||
package_base(str): package base
|
package_base(str): package base
|
||||||
repository(str): repository name
|
repository(str): repository name
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class AUR(Remote):
|
|||||||
"""
|
"""
|
||||||
generate remote web url from the package base
|
generate remote web url from the package base
|
||||||
|
|
||||||
Args
|
Args:
|
||||||
package_base(str): package base
|
package_base(str): package base
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -146,7 +146,7 @@ class AUR(Remote):
|
|||||||
# search api provides reduced models
|
# search api provides reduced models
|
||||||
for stub in self.package_search(package_name, pacman=pacman, search_by="provides")
|
for stub in self.package_search(package_name, pacman=pacman, search_by="provides")
|
||||||
# verity that found package actually provides it
|
# verity that found package actually provides it
|
||||||
if package_name in (package := self.package_info(stub.package_base, pacman=pacman)).provides
|
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]:
|
def package_search(self, *keywords: str, pacman: Pacman | None, search_by: str | None) -> list[AURPackage]:
|
||||||
|
|||||||
@ -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).
|
||||||
@ -46,7 +46,7 @@ class Official(Remote):
|
|||||||
"""
|
"""
|
||||||
generate remote git url from the package base
|
generate remote git url from the package base
|
||||||
|
|
||||||
Args
|
Args:
|
||||||
package_base(str): package base
|
package_base(str): package base
|
||||||
repository(str): repository name
|
repository(str): repository name
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ class Official(Remote):
|
|||||||
"""
|
"""
|
||||||
generate remote web url from the package base
|
generate remote web url from the package base
|
||||||
|
|
||||||
Args
|
Args:
|
||||||
package_base(str): package base
|
package_base(str): package base
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|||||||
@ -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).
|
||||||
@ -110,7 +110,7 @@ class Remote(SyncHttpClient):
|
|||||||
"""
|
"""
|
||||||
generate remote git url from the package base
|
generate remote git url from the package base
|
||||||
|
|
||||||
Args
|
Args:
|
||||||
package_base(str): package base
|
package_base(str): package base
|
||||||
repository(str): repository name
|
repository(str): repository name
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ class Remote(SyncHttpClient):
|
|||||||
"""
|
"""
|
||||||
generate remote web url from the package base
|
generate remote web url from the package base
|
||||||
|
|
||||||
Args
|
Args:
|
||||||
package_base(str): package base
|
package_base(str): package base
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|||||||
@ -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).
|
||||||
@ -31,20 +31,21 @@ class Repo(LazyLogging):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
name(str): repository name
|
name(str): repository name
|
||||||
paths(RepositoryPaths): repository paths instance
|
root(Path): repository root
|
||||||
sign_args(list[str]): additional args which have to be used to sign repository archive
|
sign_args(list[str]): additional args which have to be used to sign repository archive
|
||||||
uid(int): uid of the repository owner user
|
uid(int): uid of the repository owner user
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str, paths: RepositoryPaths, sign_args: list[str]) -> None:
|
def __init__(self, name: str, paths: RepositoryPaths, sign_args: list[str], root: Path | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
name(str): repository name
|
name(str): repository name
|
||||||
paths(RepositoryPaths): repository paths instance
|
paths(RepositoryPaths): repository paths instance
|
||||||
sign_args(list[str]): additional args which have to be used to sign repository archive
|
sign_args(list[str]): additional args which have to be used to sign repository archive
|
||||||
|
root(Path | None, optional): repository root. If none set, the default will be used (Default value = None)
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
self.paths = paths
|
self.root = root or paths.repository
|
||||||
self.uid, _ = paths.root_owner
|
self.uid, _ = paths.root_owner
|
||||||
self.sign_args = sign_args
|
self.sign_args = sign_args
|
||||||
|
|
||||||
@ -56,45 +57,56 @@ class Repo(LazyLogging):
|
|||||||
Returns:
|
Returns:
|
||||||
Path: path to repository database
|
Path: path to repository database
|
||||||
"""
|
"""
|
||||||
return self.paths.repository / f"{self.name}.db.tar.gz"
|
return self.root / f"{self.name}.db.tar.gz"
|
||||||
|
|
||||||
def add(self, path: Path) -> None:
|
def add(self, path: Path, *, remove: bool = True) -> None:
|
||||||
"""
|
"""
|
||||||
add new package to repository
|
add new package to repository
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path(Path): path to archive to add
|
path(Path): path to archive to add
|
||||||
|
remove(bool, optional): whether to remove old packages or not (Default value = True)
|
||||||
"""
|
"""
|
||||||
|
command = ["repo-add", *self.sign_args]
|
||||||
|
if remove:
|
||||||
|
command.extend(["--remove"])
|
||||||
|
command.extend([str(self.repo_path), str(path)])
|
||||||
|
|
||||||
|
# add to repository
|
||||||
check_output(
|
check_output(
|
||||||
"repo-add", *self.sign_args, "-R", str(self.repo_path), str(path),
|
*command,
|
||||||
exception=BuildError.from_process(path.name),
|
exception=BuildError.from_process(path.name),
|
||||||
cwd=self.paths.repository,
|
cwd=self.root,
|
||||||
logger=self.logger,
|
logger=self.logger,
|
||||||
user=self.uid)
|
user=self.uid,
|
||||||
|
)
|
||||||
|
|
||||||
def init(self) -> None:
|
def init(self) -> None:
|
||||||
"""
|
"""
|
||||||
create empty repository database. It just calls add with empty arguments
|
create empty repository database. It just calls add with empty arguments
|
||||||
"""
|
"""
|
||||||
check_output("repo-add", *self.sign_args, str(self.repo_path),
|
check_output("repo-add", *self.sign_args, str(self.repo_path),
|
||||||
cwd=self.paths.repository, logger=self.logger, user=self.uid)
|
cwd=self.root, logger=self.logger, user=self.uid)
|
||||||
|
|
||||||
def remove(self, package: str, filename: Path) -> None:
|
def remove(self, package_name: str | None, filename: Path) -> None:
|
||||||
"""
|
"""
|
||||||
remove package from repository
|
remove package from repository
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
package(str): package name to remove
|
package_name(str | None): package name to remove. If none set, it will be guessed from filename
|
||||||
filename(Path): package filename to remove
|
filename(Path): package filename to remove
|
||||||
"""
|
"""
|
||||||
|
package_name = package_name or filename.name.rsplit("-", maxsplit=3)[0]
|
||||||
|
|
||||||
# remove package and signature (if any) from filesystem
|
# remove package and signature (if any) from filesystem
|
||||||
for full_path in self.paths.repository.glob(f"{filename}*"):
|
for full_path in self.root.glob(f"**/{filename.name}*"):
|
||||||
full_path.unlink()
|
full_path.unlink()
|
||||||
|
|
||||||
# remove package from registry
|
# remove package from registry
|
||||||
check_output(
|
check_output(
|
||||||
"repo-remove", *self.sign_args, str(self.repo_path), package,
|
"repo-remove", *self.sign_args, str(self.repo_path), package_name,
|
||||||
exception=BuildError.from_process(package),
|
exception=BuildError.from_process(package_name),
|
||||||
cwd=self.paths.repository,
|
cwd=self.root,
|
||||||
logger=self.logger,
|
logger=self.logger,
|
||||||
user=self.uid)
|
user=self.uid,
|
||||||
|
)
|
||||||
|
|||||||
20
src/ahriman/core/archive/__init__.py
Normal file
20
src/ahriman/core/archive/__init__.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2025 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
from ahriman.core.archive.archive_trigger import ArchiveTrigger
|
||||||
131
src/ahriman/core/archive/archive_tree.py
Normal file
131
src/ahriman/core/archive/archive_tree.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2025 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 datetime
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ahriman.core.alpm.repo import Repo
|
||||||
|
from ahriman.core.log import LazyLogging
|
||||||
|
from ahriman.core.utils import utcnow, walk
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
|
|
||||||
|
class ArchiveTree(LazyLogging):
|
||||||
|
"""
|
||||||
|
wrapper around archive tree
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
paths(RepositoryPaths): repository paths instance
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
sign_args(list[str]): additional args which have to be used to sign repository archive
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, repository_path: RepositoryPaths, sign_args: list[str]) -> None:
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
repository_path(RepositoryPaths): repository paths instance
|
||||||
|
sign_args(list[str]): additional args which have to be used to sign repository archive
|
||||||
|
"""
|
||||||
|
self.paths = repository_path
|
||||||
|
self.repository_id = repository_path.repository_id
|
||||||
|
self.sign_args = sign_args
|
||||||
|
|
||||||
|
def repository_for(self, date: datetime.date | None = None) -> Path:
|
||||||
|
"""
|
||||||
|
get full path to repository at the specified date
|
||||||
|
|
||||||
|
Args:
|
||||||
|
date(datetime.date | None, optional): date to generate path. If none supplied then today will be used
|
||||||
|
(Default value = None)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path: path to the repository root
|
||||||
|
"""
|
||||||
|
date = date or utcnow().date()
|
||||||
|
return (
|
||||||
|
self.paths.archive
|
||||||
|
/ "repos"
|
||||||
|
/ date.strftime("%Y")
|
||||||
|
/ date.strftime("%m")
|
||||||
|
/ date.strftime("%d")
|
||||||
|
/ self.repository_id.name
|
||||||
|
/ self.repository_id.architecture
|
||||||
|
)
|
||||||
|
|
||||||
|
def symlinks_create(self, packages: list[Package]) -> None:
|
||||||
|
"""
|
||||||
|
create symlinks for the specified packages in today's repository
|
||||||
|
|
||||||
|
Args:
|
||||||
|
packages(list[Package]): list of packages to be updated
|
||||||
|
"""
|
||||||
|
root = self.repository_for()
|
||||||
|
repo = Repo(self.repository_id.name, self.paths, self.sign_args, root)
|
||||||
|
|
||||||
|
for package in packages:
|
||||||
|
archive = self.paths.archive_for(package.base)
|
||||||
|
|
||||||
|
for package_name, single in package.packages.items():
|
||||||
|
if single.filename is None:
|
||||||
|
self.logger.warning("received empty package filename for %s", package_name)
|
||||||
|
continue
|
||||||
|
|
||||||
|
has_file = False
|
||||||
|
for file in archive.glob(f"{single.filename}*"):
|
||||||
|
symlink = root / file.name
|
||||||
|
try:
|
||||||
|
symlink.symlink_to(file.relative_to(symlink.parent, walk_up=True))
|
||||||
|
has_file = True
|
||||||
|
except FileExistsError:
|
||||||
|
continue # symlink is already created, skip processing
|
||||||
|
|
||||||
|
if has_file:
|
||||||
|
repo.add(root / single.filename)
|
||||||
|
|
||||||
|
def symlinks_fix(self) -> None:
|
||||||
|
"""
|
||||||
|
remove broken symlinks across repositories for all dates
|
||||||
|
"""
|
||||||
|
for path in walk(self.paths.archive / "repos"):
|
||||||
|
root = path.parent
|
||||||
|
*_, name, architecture = root.parts
|
||||||
|
if self.repository_id.name != name or self.repository_id.architecture != architecture:
|
||||||
|
continue # we only process same name repositories
|
||||||
|
|
||||||
|
if not path.is_symlink():
|
||||||
|
continue # find symlinks only
|
||||||
|
if path.exists():
|
||||||
|
continue # filter out not broken symlinks
|
||||||
|
|
||||||
|
Repo(self.repository_id.name, self.paths, self.sign_args, root).remove(None, path)
|
||||||
|
|
||||||
|
def tree_create(self) -> None:
|
||||||
|
"""
|
||||||
|
create repository tree for current repository
|
||||||
|
"""
|
||||||
|
root = self.repository_for()
|
||||||
|
if root.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
with self.paths.preserve_owner(self.paths.archive):
|
||||||
|
root.mkdir(0o755, parents=True)
|
||||||
|
# init empty repository here
|
||||||
|
Repo(self.repository_id.name, self.paths, self.sign_args, root).init()
|
||||||
69
src/ahriman/core/archive/archive_trigger.py
Normal file
69
src/ahriman/core/archive/archive_trigger.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2025 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
from ahriman.core.archive.archive_tree import ArchiveTree
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.sign.gpg import GPG
|
||||||
|
from ahriman.core.triggers import Trigger
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
from ahriman.models.result import Result
|
||||||
|
|
||||||
|
|
||||||
|
class ArchiveTrigger(Trigger):
|
||||||
|
"""
|
||||||
|
archive repository extension
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
paths(RepositoryPaths): repository paths instance
|
||||||
|
tree(ArchiveTree): archive tree wrapper
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, repository_id: RepositoryId, configuration: Configuration) -> None:
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
|
"""
|
||||||
|
Trigger.__init__(self, repository_id, configuration)
|
||||||
|
|
||||||
|
self.paths = configuration.repository_paths
|
||||||
|
self.tree = ArchiveTree(self.paths, GPG(configuration).repository_sign_args)
|
||||||
|
|
||||||
|
def on_result(self, result: Result, packages: list[Package]) -> None:
|
||||||
|
"""
|
||||||
|
run trigger
|
||||||
|
|
||||||
|
Args:
|
||||||
|
result(Result): build result
|
||||||
|
packages(list[Package]): list of all available packages
|
||||||
|
"""
|
||||||
|
self.tree.symlinks_create(packages)
|
||||||
|
|
||||||
|
def on_start(self) -> None:
|
||||||
|
"""
|
||||||
|
trigger action which will be called at the start of the application
|
||||||
|
"""
|
||||||
|
self.tree.tree_create()
|
||||||
|
|
||||||
|
def on_stop(self) -> None:
|
||||||
|
"""
|
||||||
|
trigger action which will be called before the stop of the application
|
||||||
|
"""
|
||||||
|
self.tree.symlinks_fix()
|
||||||
@ -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