mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 15:27:17 +00:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
d283dccc1e | |||
8a4e900ab9 | |||
fa6cf8ce36 | |||
a706fbb751 | |||
9a23f5c79d | |||
aaab9069bf | |||
f00b575641 | |||
6f57ed550b | |||
08640d9108 | |||
65324633b4 | |||
ed67898012 | |||
a1a8dd68e8 | |||
a9505386c2 | |||
a07b20bf50 | |||
ed70897c39 | |||
0423c3e67c | |||
571f62327f | |||
286ff4bcef | |||
0660c33de3 | |||
c8421e97ee | |||
bc2288afc1 | |||
503c8b0355 | |||
6738f9206d | |||
f865e998b0 | |||
4880ca4fee |
2
.github/workflows/setup.sh
vendored
2
.github/workflows/setup.sh
vendored
@ -18,7 +18,7 @@ if [[ -z $MINIMAL_INSTALL ]]; then
|
||||
# web server
|
||||
pacman -S --noconfirm python-aioauth-client python-aiohttp python-aiohttp-apispec-git python-aiohttp-cors python-aiohttp-jinja2 python-aiohttp-security python-aiohttp-session python-cryptography python-jinja
|
||||
# additional features
|
||||
pacman -S --noconfirm gnupg python-boto3 python-cerberus python-matplotlib rsync
|
||||
pacman -S --noconfirm gnupg ipython python-boto3 python-cerberus python-matplotlib rsync
|
||||
fi
|
||||
# FIXME since 1.0.4 devtools requires dbus to be run, which doesn't work now in container
|
||||
cp "docker/systemd-nspawn.sh" "/usr/local/bin/systemd-nspawn"
|
||||
|
@ -305,7 +305,7 @@ max-branches=12
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
max-parents=15
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
@ -80,7 +80,7 @@ Again, the most checks can be performed by `tox` command, though some additional
|
||||
>>> clazz = Clazz()
|
||||
"""
|
||||
|
||||
CLAZZ_ATTRIBUTE = 42
|
||||
CLAZZ_ATTRIBUTE: ClassVar[int] = 42
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""
|
||||
@ -96,6 +96,7 @@ Again, the most checks can be performed by `tox` command, though some additional
|
||||
* Type annotations are the must, even for local functions. For the function argument `self` (for instance methods) and `cls` (for class methods) should not be annotated.
|
||||
* For collection types built-in classes must be used if possible (e.g. `dict` instead of `typing.Dict`, `tuple` instead of `typing.Tuple`). In case if built-in type is not available, but `collections.abc` provides interface, it must be used (e.g. `collections.abc.Awaitable` instead of `typing.Awaitable`, `collections.abc.Iterable` instead of `typing.Iterable`). For union classes, the bar operator (`|`) must be used (e.g. `float | int` instead of `typing.Union[float, int]`), which also includes `typing.Optional` (e.g. `str | None` instead of `Optional[str]`).
|
||||
* `classmethod` should (almost) always return `Self`. In case of mypy warning (e.g. if there is a branch in which function doesn't return the instance of `cls`) consider using `staticmethod` instead.
|
||||
* Class attributes must be decorated as `ClassVar[...]`.
|
||||
* Recommended order of function definitions in class:
|
||||
|
||||
```python
|
||||
@ -175,11 +176,10 @@ Again, the most checks can be performed by `tox` command, though some additional
|
||||
* Web API methods must be documented by using `aiohttp_apispec` library. The schema testing mostly should be implemented in related view class tests. Recommended example for documentation (excluding comments):
|
||||
|
||||
```python
|
||||
import aiohttp_apispec
|
||||
|
||||
from marshmallow import Schema, fields
|
||||
|
||||
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PaginationSchema
|
||||
|
||||
from ahriman.web.apispec.decorators import apidocs
|
||||
from ahriman.web.schemas import PackageNameSchema, PaginationSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@ -198,25 +198,17 @@ Again, the most checks can be performed by `tox` command, though some additional
|
||||
POST_PERMISSION = ...
|
||||
ROUTES = ...
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
@apidocs(
|
||||
tags=["Tag"],
|
||||
summary="Do foo",
|
||||
description="Extended description of the method which does foo",
|
||||
responses={
|
||||
200: {"description": "Success response", "schema": ResponseSchema},
|
||||
204: {"description": "Success response"}, # example without json schema response
|
||||
400: {"description": "Bad data is supplied", "schema": ErrorSchema}, # exception raised by this method
|
||||
401: {"description": "Authorization required", "schema": ErrorSchema}, # should be always presented
|
||||
403: {"description": "Access is forbidden", "schema": ErrorSchema}, # should be always presented
|
||||
404: {"description": "Repository is unknown", "schema": ErrorSchema}, # include if BaseView.service() method is called
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema}, # should be always presented
|
||||
},
|
||||
security=[{"token": [POST_PERMISSION]}],
|
||||
error_400_enabled=True, # exception raised by this method
|
||||
error_404_description="Repository is unknown",
|
||||
schema=ResponseSchema, # leave empty if no responses here
|
||||
match_schema=PackageNameSchema,
|
||||
query_schema=PaginationSchema,
|
||||
body_schema=RequestSchema(many=True),
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema) # should be always presented
|
||||
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||
@aiohttp_apispec.querystring_schema(PaginationSchema)
|
||||
@aiohttp_apispec.json_schema(RequestSchema(many=True))
|
||||
async def post(self) -> None: ...
|
||||
```
|
||||
|
||||
|
@ -108,9 +108,7 @@ RUN cp "/etc/pacman.d/mirrorlist" "/etc/pacman.d/mirrorlist.orig" && \
|
||||
sed -i "s/SigLevel *=.*/SigLevel = Optional/g" "/etc/pacman.conf" && \
|
||||
pacman -Sy
|
||||
## install package and its optional dependencies
|
||||
RUN pacman -S --noconfirm \
|
||||
--assume-installed python-aiohttp-apispec=3.0.0 \
|
||||
ahriman
|
||||
RUN pacman -S --noconfirm ahriman
|
||||
RUN pacman -S --noconfirm --asdeps \
|
||||
python-aioauth-client \
|
||||
python-aiohttp-apispec-git \
|
||||
|
3632
docs/_static/architecture.dot
vendored
3632
docs/_static/architecture.dot
vendored
File diff suppressed because it is too large
Load Diff
@ -29,6 +29,14 @@ ahriman.application.help\_formatter module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.interactive\_shell module
|
||||
---------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.application.interactive_shell
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.application.lock module
|
||||
-------------------------------
|
||||
|
||||
|
@ -124,6 +124,14 @@ ahriman.core.database.migrations.m014\_auditlog module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.database.migrations.m015\_logs\_process\_id module
|
||||
---------------------------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.database.migrations.m015_logs_process_id
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
|
@ -92,6 +92,14 @@ ahriman.core.formatters.repository\_printer module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.formatters.repository\_stats\_printer module
|
||||
---------------------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.core.formatters.repository_stats_printer
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.core.formatters.status\_printer module
|
||||
----------------------------------------------
|
||||
|
||||
|
@ -100,6 +100,14 @@ ahriman.models.log\_handler module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.log\_record module
|
||||
---------------------------------
|
||||
|
||||
.. automodule:: ahriman.models.log_record
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.log\_record\_id module
|
||||
-------------------------------------
|
||||
|
||||
@ -236,6 +244,14 @@ ahriman.models.repository\_paths module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.repository\_stats module
|
||||
---------------------------------------
|
||||
|
||||
.. automodule:: ahriman.models.repository_stats
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.result module
|
||||
----------------------------
|
||||
|
||||
@ -252,6 +268,14 @@ ahriman.models.scan\_paths module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.series\_statistics module
|
||||
----------------------------------------
|
||||
|
||||
.. automodule:: ahriman.models.series_statistics
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.models.sign\_settings module
|
||||
------------------------------------
|
||||
|
||||
|
29
docs/ahriman.web.apispec.rst
Normal file
29
docs/ahriman.web.apispec.rst
Normal file
@ -0,0 +1,29 @@
|
||||
ahriman.web.apispec package
|
||||
===========================
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
ahriman.web.apispec.decorators module
|
||||
-------------------------------------
|
||||
|
||||
.. automodule:: ahriman.web.apispec.decorators
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.apispec.info module
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: ahriman.web.apispec.info
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: ahriman.web.apispec
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
@ -7,6 +7,7 @@ Subpackages
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
ahriman.web.apispec
|
||||
ahriman.web.middlewares
|
||||
ahriman.web.schemas
|
||||
ahriman.web.views
|
||||
@ -14,14 +15,6 @@ Subpackages
|
||||
Submodules
|
||||
----------
|
||||
|
||||
ahriman.web.apispec module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: ahriman.web.apispec
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.cors module
|
||||
-----------------------
|
||||
|
||||
|
@ -116,6 +116,14 @@ ahriman.web.schemas.login\_schema module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.schemas.logs\_rotate\_schema module
|
||||
-----------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.web.schemas.logs_rotate_schema
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.schemas.logs\_schema module
|
||||
---------------------------------------
|
||||
|
||||
@ -260,6 +268,14 @@ ahriman.web.schemas.repository\_id\_schema module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.schemas.repository\_stats\_schema module
|
||||
----------------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.web.schemas.repository_stats_schema
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.schemas.search\_schema module
|
||||
-----------------------------------------
|
||||
|
||||
@ -284,14 +300,6 @@ ahriman.web.schemas.update\_flags\_schema module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.schemas.versioned\_log\_schema module
|
||||
-------------------------------------------------
|
||||
|
||||
.. automodule:: ahriman.web.schemas.versioned_log_schema
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.schemas.worker\_schema module
|
||||
-----------------------------------------
|
||||
|
||||
|
@ -12,6 +12,14 @@ ahriman.web.views.v1.service.add module
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.views.v1.service.logs module
|
||||
----------------------------------------
|
||||
|
||||
.. automodule:: ahriman.web.views.v1.service.logs
|
||||
:members:
|
||||
:no-undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
ahriman.web.views.v1.service.pgp module
|
||||
---------------------------------------
|
||||
|
||||
|
@ -81,6 +81,7 @@ 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.
|
||||
* ``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.
|
||||
* ``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.
|
||||
|
||||
``alpm:*`` groups
|
||||
@ -217,7 +218,7 @@ Mirrorlist generator plugin
|
||||
``remote-pull`` group
|
||||
---------------------
|
||||
|
||||
Remote git source synchronization settings. Unlike ``Upload`` triggers those triggers are used for PKGBUILD synchronization - fetch from remote repository PKGBUILDs before updating process.
|
||||
Remote git source synchronization settings. Unlike ``upload`` triggers those triggers are used for PKGBUILD synchronization - fetch from remote repository PKGBUILDs before updating process.
|
||||
|
||||
It supports authorization; to do so you'd need to prefix the URL with authorization part, e.g. ``https://key:token@github.com/arcan1s/ahriman.git``. It is highly recommended to use application tokens instead of your user authorization details. Alternatively, you can use any other option supported by git, e.g.:
|
||||
|
||||
|
@ -56,6 +56,13 @@ Though originally I've created ahriman by trying to improve the project, it stil
|
||||
|
||||
It is automation tools for ``repoctl`` mentioned above. Except for using shell it looks pretty cool and also offers some additional features like patches, remote synchronization (isn't it?) and reporting.
|
||||
|
||||
`AURCache <https://github.com/Lukas-Heiligenbrunner/AURCache>`__
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
That's really cool project if you are looking for simple service to build AUR packages. It provides very informative dashboard and easy to configure and use. However, it doesn't provide direct way to control build process (e.g. it is neither trivial to build packages for architectures which are not supported by default nor to change build flags).
|
||||
|
||||
Also this application relies on docker setup (e.g. builders are only available as special docker containers). In addition, it uses ``paru`` to build packages instead of ``devtools``.
|
||||
|
||||
How to check service logs
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -12,19 +12,22 @@ Initial setup
|
||||
|
||||
sudo ahriman -a x86_64 -r aur service-setup ...
|
||||
|
||||
``service-setup`` literally does the following steps:
|
||||
.. admonition:: Details
|
||||
:collapsible: closed
|
||||
|
||||
#.
|
||||
Create ``/var/lib/ahriman/.makepkg.conf`` with ``makepkg.conf`` overrides if required (at least you might want to set ``PACKAGER``):
|
||||
``service-setup`` literally does the following steps:
|
||||
|
||||
.. code-block:: shell
|
||||
#.
|
||||
Create ``/var/lib/ahriman/.makepkg.conf`` with ``makepkg.conf`` overrides if required (at least you might want to set ``PACKAGER``):
|
||||
|
||||
echo 'PACKAGER="ahriman bot <ahriman@example.com>"' | sudo -u ahriman tee -a /var/lib/ahriman/.makepkg.conf
|
||||
.. code-block:: shell
|
||||
|
||||
#.
|
||||
Configure build tools (it is required for correct dependency management system):
|
||||
echo 'PACKAGER="ahriman bot <ahriman@example.com>"' | sudo -u ahriman tee -a /var/lib/ahriman/.makepkg.conf
|
||||
|
||||
#.
|
||||
#.
|
||||
Configure build tools (it is required for correct dependency management system):
|
||||
|
||||
#.
|
||||
Create build command (you can choose any name for command, basically it should be ``{name}-{arch}-build``):
|
||||
|
||||
.. code-block:: shell
|
||||
@ -67,7 +70,7 @@ Initial setup
|
||||
echo 'ahriman ALL=(ALL) NOPASSWD:SETENV: CARCHBUILD_CMD' | tee -a /etc/sudoers.d/ahriman
|
||||
chmod 400 /etc/sudoers.d/ahriman
|
||||
|
||||
This command supports several arguments, kindly refer to its help message.
|
||||
This command supports several arguments, kindly refer to its help message.
|
||||
|
||||
#.
|
||||
Start and enable ``ahriman@.timer`` via ``systemctl``:
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
pkgbase='ahriman'
|
||||
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
|
||||
pkgver=2.16.0
|
||||
pkgver=2.17.1
|
||||
pkgrel=1
|
||||
pkgdesc="ArcH linux ReposItory MANager"
|
||||
arch=('any')
|
||||
@ -30,6 +30,7 @@ package_ahriman-core() {
|
||||
pkgname='ahriman-core'
|
||||
optdepends=('ahriman-triggers: additional extensions for the application'
|
||||
'ahriman-web: web server'
|
||||
'ipython: an enhanced shell interpreter'
|
||||
'python-boto3: sync to s3'
|
||||
'python-cerberus: configuration validator'
|
||||
'python-matplotlib: usage statistics chart'
|
||||
@ -71,8 +72,9 @@ package_ahriman-triggers() {
|
||||
package_ahriman-web() {
|
||||
pkgname='ahriman-web'
|
||||
pkgdesc="ArcH linux ReposItory MANager, web server"
|
||||
depends=("$pkgbase-core=$pkgver" 'python-aiohttp-apispec>=3.0.0' 'python-aiohttp-cors' 'python-aiohttp-jinja2')
|
||||
depends=("$pkgbase-core=$pkgver" 'python-aiohttp-cors' 'python-aiohttp-jinja2')
|
||||
optdepends=('python-aioauth-client: OAuth2 authorization support'
|
||||
'python-aiohttp-apispec>=3.0.0: autogenerated API documentation'
|
||||
'python-aiohttp-security: authorization support'
|
||||
'python-aiohttp-session: authorization support'
|
||||
'python-cryptography: authorization support')
|
||||
|
@ -7,6 +7,8 @@ logging = ahriman.ini.d/logging.ini
|
||||
;apply_migrations = yes
|
||||
; Path to the application SQLite database.
|
||||
database = ${repository:root}/ahriman.db
|
||||
; Keep last build logs for each package
|
||||
keep_last_logs = 5
|
||||
|
||||
[alpm]
|
||||
; Path to pacman system database cache.
|
||||
|
@ -15,12 +15,12 @@
|
||||
|
||||
<div class="container">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="navbar-brand"><img src="/static/logo.svg" width="30" height="30" alt=""></div>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#repositories-navbar-supported-content" aria-controls="repositories-navbar-supported-content" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<div class="navbar-brand"><a href="https://github.com/arcan1s/ahriman" title="logo"><img src="/static/logo.svg" width="30" height="30" alt=""></a></div>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#repositories-navbar" aria-controls="repositories-navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div id="repositories-navbar-supported-content" class="collapse navbar-collapse">
|
||||
<div id="repositories-navbar" class="collapse navbar-collapse">
|
||||
<ul id="repositories" class="nav nav-tabs">
|
||||
{% for repository in repositories %}
|
||||
<li class="nav-item">
|
||||
@ -36,7 +36,9 @@
|
||||
|
||||
<div class="container">
|
||||
<div id="toolbar" class="dropdown">
|
||||
<a id="badge-status" tabindex="0" role="button" class="btn btn-outline-secondary" data-bs-toggle="popover" data-bs-trigger="focus" data-bs-content="no run data"><i class="bi bi-info-circle"></i></a>
|
||||
<button id="dashboard-button" type="button" class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#dashboard-modal">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
</button>
|
||||
|
||||
{% if not auth.enabled or auth.username is not none %}
|
||||
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
@ -119,7 +121,9 @@
|
||||
<li><a id="badge-version" class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources"><i class="bi bi-github"></i> ahriman</a></li>
|
||||
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
|
||||
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
|
||||
<li><a class="nav-link" href="/api-docs" title="API documentation">api</a></li>
|
||||
{% if docs_enabled %}
|
||||
<li><a class="nav-link" href="/api-docs" title="API documentation">api</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
{% if index_url is not none %}
|
||||
@ -150,6 +154,7 @@
|
||||
|
||||
{% include "build-status/alerts.jinja2" %}
|
||||
|
||||
{% include "build-status/dashboard.jinja2" %}
|
||||
{% include "build-status/package-add-modal.jinja2" %}
|
||||
{% include "build-status/package-rebuild-modal.jinja2" %}
|
||||
{% include "build-status/key-import-modal.jinja2" %}
|
||||
|
@ -0,0 +1,76 @@
|
||||
<div id="dashboard-modal" tabindex="-1" role="dialog" class="modal fade">
|
||||
<div class="modal-dialog modal-xl" role="document">
|
||||
<div class="modal-content">
|
||||
<div id="dashboard-modal-header" class="modal-header">
|
||||
<h4 class="modal-title">System health</h4>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group row mt-2">
|
||||
<div class="col-4 col-lg-2" style="text-align: right">Repository name</div>
|
||||
<div id="dashboard-name" class="col-8 col-lg-3"></div>
|
||||
<div class="col-4 col-lg-2" style="text-align: right">Repository architecture</div>
|
||||
<div id="dashboard-architecture" class="col-8 col-lg-3"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mt-2">
|
||||
<div class="col-4 col-lg-2" style="text-align: right">Current status</div>
|
||||
<div id="dashboard-status" class="col-8 col-lg-3"></div>
|
||||
<div class="col-4 col-lg-2" style="text-align: right">Updated at</div>
|
||||
<div id="dashboard-status-timestamp" class="col-8 col-lg-3"></div>
|
||||
</div>
|
||||
|
||||
<div id="dashboard-canvas" class="form-group row mt-2">
|
||||
<div class="col-8 col-lg-6">
|
||||
<canvas id="dashboard-packages-count-chart"></canvas>
|
||||
</div>
|
||||
<div class="col-8 col-lg-6">
|
||||
<canvas id="dashboard-packages-statuses-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i><span class="d-none d-sm-inline"> close</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const dashboardModal = document.getElementById("dashboard-modal");
|
||||
const dashboardModalHeader = document.getElementById("dashboard-modal-header");
|
||||
|
||||
const dashboardName = document.getElementById("dashboard-name");
|
||||
const dashboardArchitecture = document.getElementById("dashboard-architecture");
|
||||
const dashboardStatus = document.getElementById("dashboard-status");
|
||||
const dashboardStatusTimestamp = document.getElementById("dashboard-status-timestamp");
|
||||
|
||||
const dashboardCanvas = document.getElementById("dashboard-canvas");
|
||||
const dashboardPackagesStatusesChartCanvas = document.getElementById("dashboard-packages-statuses-chart");
|
||||
let dashboardPackagesStatusesChart = null;
|
||||
const dashboardPackagesCountChartCanvas = document.getElementById("dashboard-packages-count-chart");
|
||||
let dashboardPackagesCountChart = null;
|
||||
|
||||
ready(_ => {
|
||||
dashboardPackagesStatusesChart = new Chart(dashboardPackagesStatusesChartCanvas, {
|
||||
type: "pie",
|
||||
data: {},
|
||||
options: {
|
||||
responsive: true,
|
||||
},
|
||||
});
|
||||
dashboardPackagesCountChart = new Chart(dashboardPackagesCountChartCanvas, {
|
||||
type: "bar",
|
||||
data: {},
|
||||
options: {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
@ -59,7 +59,17 @@
|
||||
</nav>
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
<div id="package-info-logs" class="tab-pane fade show active" role="tabpanel" aria-labelledby="package-info-logs-button" tabindex="0">
|
||||
<pre class="language-console"><code id="package-info-logs-input" class="pre-scrollable language-console"></code><button id="package-info-logs-copy-button" type="button" class="btn language-console" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
||||
<div class="row">
|
||||
<div class="col-1 dropend">
|
||||
<button id="package-info-logs-dropdown" class="btn dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-list"></i>
|
||||
</button>
|
||||
<nav id="package-info-logs-versions" class="dropdown-menu" aria-labelledby="package-info-logs-dropdown"></nav>
|
||||
</div>
|
||||
<div class="col-11">
|
||||
<pre class="language-console"><code id="package-info-logs-input" class="pre-scrollable language-console"></code><button id="package-info-logs-copy-button" type="button" class="btn language-console" onclick="copyLogs()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="package-info-changes" class="tab-pane fade" role="tabpanel" aria-labelledby="package-info-changes-button" tabindex="0">
|
||||
<pre class="language-diff"><code id="package-info-changes-input" class="pre-scrollable language-diff"></code><button id="package-info-changes-copy-button" type="button" class="btn language-diff" onclick="copyChanges()"><i class="bi bi-clipboard"></i> copy</button></pre>
|
||||
@ -100,6 +110,7 @@
|
||||
const packageInfoModalHeader = document.getElementById("package-info-modal-header");
|
||||
const packageInfo = document.getElementById("package-info");
|
||||
|
||||
const packageInfoLogsVersions = document.getElementById("package-info-logs-versions");
|
||||
const packageInfoLogsInput = document.getElementById("package-info-logs-input");
|
||||
const packageInfoLogsCopyButton = document.getElementById("package-info-logs-copy-button");
|
||||
|
||||
@ -285,25 +296,51 @@
|
||||
convert: response => response.json(),
|
||||
},
|
||||
data => {
|
||||
const logs = data.map(log_record => {
|
||||
return `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`;
|
||||
});
|
||||
packageInfoLogsInput.textContent = logs.join("\n");
|
||||
highlight(packageInfoLogsInput);
|
||||
const selectors = Object
|
||||
.values(
|
||||
data.reduce((acc, log_record) => {
|
||||
const id = `${log_record.version}-${log_record.process_id}`;
|
||||
if (acc[id])
|
||||
acc[id].created = Math.min(log_record.created, acc[id].created);
|
||||
else
|
||||
acc[id] = log_record;
|
||||
return acc;
|
||||
}, {})
|
||||
)
|
||||
.sort(({created: left}, {created: right}) =>
|
||||
right - left
|
||||
)
|
||||
.map(version => {
|
||||
const link = document.createElement("a");
|
||||
link.classList.add("dropdown-item");
|
||||
|
||||
link.textContent = new Date(1000 * version.created).toISOStringShort();
|
||||
link.href = "#";
|
||||
link.onclick = _ => {
|
||||
const logs = data
|
||||
.filter(log_record => log_record.version === version.version && log_record.process_id === version.process_id)
|
||||
.map(log_record => `[${new Date(1000 * log_record.created).toISOString()}] ${log_record.message}`);
|
||||
|
||||
packageInfoLogsInput.textContent = logs.join("\n");
|
||||
highlight(packageInfoLogsInput);
|
||||
|
||||
Array.from(packageInfoLogsVersions.children).forEach(el => el.classList.remove("active"));
|
||||
link.classList.add("active");
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
return link;
|
||||
});
|
||||
|
||||
packageInfoLogsVersions.replaceChildren(...selectors);
|
||||
selectors.find(Boolean)?.click();
|
||||
},
|
||||
onFailure,
|
||||
);
|
||||
}
|
||||
|
||||
function loadPackage(packageBase, onFailure) {
|
||||
const headerClass = status => {
|
||||
if (status === "pending") return ["bg-warning"];
|
||||
if (status === "building") return ["bg-warning"];
|
||||
if (status === "failed") return ["bg-danger", "text-white"];
|
||||
if (status === "success") return ["bg-success", "text-white"];
|
||||
return ["bg-secondary", "text-white"];
|
||||
};
|
||||
|
||||
makeRequest(
|
||||
`/api/v1/packages/${packageBase}`,
|
||||
{
|
||||
|
@ -7,7 +7,7 @@
|
||||
// so far bootstrap-table only operates with jquery elements
|
||||
const table = $(document.getElementById("packages"));
|
||||
|
||||
const statusBadge = document.getElementById("badge-status");
|
||||
const dashboardButton = document.getElementById("dashboard-button");
|
||||
const versionBadge = document.getElementById("badge-version");
|
||||
|
||||
function doPackageAction(uri, packages, repository, successText, failureText, data) {
|
||||
@ -141,14 +141,62 @@
|
||||
data => {
|
||||
versionBadge.innerHTML = `<i class="bi bi-github"></i> ahriman ${safe(data.version)}`;
|
||||
|
||||
statusBadge.classList.remove(...statusBadge.classList);
|
||||
statusBadge.classList.add("btn");
|
||||
statusBadge.classList.add(badgeClass(data.status.status));
|
||||
dashboardButton.classList.remove(...dashboardButton.classList);
|
||||
dashboardButton.classList.add("btn");
|
||||
dashboardButton.classList.add(badgeClass(data.status.status));
|
||||
|
||||
const popover = bootstrap.Popover.getOrCreateInstance(statusBadge);
|
||||
popover.dispose();
|
||||
statusBadge.dataset.bsContent = `${data.status.status} at ${new Date(1000 * data.status.timestamp).toISOStringShort()}`;
|
||||
bootstrap.Popover.getOrCreateInstance(statusBadge);
|
||||
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;
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -227,7 +275,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
bootstrap.Popover.getOrCreateInstance(statusBadge);
|
||||
selectRepository();
|
||||
});
|
||||
</script>
|
||||
|
@ -58,6 +58,14 @@
|
||||
return value.includes(dataList[index].toLowerCase());
|
||||
}
|
||||
|
||||
function headerClass(status) {
|
||||
if (status === "pending") return ["bg-warning"];
|
||||
if (status === "building") return ["bg-warning"];
|
||||
if (status === "failed") return ["bg-danger", "text-white"];
|
||||
if (status === "success") return ["bg-success", "text-white"];
|
||||
return ["bg-secondary", "text-white"];
|
||||
}
|
||||
|
||||
function listToTable(data) {
|
||||
return Array.from(new Set(data))
|
||||
.sort()
|
||||
|
@ -64,10 +64,10 @@ _shtab_ahriman_service_key_import_option_strings=('-h' '--help' '--key-server')
|
||||
_shtab_ahriman_service_repositories_option_strings=('-h' '--help' '--id-only' '--no-id-only')
|
||||
_shtab_ahriman_service_run_option_strings=('-h' '--help')
|
||||
_shtab_ahriman_service_setup_option_strings=('-h' '--help' '--build-as-user' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
|
||||
_shtab_ahriman_service_shell_option_strings=('-h' '--help')
|
||||
_shtab_ahriman_service_shell_option_strings=('-h' '--help' '-o' '--output')
|
||||
_shtab_ahriman_service_tree_migrate_option_strings=('-h' '--help')
|
||||
_shtab_ahriman_setup_option_strings=('-h' '--help' '--build-as-user' '--from-configuration' '--generate-salt' '--no-generate-salt' '--makeflags-jobs' '--no-makeflags-jobs' '--mirror' '--multilib' '--no-multilib' '--packager' '--server' '--sign-key' '--sign-target' '--web-port' '--web-unix-socket')
|
||||
_shtab_ahriman_shell_option_strings=('-h' '--help')
|
||||
_shtab_ahriman_shell_option_strings=('-h' '--help' '-o' '--output')
|
||||
_shtab_ahriman_sign_option_strings=('-h' '--help')
|
||||
_shtab_ahriman_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
|
||||
_shtab_ahriman_status_update_option_strings=('-h' '--help' '-s' '--status')
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH AHRIMAN "1" "2024\-12\-01" "ahriman" "Generated Python Manual"
|
||||
.TH AHRIMAN "1" "2025\-01\-05" "ahriman" "Generated Python Manual"
|
||||
.SH NAME
|
||||
ahriman
|
||||
.SH SYNOPSIS
|
||||
@ -940,7 +940,7 @@ port of the web service
|
||||
path to unix socket used for interprocess communications
|
||||
|
||||
.SH COMMAND \fI\,'ahriman service\-shell'\/\fR
|
||||
usage: ahriman service\-shell [\-h] [code]
|
||||
usage: ahriman service\-shell [\-h] [\-o OUTPUT] [code]
|
||||
|
||||
drop into python shell
|
||||
|
||||
@ -948,6 +948,11 @@ drop into python shell
|
||||
\fBcode\fR
|
||||
instead of dropping into shell, just execute the specified code
|
||||
|
||||
.SH OPTIONS \fI\,'ahriman service\-shell'\/\fR
|
||||
.TP
|
||||
\fB\-o\fR \fI\,OUTPUT\/\fR, \fB\-\-output\fR \fI\,OUTPUT\/\fR
|
||||
output commands and result to the file
|
||||
|
||||
.SH COMMAND \fI\,'ahriman service\-tree\-migrate'\/\fR
|
||||
usage: ahriman service\-tree\-migrate [\-h]
|
||||
|
||||
|
@ -583,6 +583,7 @@ _shtab_ahriman_service_setup_options=(
|
||||
|
||||
_shtab_ahriman_service_shell_options=(
|
||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||
{-o,--output}"[output commands and result to the file (default\: None)]:output:"
|
||||
":instead of dropping into shell, just execute the specified code (default\: None):"
|
||||
)
|
||||
|
||||
@ -608,6 +609,7 @@ _shtab_ahriman_setup_options=(
|
||||
|
||||
_shtab_ahriman_shell_options=(
|
||||
"(- : *)"{-h,--help}"[show this help message and exit]"
|
||||
{-o,--output}"[output commands and result to the file (default\: None)]:output:"
|
||||
":instead of dropping into shell, just execute the specified code (default\: None):"
|
||||
)
|
||||
|
||||
|
@ -60,6 +60,9 @@ pacman = [
|
||||
s3 = [
|
||||
"boto3",
|
||||
]
|
||||
shell = [
|
||||
"IPython"
|
||||
]
|
||||
stats = [
|
||||
"matplotlib",
|
||||
]
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -17,4 +17,4 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2.16.0"
|
||||
__version__ = "2.17.1"
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -117,7 +117,7 @@ class Application(ApplicationPackages, ApplicationRepository):
|
||||
|
||||
Args:
|
||||
packages(list[Package]): list of source packages of which dependencies have to be processed
|
||||
process_dependencies(bool): if no set, dependencies will not be processed
|
||||
process_dependencies(bool): if set to ``False``, dependencies will not be processed
|
||||
|
||||
Returns:
|
||||
list[Package]: updated packages list. Packager for dependencies will be copied from the original package
|
||||
@ -130,6 +130,9 @@ class Application(ApplicationPackages, ApplicationRepository):
|
||||
>>> packages = application.with_dependencies(packages, process_dependencies=True)
|
||||
>>> application.print_updates(packages, log_fn=print)
|
||||
"""
|
||||
if not process_dependencies or not packages:
|
||||
return packages
|
||||
|
||||
def missing_dependencies(source: Iterable[Package]) -> dict[str, str | None]:
|
||||
# append list of known packages with packages which are in current sources
|
||||
satisfied_packages = known_packages | {
|
||||
@ -145,22 +148,29 @@ class Application(ApplicationPackages, ApplicationRepository):
|
||||
if dependency not in satisfied_packages
|
||||
}
|
||||
|
||||
if not process_dependencies or not packages:
|
||||
return packages
|
||||
def new_packages(root: Package) -> dict[str, Package]:
|
||||
portion = {root.base: root}
|
||||
while missing := missing_dependencies(portion.values()):
|
||||
for package_name, packager in missing.items():
|
||||
if (source_dir := self.repository.paths.cache_for(package_name)).is_dir():
|
||||
# there is local cache, load package from it
|
||||
leaf = Package.from_build(source_dir, self.repository.architecture, packager)
|
||||
else:
|
||||
leaf = Package.from_aur(package_name, packager)
|
||||
portion[leaf.base] = leaf
|
||||
|
||||
# register package in the database
|
||||
self.repository.reporter.set_unknown(leaf)
|
||||
|
||||
return portion
|
||||
|
||||
known_packages = self._known_packages()
|
||||
with_dependencies = {package.base: package for package in packages}
|
||||
|
||||
while missing := missing_dependencies(with_dependencies.values()):
|
||||
for package_name, username in missing.items():
|
||||
if (source_dir := self.repository.paths.cache_for(package_name)).is_dir():
|
||||
# there is local cache, load package from it
|
||||
package = Package.from_build(source_dir, self.repository.architecture, username)
|
||||
else:
|
||||
package = Package.from_aur(package_name, username)
|
||||
with_dependencies[package.base] = package
|
||||
|
||||
# register package in the database
|
||||
self.repository.reporter.set_unknown(package)
|
||||
with_dependencies: dict[str, Package] = {}
|
||||
for package in packages:
|
||||
with self.in_package_context(package.base, package.version): # use the same context for the logger
|
||||
try:
|
||||
with_dependencies |= new_packages(package)
|
||||
except Exception:
|
||||
self.logger.exception("could not process dependencies of %s, skip the package", package.base)
|
||||
|
||||
return list(with_dependencies.values())
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -22,7 +22,7 @@ import logging
|
||||
|
||||
from collections.abc import Callable, Iterable
|
||||
from multiprocessing import Pool
|
||||
from typing import TypeVar
|
||||
from typing import ClassVar, TypeVar
|
||||
|
||||
from ahriman.application.lock import Lock
|
||||
from ahriman.core.configuration import Configuration
|
||||
@ -53,13 +53,13 @@ class Handler:
|
||||
Wrapper for all command line actions, though each derived class implements :func:`run()` method, it usually
|
||||
must not be called directly. The recommended way is to call :func:`execute()` class method, e.g.::
|
||||
|
||||
>>> from ahriman.application.handlers import Add
|
||||
>>> from ahriman.application.handlers.add import Add
|
||||
>>>
|
||||
>>> Add.execute(args)
|
||||
"""
|
||||
|
||||
ALLOW_MULTI_ARCHITECTURE_RUN = True
|
||||
arguments: list[Callable[[SubParserAction], argparse.ArgumentParser]]
|
||||
ALLOW_MULTI_ARCHITECTURE_RUN: ClassVar[bool] = True
|
||||
arguments: ClassVar[list[Callable[[SubParserAction], argparse.ArgumentParser]]]
|
||||
|
||||
@classmethod
|
||||
def call(cls, args: argparse.Namespace, repository_id: RepositoryId) -> bool:
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -21,6 +21,7 @@ import argparse
|
||||
|
||||
from collections.abc import Callable, Iterable
|
||||
from dataclasses import fields
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||
from ahriman.core.alpm.remote import AUR, Official
|
||||
@ -40,7 +41,7 @@ class Search(Handler):
|
||||
"""
|
||||
|
||||
ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
|
||||
SORT_FIELDS = {
|
||||
SORT_FIELDS: ClassVar[set[str]] = {
|
||||
field.name
|
||||
for field in fields(AURPackage)
|
||||
if field.default_factory is not list
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -21,6 +21,7 @@ import argparse
|
||||
|
||||
from pathlib import Path
|
||||
from pwd import getpwuid
|
||||
from typing import ClassVar
|
||||
from urllib.parse import quote_plus as url_encode
|
||||
|
||||
from ahriman.application.application import Application
|
||||
@ -46,9 +47,9 @@ class Setup(Handler):
|
||||
|
||||
ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
|
||||
|
||||
ARCHBUILD_COMMAND_PATH = Path("/") / "usr" / "bin" / "archbuild"
|
||||
MIRRORLIST_PATH = Path("/") / "etc" / "pacman.d" / "mirrorlist"
|
||||
SUDOERS_DIR_PATH = Path("/") / "etc" / "sudoers.d"
|
||||
ARCHBUILD_COMMAND_PATH: ClassVar[Path] = Path("/") / "usr" / "bin" / "archbuild"
|
||||
MIRRORLIST_PATH: ClassVar[Path] = Path("/") / "etc" / "pacman.d" / "mirrorlist"
|
||||
SUDOERS_DIR_PATH: ClassVar[Path] = Path("/") / "etc" / "sudoers.d"
|
||||
|
||||
@classmethod
|
||||
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -18,12 +18,12 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import argparse
|
||||
import code
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||
from ahriman.application.interactive_shell import InteractiveShell
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.formatters import StringPrinter
|
||||
from ahriman.models.repository_id import RepositoryId
|
||||
@ -58,11 +58,13 @@ class Shell(Handler):
|
||||
"configuration": configuration,
|
||||
"repository_id": repository_id,
|
||||
}
|
||||
console = InteractiveShell(locals=local_variables)
|
||||
|
||||
if args.code is None:
|
||||
code.interact(local=local_variables)
|
||||
else:
|
||||
code.InteractiveConsole(locals=local_variables).runcode(args.code)
|
||||
match args.code:
|
||||
case None:
|
||||
console.interact()
|
||||
case snippet:
|
||||
console.runcode(snippet)
|
||||
|
||||
@staticmethod
|
||||
def _set_service_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
@ -79,6 +81,7 @@ class Shell(Handler):
|
||||
description="drop into python shell")
|
||||
parser.add_argument("code", help="instead of dropping into shell, just execute the specified code", nargs="?")
|
||||
parser.add_argument("-v", "--verbose", help=argparse.SUPPRESS, action="store_true")
|
||||
parser.add_argument("-o", "--output", help="output commands and result to the file", type=Path)
|
||||
parser.set_defaults(lock=None, report=False)
|
||||
return parser
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -27,7 +27,7 @@ from pathlib import Path
|
||||
from ahriman.application.application import Application
|
||||
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.formatters import EventStatsPrinter, PackageStatsPrinter
|
||||
from ahriman.core.formatters import EventStatsPrinter, PackageStatsPrinter, RepositoryStatsPrinter
|
||||
from ahriman.core.utils import enum_values, pretty_datetime
|
||||
from ahriman.models.event import Event, EventType
|
||||
from ahriman.models.repository_id import RepositoryId
|
||||
@ -64,6 +64,7 @@ class Statistics(Handler):
|
||||
|
||||
match args.package:
|
||||
case None:
|
||||
RepositoryStatsPrinter(repository_id, application.reporter.statistics())(verbose=True)
|
||||
Statistics.stats_per_package(args.event, events, args.chart)
|
||||
case _:
|
||||
Statistics.stats_for_package(args.event, events, args.chart)
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -23,6 +23,7 @@ import sys
|
||||
|
||||
from collections.abc import Generator
|
||||
from importlib import metadata
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman import __version__
|
||||
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||
@ -36,11 +37,11 @@ class Versions(Handler):
|
||||
version handler
|
||||
|
||||
Attributes:
|
||||
PEP423_PACKAGE_NAME(str): (class attribute) special regex for valid PEP423 package name
|
||||
PEP423_PACKAGE_NAME(re.Pattern[str]): (class attribute) special regex for valid PEP423 package name
|
||||
"""
|
||||
|
||||
ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
|
||||
PEP423_PACKAGE_NAME = re.compile(r"^[A-Za-z0-9._-]+")
|
||||
PEP423_PACKAGE_NAME: ClassVar[re.Pattern[str]] = re.compile(r"^[A-Za-z0-9._-]+")
|
||||
|
||||
@classmethod
|
||||
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
60
src/ahriman/application/interactive_shell.py
Normal file
60
src/ahriman/application/interactive_shell.py
Normal file
@ -0,0 +1,60 @@
|
||||
#
|
||||
# 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 code import InteractiveConsole
|
||||
from importlib.util import find_spec
|
||||
from typing import Any
|
||||
|
||||
|
||||
class InteractiveShell(InteractiveConsole):
|
||||
"""
|
||||
wrapper around :class:`code.InteractiveConsole` to pass :func:`interact()` to IPython shell
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def has_ipython() -> bool:
|
||||
"""
|
||||
check if IPython shell is available
|
||||
|
||||
Returns:
|
||||
bool: ``True`` if IPython shell is available, ``False`` otherwise
|
||||
"""
|
||||
try:
|
||||
return find_spec("IPython.terminal.embed") is not None
|
||||
except ModuleNotFoundError:
|
||||
return False
|
||||
|
||||
def interact(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""
|
||||
pass controller to IPython shell
|
||||
|
||||
Args:
|
||||
*args(Any): positional arguments
|
||||
**kwargs(Any): keyword arguments
|
||||
"""
|
||||
if self.has_ipython():
|
||||
from IPython.terminal.embed import InteractiveShellEmbed
|
||||
|
||||
shell = InteractiveShellEmbed(user_ns=self.locals) # type: ignore[no-untyped-call]
|
||||
shell.show_banner() # type: ignore[no-untyped-call]
|
||||
shell.interact() # type: ignore[no-untyped-call]
|
||||
else:
|
||||
# fallback to default
|
||||
import readline # pylint: disable=unused-import
|
||||
InteractiveConsole.interact(self, *args, **kwargs)
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -23,6 +23,7 @@ import shutil
|
||||
from email.utils import parsedate_to_datetime
|
||||
from pathlib import Path
|
||||
from pyalpm import DB # type: ignore[import-not-found]
|
||||
from typing import ClassVar
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from ahriman.core.configuration import Configuration
|
||||
@ -41,7 +42,7 @@ class PacmanDatabase(SyncHttpClient):
|
||||
sync_files_database(bool): sync files database
|
||||
"""
|
||||
|
||||
LAST_MODIFIED_HEADER = "Last-Modified"
|
||||
LAST_MODIFIED_HEADER: ClassVar[str] = "Last-Modified"
|
||||
|
||||
def __init__(self, database: DB, configuration: Configuration) -> None:
|
||||
"""
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -34,13 +34,14 @@ class PkgbuildToken(StrEnum):
|
||||
well-known tokens dictionary
|
||||
|
||||
Attributes:
|
||||
ArrayEnds(PkgbuildToken): (class attribute) array ends token
|
||||
ArrayStarts(PkgbuildToken): (class attribute) array starts token
|
||||
Comma(PkgbuildToken): (class attribute) comma token
|
||||
Comment(PkgbuildToken): (class attribute) comment token
|
||||
FunctionDeclaration(PkgbuildToken): (class attribute) function declaration token
|
||||
FunctionEnds(PkgbuildToken): (class attribute) function ends token
|
||||
FunctionStarts(PkgbuildToken): (class attribute) function starts token
|
||||
ArrayEnds(PkgbuildToken): array ends token
|
||||
ArrayStarts(PkgbuildToken): array starts token
|
||||
Comma(PkgbuildToken): comma token
|
||||
Comment(PkgbuildToken): comment token
|
||||
FunctionDeclaration(PkgbuildToken): function declaration token
|
||||
FunctionEnds(PkgbuildToken): function ends token
|
||||
FunctionStarts(PkgbuildToken): function starts token
|
||||
NewLine(PkgbuildToken): new line token
|
||||
"""
|
||||
|
||||
ArrayStarts = "("
|
||||
@ -54,6 +55,8 @@ class PkgbuildToken(StrEnum):
|
||||
FunctionStarts = "{"
|
||||
FunctionEnds = "}"
|
||||
|
||||
NewLine = "\n"
|
||||
|
||||
|
||||
class PkgbuildParser(shlex.shlex):
|
||||
"""
|
||||
@ -174,31 +177,18 @@ class PkgbuildParser(shlex.shlex):
|
||||
Returns:
|
||||
bool: ``True`` if the previous element of the stream is a quote or escaped and ``False`` otherwise
|
||||
"""
|
||||
# wrapper around reading utf symbols from random position of the stream
|
||||
def read_last() -> tuple[int, str]:
|
||||
while (position := self._io.tell()) > 0:
|
||||
try:
|
||||
return position, self._io.read(1)
|
||||
except UnicodeDecodeError:
|
||||
self._io.seek(position - 1)
|
||||
|
||||
raise PkgbuildParserError("reached starting position, no valid symbols found")
|
||||
|
||||
current_position = self._io.tell()
|
||||
|
||||
last_char = penultimate_char = None
|
||||
index = current_position - 1
|
||||
while index > 0:
|
||||
self._io.seek(index)
|
||||
|
||||
index, last_char = read_last()
|
||||
index, last_char = self._read_last(index)
|
||||
if last_char.isspace():
|
||||
index -= 1
|
||||
continue
|
||||
|
||||
if index > 1:
|
||||
self._io.seek(index - 1)
|
||||
_, penultimate_char = read_last()
|
||||
_, penultimate_char = self._read_last(index - 1)
|
||||
|
||||
break
|
||||
|
||||
@ -227,7 +217,7 @@ class PkgbuildParser(shlex.shlex):
|
||||
case PkgbuildToken.ArrayEnds:
|
||||
break
|
||||
case comment if comment.startswith(PkgbuildToken.Comment):
|
||||
self.instream.readline()
|
||||
self._read_comment()
|
||||
continue
|
||||
|
||||
yield token
|
||||
@ -268,7 +258,7 @@ class PkgbuildParser(shlex.shlex):
|
||||
if counter == 0:
|
||||
break
|
||||
case comment if comment.startswith(PkgbuildToken.Comment):
|
||||
self.instream.readline()
|
||||
self._read_comment()
|
||||
|
||||
if not 0 < start_position < end_position:
|
||||
raise PkgbuildParserError("function body wasn't found")
|
||||
@ -304,7 +294,7 @@ class PkgbuildParser(shlex.shlex):
|
||||
return
|
||||
|
||||
if token.startswith(PkgbuildToken.Comment):
|
||||
self.instream.readline()
|
||||
self._read_comment()
|
||||
return
|
||||
|
||||
match self.get_token():
|
||||
@ -332,6 +322,44 @@ class PkgbuildParser(shlex.shlex):
|
||||
case other if other is not None:
|
||||
yield from self._parse_token(other)
|
||||
|
||||
def _read_comment(self) -> None:
|
||||
"""
|
||||
read comment from the current position. This method doesn't check comment itself, just read the stream
|
||||
until the comment line ends
|
||||
"""
|
||||
_, last_symbol = self._read_last()
|
||||
if last_symbol != PkgbuildToken.NewLine:
|
||||
self.instream.readline()
|
||||
|
||||
def _read_last(self, initial_index: int | None = None) -> tuple[int, str]:
|
||||
"""
|
||||
wrapper around read to read the last symbol from the input stream. This method is designed to process UTF-8
|
||||
symbols correctly. This method does not reset current stream position
|
||||
|
||||
Args:
|
||||
initial_index(int | None, optional): initial index to start reading from. If none set, the previous position
|
||||
will be used (Default value = None)
|
||||
|
||||
Returns:
|
||||
tuple[int, str]: last symbol and its position in the stream
|
||||
|
||||
Raises:
|
||||
PkgbuildParserError: in case if stream reached starting position, but no valid symbols were found
|
||||
"""
|
||||
if initial_index is None:
|
||||
initial_index = self._io.tell() - 1
|
||||
if initial_index < 0:
|
||||
raise PkgbuildParserError("stream is on starting position")
|
||||
self._io.seek(initial_index)
|
||||
|
||||
while (position := self._io.tell()) > 0:
|
||||
try:
|
||||
return position, self._io.read(1)
|
||||
except UnicodeDecodeError:
|
||||
self._io.seek(position - 1)
|
||||
|
||||
raise PkgbuildParserError("reached starting position, no valid symbols found")
|
||||
|
||||
def parse(self) -> Generator[PkgbuildPatch, None, None]:
|
||||
"""
|
||||
parse source stream and yield parsed entries
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from typing import Any
|
||||
from typing import Any, ClassVar
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.remote import Remote
|
||||
@ -35,9 +35,9 @@ class AUR(Remote):
|
||||
DEFAULT_RPC_VERSION(str): (class attribute) default AUR RPC version
|
||||
"""
|
||||
|
||||
DEFAULT_AUR_URL = "https://aur.archlinux.org"
|
||||
DEFAULT_RPC_URL = f"{DEFAULT_AUR_URL}/rpc"
|
||||
DEFAULT_RPC_VERSION = "5"
|
||||
DEFAULT_AUR_URL: ClassVar[str] = "https://aur.archlinux.org"
|
||||
DEFAULT_RPC_URL: ClassVar[str] = f"{DEFAULT_AUR_URL}/rpc"
|
||||
DEFAULT_RPC_VERSION: ClassVar[str] = "5"
|
||||
|
||||
@classmethod
|
||||
def remote_git_url(cls, package_base: str, repository: str) -> str:
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -17,7 +17,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from typing import Any
|
||||
from typing import Any, ClassVar
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.remote import Remote
|
||||
@ -36,10 +36,10 @@ class Official(Remote):
|
||||
DEFAULT_RPC_URL(str): (class attribute) default archlinux repositories RPC url
|
||||
"""
|
||||
|
||||
DEFAULT_ARCHLINUX_GIT_URL = "https://gitlab.archlinux.org"
|
||||
DEFAULT_ARCHLINUX_URL = "https://archlinux.org"
|
||||
DEFAULT_SEARCH_REPOSITORIES = ["Core", "Extra", "Multilib"]
|
||||
DEFAULT_RPC_URL = "https://archlinux.org/packages/search/json"
|
||||
DEFAULT_ARCHLINUX_GIT_URL: ClassVar[str] = "https://gitlab.archlinux.org"
|
||||
DEFAULT_ARCHLINUX_URL: ClassVar[str] = "https://archlinux.org"
|
||||
DEFAULT_SEARCH_REPOSITORIES: ClassVar[list[str]] = ["Core", "Extra", "Multilib"]
|
||||
DEFAULT_RPC_URL: ClassVar[str] = "https://archlinux.org/packages/search/json"
|
||||
|
||||
@classmethod
|
||||
def remote_git_url(cls, package_base: str, repository: str) -> str:
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -19,9 +19,8 @@
|
||||
#
|
||||
try:
|
||||
import aiohttp_security
|
||||
_has_aiohttp_security = True
|
||||
except ImportError:
|
||||
_has_aiohttp_security = False
|
||||
aiohttp_security = None # type: ignore[assignment]
|
||||
|
||||
from typing import Any
|
||||
|
||||
@ -40,7 +39,7 @@ async def authorized_userid(*args: Any, **kwargs: Any) -> Any:
|
||||
Returns:
|
||||
Any: ``None`` in case if no aiohttp_security module found and function call otherwise
|
||||
"""
|
||||
if _has_aiohttp_security:
|
||||
if aiohttp_security is not None:
|
||||
return await aiohttp_security.authorized_userid(*args, **kwargs) # pylint: disable=no-value-for-parameter
|
||||
return None
|
||||
|
||||
@ -56,7 +55,7 @@ async def check_authorized(*args: Any, **kwargs: Any) -> Any:
|
||||
Returns:
|
||||
Any: ``None`` in case if no aiohttp_security module found and function call otherwise
|
||||
"""
|
||||
if _has_aiohttp_security:
|
||||
if aiohttp_security is not None:
|
||||
return await aiohttp_security.check_authorized(*args, **kwargs) # pylint: disable=no-value-for-parameter
|
||||
return None
|
||||
|
||||
@ -72,7 +71,7 @@ async def forget(*args: Any, **kwargs: Any) -> Any:
|
||||
Returns:
|
||||
Any: ``None`` in case if no aiohttp_security module found and function call otherwise
|
||||
"""
|
||||
if _has_aiohttp_security:
|
||||
if aiohttp_security is not None:
|
||||
return await aiohttp_security.forget(*args, **kwargs) # pylint: disable=no-value-for-parameter
|
||||
return None
|
||||
|
||||
@ -88,6 +87,6 @@ async def remember(*args: Any, **kwargs: Any) -> Any:
|
||||
Returns:
|
||||
Any: ``None`` in case if no aiohttp_security module found and function call otherwise
|
||||
"""
|
||||
if _has_aiohttp_security:
|
||||
if aiohttp_security is not None:
|
||||
return await aiohttp_security.remember(*args, **kwargs) # pylint: disable=no-value-for-parameter
|
||||
return None
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -136,7 +136,7 @@ class PackageArchive:
|
||||
dependencies, roots = self.depends_on_paths()
|
||||
installed_packages = self.installed_packages()
|
||||
# build list of packages, which contains both the package itself and (possible) debug packages
|
||||
packages = list(self.package.packages) + [f"{package}-debug" for package in self.package.packages]
|
||||
packages = list(self.package.packages) + [f"{self.package.base}-debug"]
|
||||
|
||||
# build initial map of file path -> packages containing this path
|
||||
# in fact, keys will contain all libraries the package linked to and all directories it contains
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -21,6 +21,7 @@ import shutil
|
||||
|
||||
from collections.abc import Generator
|
||||
from pathlib import Path
|
||||
from typing import ClassVar
|
||||
|
||||
from ahriman.core.exceptions import CalledProcessError
|
||||
from ahriman.core.log import LazyLogging
|
||||
@ -42,9 +43,9 @@ class Sources(LazyLogging):
|
||||
GITCONFIG(dict[str, str]): (class attribute) git config options to suppress annoying hints
|
||||
"""
|
||||
|
||||
DEFAULT_BRANCH = "master" # default fallback branch
|
||||
DEFAULT_COMMIT_AUTHOR = ("ahriman", "ahriman@localhost")
|
||||
GITCONFIG = {
|
||||
DEFAULT_BRANCH: ClassVar[str] = "master" # default fallback branch
|
||||
DEFAULT_COMMIT_AUTHOR: ClassVar[tuple[str, str]] = ("ahriman", "ahriman@localhost")
|
||||
GITCONFIG: ClassVar[dict[str, str]] = {
|
||||
"init.defaultBranch": DEFAULT_BRANCH,
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
# Copyright (c) 2021-2025 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
@ -149,8 +149,11 @@ class Task(LazyLogging):
|
||||
str | None: current commit sha if available
|
||||
"""
|
||||
last_commit_sha = Sources.load(sources_dir, self.package, patches, self.paths)
|
||||
if local_version is None:
|
||||
return last_commit_sha # there is no local package or pkgrel increment is disabled
|
||||
if self.package.is_vcs: # if package is VCS, then make sure to update PKGBUILD to the latest version
|
||||
self.build(sources_dir, dry_run=True)
|
||||
|
||||
if local_version is None: # there is no local package or pkgrel increment is disabled
|
||||
return last_commit_sha
|
||||
|
||||
# load fresh package
|
||||
loaded_package = Package.from_build(sources_dir, self.architecture, None)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user