Compare commits

..

7 Commits

78 changed files with 1172 additions and 1984 deletions

View File

@ -48,6 +48,5 @@ jobs:
- name: Build an image and push
uses: docker/build-push-action@v4
with:
file: docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}

View File

@ -1,136 +0,0 @@
name: Regress
on: workflow_dispatch
jobs:
run-regress-tests:
runs-on: ubuntu-latest
services:
ahriman:
image: arcan1s/ahriman:edge
env:
AHRIMAN_PORT: 8080
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman-web.sock
options: --privileged --entrypoint entrypoint-web
ports:
- 8080
volumes:
- repo:/var/lib/ahriman
container:
image: arcan1s/ahriman:edge
env:
AHRIMAN_DEBUG: y
AHRIMAN_OUTPUT: console
AHRIMAN_PORT: 8080
AHRIMAN_UNIX_SOCKET: /var/lib/ahriman/ahriman/ahriman-web.sock
options: --privileged
volumes:
- repo:/var/lib/ahriman
steps:
- uses: actions/checkout@v3
- run: pacman -Sy
- name: Init repository
run: entrypoint help
- name: Print configuration
run: |
ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
service-config
- name: Validate configuration
run: |
ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
service-config-validate
- name: Create a user
run: |
sudo -u ahriman ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
user-add \
--packager "github actions <actions@github.com>" \
--password ahriman \
--role full \
ahriman
- name: Fetch users
run: |
ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
user-list \
--exit-code
- name: Add package
run: |
sudo -u ahriman ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
package-add \
--exit-code \
--now \
--refresh \
ahriman
- name: Update status of the package
run: |
ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
package-status-update \
--status failed \
ahriman
- name: Request status of the package
run: |
ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
package-status \
--exit-code \
--info \
--status failed \
ahriman
- name: Update packages
run: |
sudo -u ahriman ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
repo-update \
--exit-code \
|| true
- name: Add patch
run: |
echo '${pkgver%%.*}' | \
sudo -u ahriman ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
patch-add \
ahriman \
pkgrel
- name: Retrieve patches
run: |
ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
patch-list \
--exit-code \
ahriman
- name: Rebuild packages
run: |
sudo -u ahriman ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
repo-rebuild \
--depends-on python \
--exit-code
- name: Remove package
run: |
sudo -u ahriman ahriman \
--log-handler "$AHRIMAN_OUTPUT" \
package-remove \
ahriman

View File

@ -26,16 +26,11 @@ cp "docker/systemd-nspawn.sh" "/usr/local/bin/systemd-nspawn"
# create fresh tarball
tox -e archive
# run makepkg
PKGVER=$(python -c "from src.ahriman import __version__; print(__version__)")
mv "dist/ahriman-$PKGVER.tar.gz" package/archlinux
mv dist/ahriman-*.tar.gz package/archlinux
chmod +777 package/archlinux # because fuck you that's why
cd package/archlinux
sudo -u nobody -- makepkg -cf --skipchecksums --noconfirm
sudo -u nobody -- makepkg --packagelist | grep "ahriman-core-$PKGVER" | pacman -U --noconfirm --nodeps -
if [[ -z $MINIMAL_INSTALL ]]; then
sudo -u nobody -- makepkg --packagelist | grep "ahriman-triggers-$PKGVER" | pacman -U --noconfirm --nodeps -
sudo -u nobody -- makepkg --packagelist | grep "ahriman-web-$PKGVER" | pacman -U --noconfirm --nodeps -
fi
sudo -u nobody -- makepkg --packagelist | grep -v -- -debug- | pacman -U --noconfirm --nodeps -
# create machine-id which is required by build tools
systemd-machine-id-setup
@ -46,12 +41,12 @@ pacman -Qdtq | pacman -Rscn --noconfirm -
[[ -z $MINIMAL_INSTALL ]] && WEB_ARGS=("--web-port" "8080")
ahriman -a x86_64 -r "github" service-setup --packager "ahriman bot <ahriman@example.com>" "${WEB_ARGS[@]}"
# enable services
systemctl enable ahriman-web
systemctl enable ahriman@x86_64-github.timer
if [[ -z $MINIMAL_INSTALL ]]; then
# validate configuration
ahriman service-config-validate --exit-code
# run web service (detached)
systemctl enable ahriman-web
sudo -u ahriman -- ahriman web &
WEB_PID=$!
fi

View File

@ -15,9 +15,9 @@ jobs:
container:
image: archlinux:base
options: --privileged -w /build
volumes:
- ${{ github.workspace }}:/build
options: --privileged -w /build
steps:
- uses: actions/checkout@v3

View File

@ -7,8 +7,6 @@ on:
pull_request:
branches:
- master
schedule:
- cron: 1 0 * * *
jobs:
run-tests:
@ -17,9 +15,9 @@ jobs:
container:
image: archlinux:base
options: -w /build
volumes:
- ${{ github.workspace }}:/build
options: -w /build
steps:
- uses: actions/checkout@v3

View File

@ -1,77 +1,4 @@
# build image
FROM archlinux:base AS build
# install environment
## create build user
RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build
## extract container creation date and set mirror for this timestamp, set PKGEXT and refresh database next
RUN echo "Server = https://archive.archlinux.org/repos/$(stat -c "%y" "/var/lib/pacman" | cut -d " " -f 1 | sed "s,-,/,g")/\$repo/os/\$arch" > "/etc/pacman.d/mirrorlist" && \
pacman -Sy
## setup package cache
RUN runuser -u build -- mkdir "/tmp/pkg" && \
echo "PKGDEST=/tmp/pkg" >> "/etc/makepkg.conf" && \
echo "[options]" >> "/etc/pacman.conf" && \
echo "CacheDir = /tmp/pkg/" >> "/etc/pacman.conf"
## install anc configure sudo
RUN pacman -S --noconfirm --asdeps sudo && \
echo "build ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/build"
## copy install script
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies
RUN pacman -S --noconfirm --asdeps \
devtools \
git \
pyalpm \
python-bcrypt \
python-inflection \
python-pyelftools \
python-requests \
&& \
pacman -S --noconfirm --asdeps \
base-devel \
python-build \
python-flit \
python-installer \
python-tox \
python-wheel \
&& \
pacman -S --noconfirm --asdeps \
git \
python-aiohttp \
python-boto3 \
python-cerberus \
python-cryptography \
python-jinja \
python-systemd \
rsync \
&& \
runuser -u build -- install-aur-package \
python-aioauth-client \
python-sphinx-typlog-theme \
python-webargs \
python-aiohttp-apispec-git \
python-aiohttp-cors \
python-aiohttp-jinja2 \
python-aiohttp-session \
python-aiohttp-security \
python-requests-unixsocket2
# install ahriman
## copy tree
COPY --chown=build . "/home/build/ahriman"
## create package archive and install it
RUN cd "/home/build/ahriman" && \
tox -e archive && \
cp ./dist/*.tar.gz "package/archlinux" && \
cd "package/archlinux" && \
runuser -u build -- makepkg --noconfirm --skipchecksums && \
cd / && rm -r "/home/build/ahriman"
# main image
FROM archlinux:base AS ahriman
FROM archlinux:base
# image configuration
ENV AHRIMAN_ARCHITECTURE="x86_64"
@ -96,47 +23,74 @@ ENV AHRIMAN_VALIDATE_CONFIGURATION="yes"
## update pacman.conf with multilib
RUN echo "[multilib]" >> "/etc/pacman.conf" && \
echo "Include = /etc/pacman.d/mirrorlist" >> "/etc/pacman.conf"
## copy built packages from build image and setup repository
COPY --from=build "/tmp/pkg" "/var/cache/pacman/pkg"
RUN repo-add "/var/cache/pacman/pkg/core.db.tar.zst" "/var/cache/pacman/pkg/"*.pkg.tar.zst && \
repo-add "/var/cache/pacman/pkg/extra.db.tar.zst" && \
repo-add "/var/cache/pacman/pkg/multilib.db.tar.zst"
## set local directory to use as repository and refresh database
RUN cp "/etc/pacman.d/mirrorlist" "/etc/pacman.d/mirrorlist.orig" && \
echo "Server = file:///var/cache/pacman/pkg" > "/etc/pacman.d/mirrorlist" && \
cp "/etc/pacman.conf" "/etc/pacman.conf.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
## refresh packages, install sudo and install packages for building
RUN pacman -Syu --noconfirm sudo && \
pacman -S --noconfirm --asdeps fakeroot python-tox
## create build user
RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build && \
echo "build ALL=(ALL) NOPASSWD: ALL" > "/etc/sudoers.d/build"
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies
RUN pacman -S --noconfirm --asdeps \
python-aioauth-client \
python-aiohttp-apispec-git \
python-aiohttp-security \
python-aiohttp-session \
devtools \
git \
pyalpm \
python-bcrypt \
python-inflection \
python-pyelftools \
python-requests \
&& \
pacman -S --noconfirm --asdeps \
base-devel \
python-build \
python-flit \
python-installer \
python-wheel \
&& \
pacman -S --noconfirm --asdeps \
git \
python-aiohttp \
python-boto3 \
python-cerberus \
python-cryptography \
python-jinja \
python-systemd \
python-requests-unixsocket2 \
rsync \
sudo
&& \
runuser -u build -- install-aur-package \
python-aioauth-client \
python-sphinx-typlog-theme \
python-webargs \
python-aiohttp-apispec-git \
python-aiohttp-cors \
python-aiohttp-jinja2 \
python-aiohttp-session \
python-aiohttp-security \
python-requests-unixsocket2
## clear cache and restore system
RUN find "/var/cache/pacman/pkg" "/var/lib/pacman/sync" -type "f,l" -delete && \
cp "/etc/pacman.d/mirrorlist.orig" "/etc/pacman.d/mirrorlist" && \
cp "/etc/pacman.conf.orig" "/etc/pacman.conf"
## FIXME since 1.0.4 devtools requires dbus to be run, which doesn't work now in container
COPY "docker/systemd-nspawn.sh" "/usr/local/bin/systemd-nspawn"
# install ahriman
## copy tree
COPY --chown=build . "/home/build/ahriman"
## create package archive and install it
RUN cd "/home/build/ahriman" && \
tox -e archive && \
cp ./dist/*.tar.gz "package/archlinux" && \
cd "package/archlinux" && \
runuser -u build -- makepkg --noconfirm --skipchecksums && \
runuser -u build -- makepkg --packagelist | grep -v -- -debug- | pacman -U --noconfirm --nodeps - && \
cd / && rm -r "/home/build/ahriman"
# cleanup unused
RUN find "/var/cache/pacman/pkg" -type f -delete
RUN pacman -Qdtq | pacman -Rscn --noconfirm -
VOLUME ["/var/lib/ahriman"]
# minimal runtime ahriman setup
## FIXME since 1.0.4 devtools requires dbus to be run, which doesn't work now in container
COPY "docker/systemd-nspawn.sh" "/usr/local/bin/systemd-nspawn"
## entrypoint setup
COPY "docker/entrypoint.sh" "/usr/local/bin/entrypoint"
COPY "docker/entrypoint-web.sh" "/usr/local/bin/entrypoint-web"
ENTRYPOINT ["entrypoint"]
# default command
CMD ["repo-update", "--refresh"]

View File

@ -1,5 +0,0 @@
#!/bin/bash
# Special workaround for running web service in github actions, must not be usually used in real environment,
# consider running web command explicitly instead
exec entrypoint web "$@"

View File

@ -57,9 +57,6 @@ ahriman "${AHRIMAN_DEFAULT_ARGS[@]}" service-setup "${AHRIMAN_SETUP_ARGS[@]}"
# create machine-id which is required by build tools
systemd-machine-id-setup &> /dev/null
# special workaround to emulate /bin/bash entrypoint if first argument starts with /
[[ "$1" =~ ^/.* ]] && exec "$@"
# if AHRIMAN_FORCE_ROOT is set or command is unsafe we can run without sudo
# otherwise we prepend executable by sudo command
if [ -n "$AHRIMAN_FORCE_ROOT" ]; then

View File

@ -4,12 +4,8 @@ set -e
for PACKAGE in "$@"; do
BUILD_DIR="$(mktemp -d)"
# clone the remote source
git clone https://aur.archlinux.org/"$PACKAGE".git "$BUILD_DIR"
cd "$BUILD_DIR"
# checkout to the image date
git checkout "$(git rev-list -1 --before="$(stat -c "%y" "/var/lib/pacman" | cut -d " " -f 1)" master)"
# build and install the package
makepkg --nocheck --noconfirm --install --rmdeps --syncdeps
cd /
rm -r "$BUILD_DIR"

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,6 @@
Advanced usage
==============
.. toctree::
:maxdepth: 2
handlers
views
Depending on the goal the package can be used in different ways. Nevertheless, in the most cases you will need some basic classes
.. code-block:: python

View File

@ -1,51 +0,0 @@
Writing own handler
===================
It is possible to extend the application by adding own custom commands. To do so it is required to implement class, which derives from ``ahriman.application.handlers.handler.Handler`` and put it to the ``ahriman.application.handlers`` package. The class later will be loaded automatically and included to each command run.
Let's imagine, that the new class implements ``help-web``, which prints server information to the stdout. To do so, we need to implement base ``ahriman.application.handlers.handler.Handler.run`` method which is entry point for all subcommands:
.. code-block:: python
from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler
class HelpWeb(Handler):
@classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
report: bool) -> None:
# load application instance
# report is set to True to make sure that web client is loaded
application = Application(repository_id, configuration, report=True)
# extract web client
client = application.repository.reporter
# send request to the server
response = client.make_request("GET", f"{client.address}/api/v1/info")
result = response.json()
print(result)
The main functionality of the class is already described, but command is still not available yet. To do so, it is required to set ``arguments`` property, which is the list of the functions, each of them which takes argument parser object, creates new subcommand and returns the modified parser, e.g.:
.. code-block:: python
import argparse
from ahriman.application.handlers.handler import SubParserAction
...
@staticmethod
def set_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser = root.add_parser("help-web", help="get web server status",
description="request server info and print it to stdout")
arguments = set_parser
In addition, ``ahriman.application.handlers.handler.Handler.ALLOW_MULTI_ARCHITECTURE_RUN`` can be set to ``False`` in order to disable multiprocess run (e.g. in case if there are conflicting operations, like writing to stdout).
Save the file above as ``/usr/lib/python3.12/site-packages/ahriman/application/handlers/help_web.py`` (replace ``python3.12`` with actual python version) and you are set.
For more examples and details, please check builtin handlers and classes documentations.

View File

@ -1,41 +0,0 @@
Writing own API endpoint
========================
The web service loads views dynamically, thus it is possible to add custom API endpoint or even web page. The view must be derived from ``ahriman.web.views.base.BaseView`` and should implement desired HTTP methods. The API specification will be also loaded automatically if available, but optional. The implementation must be saved into the ``ahriman.web.views`` package
Let's consider example of API endpoint which always returns 204 with no response:
.. code-block:: python
from aiohttp.web import Response, HTTPNoContent
from ahriman.web.views.base import BaseView
class PingView(BaseView):
async def get(self) -> Response:
# do nothing, just raise 204 response
# check public methods of the BaseView class for all available controls
raise HTTPNoContent
The ``get()`` method can be decorated by ``aiohttp_apispec`` methods, but we will leave it for a self-study, please, consider to check examples of usages in the main package.
In order to view to be added to the route list correctly, few more properties are required to be set. First of all, it is required to specify ``ROUTES`` (list of strings), which contains list of all available routes, e.g.:
.. code-block:: python
...
ROUTES = ["/api/v1/ping"]
In addition, it is also recommended to specify permission level for using this endpoint. Since this endpoint neither does anything nor returns sensitive information, it can be set to ``UserAccess.Unauthorized``:
.. code-block:: python
...
GET_PERMISSION = UserAccess.Unauthorized
That's all. Just save the file as ``/usr/lib/python3.12/site-packages/ahriman/web/views/ping.py`` (replace ``python3.12`` with actual python version) and restart web server.
For more examples and details, please check builtin handlers and classes documentations.

View File

@ -228,14 +228,6 @@ ahriman.application.handlers.triggers module
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.triggers\_support module
-----------------------------------------------------
.. automodule:: ahriman.application.handlers.triggers_support
:members:
:no-undoc-members:
:show-inheritance:
ahriman.application.handlers.unsafe\_commands module
----------------------------------------------------

View File

@ -8,8 +8,7 @@ Packages have strict rules of importing:
* ``ahriman.application`` package must not be used outside of this package.
* ``ahriman.core`` and ``ahriman.models`` packages don't have any import restriction. Actually we would like to totally restrict importing of ``core`` package from ``models``, but it is impossible at the moment.
* ``ahriman.web`` package is allowed to be imported from ``ahriman.application`` (web handler only, only ``ahriman.web.web`` methods).
* The idea remains the same for all imports, if an package requires some specific dependencies, it must be imported locally to keep dependencies optional.
* ``ahriman.web`` package is allowed to be imported from ``ahriman.application`` (web handler only, only ``ahriman.web.web`` methods). It also must not be imported globally, only local import is allowed.
Full dependency diagram:
@ -19,7 +18,7 @@ Full dependency diagram:
``ahriman.application`` package
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This package contains application (aka executable) related classes and everything for it. It also contains package called ``ahriman.application.handlers`` in which all available subcommands are described as separated classes derived from the base ``ahriman.application.handlers.handler.Handler`` class. Those classes are being loaded dynamically through the lookup of the ``ahriman.application.handlers`` package.
This package contains application (aka executable) related classes and everything for it. It also contains package called ``ahriman.application.handlers`` in which all available subcommands are described as separated classes derived from the base ``ahriman.application.handlers.handler.Handler`` class.
``ahriman.application.application.Application`` (god class) is used for any interaction from parsers with repository. It is divided into multiple traits by functions (package related and repository related) in the same package.
@ -33,9 +32,9 @@ This package contains application (aka executable) related classes and everythin
This package contains everything required for the most of application actions and it is separated into several packages:
* ``ahriman.core.alpm`` package controls pacman related functions. It provides wrappers for ``pyalpm`` library and safe calls for repository tools (``repo-add`` and ``repo-remove``). Also this package contains ``ahriman.core.alpm.remote`` package which provides wrapper for remote sources (e.g. AUR RPC and official repositories RPC) and some other helpers.
* ``ahriman.core.auth`` package provides classes for authorization methods used by web mostly. Base class is ``ahriman.core.auth.Auth`` which must be instantiated by ``load`` method. This package is only required by the ``ahriman.web`` package.
* ``ahriman.core.auth`` package provides classes for authorization methods used by web mostly. Base class is ``ahriman.core.auth.Auth`` which must be instantiated by ``load`` method.
* ``ahriman.core.build_tools`` is a package which provides wrapper for ``devtools`` commands.
* ``ahriman.core.configuration`` contains extensions for standard ``configparser`` module and some validation related classes.
* ``ahriman.core.configuration`` contains extension for standard ``configparser`` library and some validation related classes.
* ``ahriman.core.database`` is everything for database, including data and schema migrations.
* ``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.
@ -53,11 +52,8 @@ This package contains everything required for the most of application actions an
This package also provides some generic functions and classes which may be used by other packages:
* ``ahriman.core.exceptions`` provides custom exceptions.
* ``ahriman.core.module_loader`` provides ``implementations`` method which can be used for dynamic classes load. In particular, this method is used for web views and application handlers loading.
* ``ahriman.core.spawn.Spawn`` is a tool which can spawn another ``ahriman`` process. This feature is used by web application.
* ``ahriman.core.tree`` is a dependency tree implementation.
* ``ahriman.core.types`` are an additional global types for mypy checks.
* ``ahriman.core.utils`` contains some useful functions which are not the part of any other class.
``ahriman.models`` package
^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -71,7 +67,7 @@ Web application. It is important that this package is isolated from any other to
* ``ahriman.web.middlewares`` provides middlewares for request handlers.
* ``ahriman.web.schemas`` provides schemas (actually copy paste from dataclasses) used by swagger documentation.
* ``ahriman.web.views`` contains web views derived from aiohttp view class. Those classes are loaded dynamically through the filesystem lookup.
* ``ahriman.web.views`` contains web views derived from aiohttp view class.
* ``ahriman.web.apispec`` provides generators for swagger documentation.
* ``ahriman.web.cors`` contains helpers for cross origin resource sharing middlewares.
* ``ahriman.web.routes`` creates routes for web application.
@ -82,7 +78,7 @@ Application run
#. Parse command line arguments, find subcommand and related handler which is set by the parser.
#. Call ``Handler.execute`` method.
#. Define list of architectures to run. In case if there is more than one architecture specified run several subprocesses or continue in current process otherwise. Class attribute ``ALLOW_MULTI_ARCHITECTURE_RUN`` controls whether the application can be run in multiple processes or not - this feature is required for some handlers (e.g. ``Config``, which utilizes stdout to print messages).
#. Define list of architectures to run. In case if there is more than one architecture specified run several subprocesses or continue in current process otherwise. Class attribute ``ALLOW_MULTI_ARCHITECTURE_RUN`` controls whether the application can be run in multiple processes or not - this feature is required for some handlers (e.g. ``Web``, which should be able to spawn child process in daemon mode; it is impossible to do from daemonic processes).
#. In each child process call lock functions.
#. After success checks pass control to ``Handler.run`` method defined by specific handler class.
#. Return result (success or failure) of each subprocess and exit from application.
@ -177,28 +173,28 @@ Type conversions
By default, it parses rows into python dictionary. In addition, the following pseudo-types are supported:
* ``dict[str, Any]`` and ``list[Any]`` - for storing JSON data structures in database (technically there is no restriction on types for dictionary keys and values, but it is recommended to use only string keys). The type is stored as ``json`` data type and ``json.loads`` and ``json.dumps`` methods are used in order to read and write from/to database respectively.
* ``dict[str, Any]``, ``list[Any]`` - for storing JSON data structures in database (technically there is no restriction on types for dictionary keys and values, but it is recommended to use only string keys). The type is stored as ``json`` data type and ``json.loads`` and ``json.dumps`` methods are used in order to read and write from/to database respectively.
Basic flows
-----------
By default package build operations are performed with ``PACKAGER`` which is specified in ``makepkg.conf``, however, it is possible to override this variable from command line; in this case service performs lookup in the following way:
* If packager is not set, it reads environment variables (e.g. ``DOAS_USER``, ``SUDO_USER`` and ``USER``), otherwise it uses value from command line.
* If packager is not set, it reads environment variables (e.g. ``SUDO_USER`` and ``USER``), otherwise it uses value from command line.
* It checks users for the specified username and tries to extract packager variable from it.
* If packager value has been found, it will be passed as ``PACKAGER`` system variable (additional sudo configuration to pass environment variables might be required).
* If packager value has been found, it will be passed as ``PACKAGER`` system variable (additional sudo configuration might be required).
Add new packages or rebuild existing
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The idea is to add package to a build queue from which it will be handled automatically during the next update run. Different variants are supported:
Idea is to add package to a build queue from which it will be handled automatically during the next update run. Different variants are supported:
* If supplied argument is file, then application moves the file to the directory with the built packages. Same rule is applied for directory, but in this case it copies every package-like file from the specified directory.
* If supplied argument is file, then application moves the file to the directory with built packages. Same rule applies for directory, but in this case it copies every package-like file from the specified directory.
* If supplied argument is directory and there is ``PKGBUILD`` file there, it will be treated as local package. In this case it will queue this package to build and copy source files (``PKGBUILD`` and ``.SRCINFO``) to caches.
* If supplied argument looks like URL (i.e. it has scheme, which is neither ``data`` nor ``file``, e.g. ``http://``), it tries to download the package from the specified remote source.
* If supplied argument is not file then application tries to lookup for the specified name in AUR and clones it into the temporary directory, from which it will be added into the build queue. This scenario can also handle package dependencies which are missing in repositories.
* If supplied argument looks like URL (i.e. it has scheme - e.g. ``http://`` which is neither ``data`` nor ``file``), it tries to download the package from the specified remote source.
* If supplied argument is not file then application tries to lookup for the specified name in AUR and clones it into the directory with manual updates. This scenario can also handle package dependencies which are missing in repositories.
This logic can be overwritten by specifying the ``source`` parameter, which is partially useful if you would like to add package from AUR, but there is local directory cloned from AUR. Also the official repositories calls are hidden behind explicit source definition.
This logic can be overwritten by specifying the ``source`` parameter, which is partially useful if you would like to add package from AUR, but there is local directory cloned from AUR. Also official repositories calls are hidden behind explicit source definition.
Rebuild packages
^^^^^^^^^^^^^^^^
@ -223,7 +219,7 @@ There are few ways for packages to be marked as out-of-date and hence requiring
Update packages
^^^^^^^^^^^^^^^
This feature is divided into the following stages: check AUR for updates and run rebuild for required packages. The package update flow is the following:
This feature is divided into to the following stages: check AUR for updates and run rebuild for required packages. The package update flow is the following:
#. Process every built package first. Those packages are usually added manually.
#. Run sync and report methods.
@ -257,17 +253,18 @@ The upload process is performed via special API endpoint, which is disabled by d
After success upload, the update process must be called as usual in order to copy built packages to the main repository tree.
On the other side, the delegation uses upload feature, but in addition it also calls external services in order to trigger build process. The packages are separated into the chunks based on the amount of the configured workers and their dependencies.
On the other side, the delegation uses upload feature, but in addition it also calls external services in order to trigger build process. The packages are separated to chunks based on the amount of the configured workers and their dependencies.
pkgrel bump rules
^^^^^^^^^^^^^^^^^
The application is able to automatically bump package release (``pkgrel`` variable) during the build process if there is duplicated version in the repository. The version will be incremented as following:
The application is able to automatically bump package release (``pkgrel``) during build process if there is duplicate version in repository. The version will be incremented as following:
#. Get version of the remote package.
#. Get version of the local package if available.
#. If the local version is not set, proceed with the remote one.
#. If the local version is set and the remote version is newer than local one, proceed with remote.
#. If local version is not set, proceed with remote one.
#. If local version is set and epoch or package version (``pkgver``) are different, proceed with remote version.
#. If local version is set and remote version is newer than local one, proceed with remote.
#. Extract ``pkgrel`` value.
#. If it has ``major.minor`` notation (e.g. ``1.1``), then increment last part by 1, e.g. ``1.1 -> 1.2``, ``1.0.1 -> 1.0.2``.
#. If ``pkgrel`` is a number (e.g. ``1``), then append 1 to the end of the string, e.g. ``1 -> 1.1``.
@ -275,9 +272,9 @@ The application is able to automatically bump package release (``pkgrel`` variab
Implicit dependencies resolution
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to the depends/optional/make/check depends lists the server also handles implicit dependencies. After success build, the application traverse through the build tree and finds:
In addition to the depends/optional/make/check depends lists the server also handles implicit dependencies. After success build, the application traverse through the build tree and finds
* Libraries to which the binaries (ELF-files) are linked. To do so, the ``NEEDED`` section of the ELF-files is read.
* Libraries to which the binaries (ELF-files) are linked. To do so, the ``NEEDED`` section of the ELF-files are read.
* Directories which contains files of the package, but do not belong to this package. This case covers, for example, python and ruby submodules.
Having the initial dependencies tree, the application is looking for packages which contains those (both files and directories) paths and creates the initial packages list. After that, the packages list is reduced in the following way:
@ -288,8 +285,6 @@ Having the initial dependencies tree, the application is looking for packages wh
* After that, if there is a package which *optionally* depends on the another package in the remaining list, the package will be removed.
* And finally, if there is any path, which is the child of the entry, and it contains the same package, the package from the smaller entry will be removed.
Those paths are also filtered by regular expressions set in the configuration.
All those implicit dependencies are stored in the database and extracted on each check. In case if any of the repository packages doesn't contain any entry anymore (e.g. so version has been changed or modules directory has been changed), the dependent package will be marked as out-of-dated.
Core functions reference
@ -329,7 +324,7 @@ Some packages provide different behaviour depending on configuration settings. I
Authorization
^^^^^^^^^^^^^
The package provides several authorization methods: disabled, based on configuration, PAM and OAuth2.
The package provides several authorization methods: disabled, based on configuration and OAuth2.
Disabled (default) authorization provider just allows everything for everyone and does not have any specific configuration (it uses some default configuration parameters though). It also provides generic interface for derived classes.
@ -338,7 +333,7 @@ Mapping (aka configuration) provider uses hashed passwords with optional salt fr
* ``check_credentials`` - user password validation (authentication).
* ``verify_access`` - user permission validation (authorization).
Passwords must be stored in database as ``hash(password + salt)``, where ``password`` is user defined password (taken from user input), ``salt`` is random string (any length) defined globally in configuration and ``hash`` is a secure hash function. Thus, the following configuration
Passwords must be stored in database as ``hash(password + salt)``, where ``password`` is user defined password (taken from user input), ``salt`` is random string (any length) defined globally in configuration and ``hash`` is secure hash function. Thus, the following configuration
.. code-block::
@ -351,7 +346,7 @@ OAuth provider uses library definitions (``aioauth-client``) in order *authentic
OAuth's implementation also allows authenticating users via username + password (in the same way as mapping does) though it is not recommended for end-users and password must be left blank. In particular this feature can be used by service reporting (aka robots).
In addition, web service checks the source socket used. In case if it belongs to ``socket.AF_UNIX`` family, it will skip any further checks considering the request to be performed in safe environment (e.g. on the same physical machine). This feature, in particular is being used by the reporter instances in case if socket address is set in configuration. Note, however, that this behaviour can be disabled by configuration.
In addition, web service checks the source socket used. In case if it belongs to ``socket.AF_UNIX`` family, it will skip any further checks considering the request to be performed in safe environment (e.g. on the same physical machine). This feature, in particular is being used by the reporter instances in case if socket address is set in configuration.
In order to configure users there are special subcommands.
@ -386,7 +381,7 @@ The application provides a house-made shell parser ``ahriman.core.alpm.pkgbuild_
#. During the parser process, firstly, it extract next token from the source file (basically, the word) and tries to match it to the variable assignment. If so, then just processes accordingly.
#. If it is not an assignment, the parser checks if the token was quoted.
#. If it wasn't quoted then the parser tries to match the array starts (two consecutive tokens like ``array=`` and ``(``) or it is function (``function``, ``()`` and ``{``).
#. If it wasn't then the parser tries to match the array starts (two consecutive tokens like ``array=`` and ``(``) or it is function (``function``, ``()`` and ``{``).
#. The arrays are processed until the next closing bracket ``)``. After extraction, the parser tries to expand an array according to bash rules (``prefix{first,second}suffix`` constructions).
#. The functions are just read until the closing bracket ``}`` and then reread whole text from the input string without a tokenization.
@ -394,7 +389,7 @@ All extracted fields are packed as ``ahriman.models.pkgbuild_patch.PkgbuildPatch
The PKGBUILD class also provides some additional functions on top of that:
* Ability to extract fields defined inside ``package*()`` functions, which are in particular used for the multi-packages.
* Ability to extract fields defined inside ``package*()`` functions, which are in particular used for the multipackages.
* Shell substitution, which supports constructions ``$var`` (including ``${var}``), ``${var#(#)pattern}``, ``${var%(%)pattern}`` and ``${var/(/)pattern/replacement}`` (including ``#pattern`` and ``%pattern``).
Additional features
@ -402,6 +397,7 @@ Additional features
Some features require optional dependencies to be installed:
* Version control executables (e.g. ``git``, ``svn``) for VCS packages.
* ``gnupg`` application for package and repository sign feature.
* ``rsync`` application for rsync based repository sync.
* ``boto3`` python package for ``S3`` sync.
@ -440,9 +436,6 @@ REST API supports only JSON data.
Different APIs are separated into different packages:
* ``ahriman.web.views.api`` not a real API, but some views which provide OpenAPI support.
* ``ahriman.web.views.*.auditlog`` provides event log API.
* ``ahriman.web.views.*.distributed`` is an API for builders interaction for multi-node setup.
* ``ahriman.web.views.*.packages`` contains views which provide information about existing packages.
* ``ahriman.web.views.*.service`` provides views for application controls.
* ``ahriman.web.views.*.status`` package provides REST API for application reporting.
* ``ahriman.web.views.*.user`` package provides login and logout methods which can be called without authorization.
@ -461,7 +454,7 @@ Service provides optional authorization which can be turned on in settings. In o
If this feature is configured any request will be prohibited without authentication. In addition, configuration flag ``auth.allow_read_only`` can be used in order to allow read-only operations - reading index page and packages - without authorization.
For authenticated users it uses encrypted session cookies to store tokens; encryption key is read from configuration or generated at the start of the application if not set. It also stores expiration time of the session inside.
For authenticated users it uses encrypted session cookies to store tokens; encryption key is generated each time at the start of the application. It also stores expiration time of the session inside.
External calls
^^^^^^^^^^^^^^

View File

@ -311,12 +311,6 @@ Automatic worker nodes discovery
Instead of setting ``${build:workers}`` option explicitly it is also possible to configure services to load worker list dynamically. To do so, the ``ahriman.core.distributed.WorkerLoaderTrigger`` and ``ahriman.core.distributed.WorkerTrigger`` must be used for **master** and **worker** nodes respectively. See recipes for more details.
Those triggers have to be installed as a separate package:
.. code-block:: shell
yay -S ahriman-triggers
Known limitations
"""""""""""""""""

View File

@ -16,7 +16,7 @@ The default action (in case if no arguments provided) is ``repo-update``. Basica
docker run --privileged -v /path/to/local/repo:/var/lib/ahriman arcan1s/ahriman:latest
In order to make data available outside of container, you would need to mount local (parent) directory inside container by using ``-v /path/to/local/repo:/var/lib/ahriman`` argument, where ``/path/to/local/repo`` is a path to repository on local machine. In addition, you can pass own configuration overrides by using the same ``-v`` flag, e.g.:
``--privileged`` flag is required to make mount possible inside container. In order to make data available outside of container, you would need to mount local (parent) directory inside container by using ``-v /path/to/local/repo:/var/lib/ahriman`` argument, where ``/path/to/local/repo`` is a path to repository on local machine. In addition, you can pass own configuration overrides by using the same ``-v`` flag, e.g.:
.. code-block:: shell
@ -30,28 +30,6 @@ The action can be specified during run, e.g.:
For more details please refer to the docker FAQ.
Privileged and non-privileged container
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Examples here suggest using ``--privileged`` flag which is required for the devtools and is involved in two types of operations: tmpfs mount and cgroup manipulation. Whereas it is the easiest way to operate, it might be not really secure. The other way to make devtools working is to grant required capabilities, which can be achieved by using flags:
* ``--cap-add=SYS_ADMIN``, which grants permissions to operate with tmpfs for ``systemd-nspawn``.
* ``-v /sys/fs/cgroup:/sys/fs/cgroup`` which allows access to cgroup manipulation.
Thus, there are two possible ways to run the container:
.. code-block:: shell
docker run --privileged arcan1s/ahriman:latest
and
.. code-block:: shell
docker run --cap-add=SYS_ADMIN -v /sys/fs/cgroup:/sys/fs/cgroup arcan1s/ahriman:latest
but for the simplicity this FAQ will always use ``--privileged`` flag.
Environment variables
^^^^^^^^^^^^^^^^^^^^^
@ -119,7 +97,7 @@ Otherwise, you would need to pass ``AHRIMAN_PORT`` and mount container network t
Simple server with authentication can be found in `examples <https://github.com/arcan1s/ahriman/tree/master/recipes/web>`__ too.
Multi-repository web service
Mutli-repository web service
""""""""""""""""""""""""""""
Idea is pretty same as to just run web service. However, it is required to run setup commands for each repository, except for one which is specified by ``AHRIMAN_REPOSITORY`` and ``AHRIMAN_ARCHITECTURE`` variables.

View File

@ -13,7 +13,7 @@ TL;DR
.. code-block:: shell
yay -S ahriman-core
yay -S ahriman
ahriman -a x86_64 -r aur service-setup --packager "ahriman bot <ahriman@example.com>"
systemctl enable --now ahriman@x86_64-aur.timer

View File

@ -1,12 +1,6 @@
Maintenance packages
--------------------
Those features require extensions package to be installed before, e.g.:
.. code-block:: shell
yay -S ahriman-triggers
Generate keyring package
^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -5,11 +5,11 @@ How to setup web service
^^^^^^^^^^^^^^^^^^^^^^^^
#.
Install web service:
Install dependencies:
.. code-block:: shell
yay -S ahriman-web
yay -S --asdeps python-aiohttp python-aiohttp-jinja2 python-aiohttp-apispec>=3.0.0 python-aiohttp-cors
#.
Configure service:

View File

@ -36,6 +36,6 @@ Contents
faq/index
migrations/index
architecture
advanced-usage/index
advanced-usage
triggers
modules

View File

@ -3,7 +3,7 @@ To 2.16.0
This release replaces ``passlib`` dependency with ``bcrypt``.
The reason behind this change is that python developers have deprecated and scheduled for removal ``crypt`` module, which is used by ``passlib``. (By the way, they recommend to use ``passlib`` as a replacement.) Unfortunately, it appears that ``passlib`` is unmaintained (see `the issue <https://foss.heptapod.net/python-libs/passlib/-/issues/187>`__), so the only solution is to migrate to another library.
The reason behind this change is that python developers have deprecated and scheduled for removal ``crypt`` module, which is used by ``passlib``. (By the way, they recommend to use ``passlib`` as a replacement.) Unfortunately, it appears that ``passlib`` is unmaintained (see `the issue <https://foss.heptapod.net/python-libs/passlib/-/issues/187>`__), so the only solution is to migrate to anoher library.
Because passwords are stored as hashes, it is near to impossible to shadow change passwords in database, the manual intervention is required if:

View File

@ -2,7 +2,7 @@ Initial setup
=============
#.
Install package(s) as usual. At least, ``ahriman-core`` package is required; other features can be installed separately. Alternatively, it is possible to install meta-package, which includes everything.
Install package as usual.
#.
Change settings if required, see :doc:`configuration reference <configuration>` for more details.
#.

View File

@ -1,8 +1,8 @@
# Maintainer: Evgeniy Alekseev
pkgbase='ahriman'
pkgname=('ahriman' 'ahriman-core' 'ahriman-triggers' 'ahriman-web')
pkgver=2.16.0
pkgname=('ahriman' 'ahriman-triggers' 'ahriman-web')
pkgver=2.15.2
pkgrel=1
pkgdesc="ArcH linux ReposItory MANager"
arch=('any')
@ -22,12 +22,6 @@ build() {
package_ahriman() {
pkgname='ahriman'
pkgdesc="ArcH linux ReposItory MANager (meta package)"
depends=("$pkgbase-core=$pkgver" "$pkgbase-triggers=$pkgver" "$pkgbase-web=$pkgver")
}
package_ahriman-core() {
pkgname='ahriman-core'
optdepends=('ahriman-triggers: additional extensions for the application'
'ahriman-web: web server'
'python-boto3: sync to s3'
@ -37,7 +31,7 @@ package_ahriman-core() {
'python-jinja: html report generation'
'python-systemd: journal support'
'rsync: sync by using rsync')
install="$pkgbase.install"
install="$pkgname.install"
backup=('etc/ahriman.ini'
'etc/ahriman.ini.d/logging.ini')
@ -57,7 +51,7 @@ package_ahriman-core() {
package_ahriman-triggers() {
pkgname='ahriman-triggers'
pkgdesc="ArcH linux ReposItory MANager, additional extensions"
depends=("$pkgbase-core=$pkgver")
depends=("$pkgbase=$pkgver")
backup=('etc/ahriman.ini.d/00-triggers.ini')
cd "$pkgbase-$pkgver"
@ -71,7 +65,7 @@ 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=$pkgver" 'python-aiohttp-apispec>=3.0.0' 'python-aiohttp-cors' 'python-aiohttp-jinja2')
optdepends=('python-aioauth-client: OAuth2 authorization support'
'python-aiohttp-security: authorization support'
'python-aiohttp-session: authorization support'

View File

@ -2,7 +2,6 @@ post_upgrade() {
local breakpoints=(
2.9.0-1
2.12.0-1
2.16.0-1
)
for v in "${breakpoints[@]}"; do

View File

@ -163,7 +163,7 @@
const variableValueInput = document.createElement("input");
variableValueInput.classList.add("form-control");
variableValueInput.readOnly = true;
variableValueInput.value = JSON.stringify(variable.value);
variableValueInput.value = variable.value;
const variableButtonRemove = document.createElement("button");
variableButtonRemove.type = "button";

View File

@ -1,117 +1,115 @@
# AUTOMATICALLY GENERATED by `shtab`
_shtab_ahriman_subparsers=('add' 'aur-search' 'check' 'clean' 'config' 'config-validate' 'copy' 'daemon' 'help' 'help-commands-unsafe' 'help-updates' 'help-version' 'init' 'key-import' 'package-add' 'package-changes' 'package-changes-remove' 'package-copy' 'package-remove' 'package-status' 'package-status-remove' 'package-status-update' 'package-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'rebuild' 'remove' 'remove-unknown' 'repo-backup' 'repo-check' 'repo-clean' 'repo-config' 'repo-config-validate' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'repo-init' 'repo-rebuild' 'repo-remove-unknown' 'repo-report' 'repo-restore' 'repo-setup' 'repo-sign' 'repo-statistics' 'repo-status-update' 'repo-sync' 'repo-tree' 'repo-triggers' 'repo-update' 'report' 'run' 'search' 'service-clean' 'service-config' 'service-config-validate' 'service-key-import' 'service-repositories' 'service-run' 'service-setup' 'service-shell' 'service-tree-migrate' 'setup' 'shell' 'sign' 'status' 'status-update' 'sync' 'update' 'user-add' 'user-list' 'user-remove' 'version' 'web')
_shtab_ahriman_subparsers=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-statistics' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--log-handler' '-q' '--quiet' '--report' '--no-report' '-r' '--repository' '--unsafe' '-V' '--version' '--wait-timeout')
_shtab_ahriman_add_option_strings=('-h' '--help' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_aur_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
_shtab_ahriman_check_option_strings=('-h' '--help' '--changes' '--no-changes' '--check-files' '--no-check-files' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_config_option_strings=('-h' '--help' '--info' '--no-info' '--secure' '--no-secure')
_shtab_ahriman_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_copy_option_strings=('-h' '--help' '-e' '--exit-code' '--remove')
_shtab_ahriman_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '--partitions' '--no-partitions' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_help_option_strings=('-h' '--help')
_shtab_ahriman_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
_shtab_ahriman_help_commands_unsafe_option_strings=('-h' '--help')
_shtab_ahriman_help_option_strings=('-h' '--help')
_shtab_ahriman_help_updates_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_help_version_option_strings=('-h' '--help')
_shtab_ahriman_init_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_key_import_option_strings=('-h' '--help' '--key-server')
_shtab_ahriman_version_option_strings=('-h' '--help')
_shtab_ahriman_package_add_option_strings=('-h' '--help' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_add_option_strings=('-h' '--help' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_package_changes_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_package_changes_remove_option_strings=('-h' '--help')
_shtab_ahriman_package_copy_option_strings=('-h' '--help' '-e' '--exit-code' '--remove')
_shtab_ahriman_package_remove_option_strings=('-h' '--help')
_shtab_ahriman_remove_option_strings=('-h' '--help')
_shtab_ahriman_package_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
_shtab_ahriman_status_option_strings=('-h' '--help' '--ahriman' '-e' '--exit-code' '--info' '--no-info' '-s' '--status')
_shtab_ahriman_package_status_remove_option_strings=('-h' '--help')
_shtab_ahriman_package_status_update_option_strings=('-h' '--help' '-s' '--status')
_shtab_ahriman_package_update_option_strings=('-h' '--help' '--changes' '--no-changes' '--dependencies' '--no-dependencies' '-e' '--exit-code' '--increment' '--no-increment' '-n' '--now' '-y' '--refresh' '-s' '--source' '-u' '--username' '-v' '--variable')
_shtab_ahriman_status_update_option_strings=('-h' '--help' '-s' '--status')
_shtab_ahriman_patch_add_option_strings=('-h' '--help')
_shtab_ahriman_patch_list_option_strings=('-h' '--help' '-e' '--exit-code' '-v' '--variable')
_shtab_ahriman_patch_remove_option_strings=('-h' '--help' '-v' '--variable')
_shtab_ahriman_patch_set_add_option_strings=('-h' '--help' '-t' '--track')
_shtab_ahriman_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
_shtab_ahriman_remove_option_strings=('-h' '--help')
_shtab_ahriman_remove_unknown_option_strings=('-h' '--help' '--dry-run')
_shtab_ahriman_repo_backup_option_strings=('-h' '--help')
_shtab_ahriman_repo_check_option_strings=('-h' '--help' '--changes' '--no-changes' '--check-files' '--no-check-files' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_repo_config_option_strings=('-h' '--help' '--info' '--no-info' '--secure' '--no-secure')
_shtab_ahriman_repo_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_check_option_strings=('-h' '--help' '--changes' '--no-changes' '--check-files' '--no-check-files' '-e' '--exit-code' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_create_keyring_option_strings=('-h' '--help')
_shtab_ahriman_repo_create_mirrorlist_option_strings=('-h' '--help')
_shtab_ahriman_repo_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '--partitions' '--no-partitions' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_init_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_daemon_option_strings=('-h' '--help' '-i' '--interval' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '--partitions' '--no-partitions' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_repo_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
_shtab_ahriman_rebuild_option_strings=('-h' '--help' '--depends-on' '--dry-run' '--from-database' '--increment' '--no-increment' '-e' '--exit-code' '-s' '--status' '-u' '--username')
_shtab_ahriman_repo_remove_unknown_option_strings=('-h' '--help' '--dry-run')
_shtab_ahriman_remove_unknown_option_strings=('-h' '--help' '--dry-run')
_shtab_ahriman_repo_report_option_strings=('-h' '--help')
_shtab_ahriman_report_option_strings=('-h' '--help')
_shtab_ahriman_repo_restore_option_strings=('-h' '--help' '-o' '--output')
_shtab_ahriman_repo_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_repo_sign_option_strings=('-h' '--help')
_shtab_ahriman_sign_option_strings=('-h' '--help')
_shtab_ahriman_repo_statistics_option_strings=('-h' '--help' '--chart' '-e' '--event' '--from-date' '--limit' '--offset' '--to-date')
_shtab_ahriman_repo_status_update_option_strings=('-h' '--help' '-s' '--status')
_shtab_ahriman_repo_sync_option_strings=('-h' '--help')
_shtab_ahriman_sync_option_strings=('-h' '--help')
_shtab_ahriman_repo_tree_option_strings=('-h' '--help' '-p' '--partitions')
_shtab_ahriman_repo_triggers_option_strings=('-h' '--help')
_shtab_ahriman_repo_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_report_option_strings=('-h' '--help')
_shtab_ahriman_run_option_strings=('-h' '--help')
_shtab_ahriman_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by')
_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_service_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_repo_clean_option_strings=('-h' '--help' '--cache' '--no-cache' '--chroot' '--no-chroot' '--manual' '--no-manual' '--packages' '--no-packages' '--pacman' '--no-pacman')
_shtab_ahriman_service_config_option_strings=('-h' '--help' '--info' '--no-info' '--secure' '--no-secure')
_shtab_ahriman_config_option_strings=('-h' '--help' '--info' '--no-info' '--secure' '--no-secure')
_shtab_ahriman_repo_config_option_strings=('-h' '--help' '--info' '--no-info' '--secure' '--no-secure')
_shtab_ahriman_service_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_repo_config_validate_option_strings=('-h' '--help' '-e' '--exit-code')
_shtab_ahriman_service_key_import_option_strings=('-h' '--help' '--key-server')
_shtab_ahriman_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_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_tree_migrate_option_strings=('-h' '--help')
_shtab_ahriman_init_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_repo_init_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_repo_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_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_shell_option_strings=('-h' '--help')
_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')
_shtab_ahriman_sync_option_strings=('-h' '--help')
_shtab_ahriman_update_option_strings=('-h' '--help' '--aur' '--no-aur' '--changes' '--no-changes' '--check-files' '--no-check-files' '--dependencies' '--no-dependencies' '--dry-run' '-e' '--exit-code' '--increment' '--no-increment' '--local' '--no-local' '--manual' '--no-manual' '-u' '--username' '--vcs' '--no-vcs' '-y' '--refresh')
_shtab_ahriman_service_tree_migrate_option_strings=('-h' '--help')
_shtab_ahriman_user_add_option_strings=('-h' '--help' '--key' '--packager' '-p' '--password' '-R' '--role')
_shtab_ahriman_user_list_option_strings=('-h' '--help' '-e' '--exit-code' '-R' '--role')
_shtab_ahriman_user_remove_option_strings=('-h' '--help')
_shtab_ahriman_version_option_strings=('-h' '--help')
_shtab_ahriman_web_option_strings=('-h' '--help')
_shtab_ahriman_pos_0_choices=('add' 'aur-search' 'check' 'clean' 'config' 'config-validate' 'copy' 'daemon' 'help' 'help-commands-unsafe' 'help-updates' 'help-version' 'init' 'key-import' 'package-add' 'package-changes' 'package-changes-remove' 'package-copy' 'package-remove' 'package-status' 'package-status-remove' 'package-status-update' 'package-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'rebuild' 'remove' 'remove-unknown' 'repo-backup' 'repo-check' 'repo-clean' 'repo-config' 'repo-config-validate' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'repo-init' 'repo-rebuild' 'repo-remove-unknown' 'repo-report' 'repo-restore' 'repo-setup' 'repo-sign' 'repo-statistics' 'repo-status-update' 'repo-sync' 'repo-tree' 'repo-triggers' 'repo-update' 'report' 'run' 'search' 'service-clean' 'service-config' 'service-config-validate' 'service-key-import' 'service-repositories' 'service-run' 'service-setup' 'service-shell' 'service-tree-migrate' 'setup' 'shell' 'sign' 'status' 'status-update' 'sync' 'update' 'user-add' 'user-list' 'user-remove' 'version' 'web')
_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help-commands-unsafe' 'help' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-changes' 'package-changes-remove' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-create-keyring' 'repo-create-mirrorlist' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-statistics' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-repositories' 'service-run' 'run' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'service-tree-migrate' 'user-add' 'user-list' 'user-remove' 'web')
_shtab_ahriman___log_handler_choices=('console' 'syslog' 'journald')
_shtab_ahriman_add__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_add___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_aur_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'submitter' 'url' 'url_path' 'version')
_shtab_ahriman_init___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'submitter' 'url' 'url_path' 'version')
_shtab_ahriman_package_add__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_add___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_status__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_add__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_add___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_update__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_package_update___source_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository')
_shtab_ahriman_rebuild__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_rebuild___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_init___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_package_status__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_package_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_rebuild__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_rebuild___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_setup___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_rebuild__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_rebuild___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_statistics__e_choices=('package-outdated' 'package-removed' 'package-update-failed' 'package-updated')
_shtab_ahriman_repo_statistics___event_choices=('package-outdated' 'package-removed' 'package-update-failed' 'package-updated')
_shtab_ahriman_repo_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_repo_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'submitter' 'url' 'url_path' 'version')
_shtab_ahriman_service_setup___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_init___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_repo_init___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_repo_setup___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_setup___sign_target_choices=('disabled' 'packages' 'repository')
_shtab_ahriman_status__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status_update__s_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_status_update___status_choices=('unknown' 'pending' 'building' 'failed' 'success')
_shtab_ahriman_user_add__R_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_user_add___role_choices=('unauthorized' 'read' 'reporter' 'full')
_shtab_ahriman_user_list__R_choices=('unauthorized' 'read' 'reporter' 'full')
@ -128,21 +126,6 @@ _shtab_ahriman___no_report_nargs=0
_shtab_ahriman___unsafe_nargs=0
_shtab_ahriman__V_nargs=0
_shtab_ahriman___version_nargs=0
_shtab_ahriman_add_pos_0_nargs=+
_shtab_ahriman_add__h_nargs=0
_shtab_ahriman_add___help_nargs=0
_shtab_ahriman_add___changes_nargs=0
_shtab_ahriman_add___no_changes_nargs=0
_shtab_ahriman_add___dependencies_nargs=0
_shtab_ahriman_add___no_dependencies_nargs=0
_shtab_ahriman_add__e_nargs=0
_shtab_ahriman_add___exit_code_nargs=0
_shtab_ahriman_add___increment_nargs=0
_shtab_ahriman_add___no_increment_nargs=0
_shtab_ahriman_add__n_nargs=0
_shtab_ahriman_add___now_nargs=0
_shtab_ahriman_add__y_nargs=0
_shtab_ahriman_add___refresh_nargs=0
_shtab_ahriman_aur_search_pos_0_nargs=+
_shtab_ahriman_aur_search__h_nargs=0
_shtab_ahriman_aur_search___help_nargs=0
@ -150,91 +133,26 @@ _shtab_ahriman_aur_search__e_nargs=0
_shtab_ahriman_aur_search___exit_code_nargs=0
_shtab_ahriman_aur_search___info_nargs=0
_shtab_ahriman_aur_search___no_info_nargs=0
_shtab_ahriman_check_pos_0_nargs=*
_shtab_ahriman_check__h_nargs=0
_shtab_ahriman_check___help_nargs=0
_shtab_ahriman_check___changes_nargs=0
_shtab_ahriman_check___no_changes_nargs=0
_shtab_ahriman_check___check_files_nargs=0
_shtab_ahriman_check___no_check_files_nargs=0
_shtab_ahriman_check__e_nargs=0
_shtab_ahriman_check___exit_code_nargs=0
_shtab_ahriman_check___vcs_nargs=0
_shtab_ahriman_check___no_vcs_nargs=0
_shtab_ahriman_check__y_nargs=0
_shtab_ahriman_check___refresh_nargs=0
_shtab_ahriman_clean__h_nargs=0
_shtab_ahriman_clean___help_nargs=0
_shtab_ahriman_clean___cache_nargs=0
_shtab_ahriman_clean___no_cache_nargs=0
_shtab_ahriman_clean___chroot_nargs=0
_shtab_ahriman_clean___no_chroot_nargs=0
_shtab_ahriman_clean___manual_nargs=0
_shtab_ahriman_clean___no_manual_nargs=0
_shtab_ahriman_clean___packages_nargs=0
_shtab_ahriman_clean___no_packages_nargs=0
_shtab_ahriman_clean___pacman_nargs=0
_shtab_ahriman_clean___no_pacman_nargs=0
_shtab_ahriman_config__h_nargs=0
_shtab_ahriman_config___help_nargs=0
_shtab_ahriman_config___info_nargs=0
_shtab_ahriman_config___no_info_nargs=0
_shtab_ahriman_config___secure_nargs=0
_shtab_ahriman_config___no_secure_nargs=0
_shtab_ahriman_config_validate__h_nargs=0
_shtab_ahriman_config_validate___help_nargs=0
_shtab_ahriman_config_validate__e_nargs=0
_shtab_ahriman_config_validate___exit_code_nargs=0
_shtab_ahriman_copy_pos_1_nargs=+
_shtab_ahriman_copy__h_nargs=0
_shtab_ahriman_copy___help_nargs=0
_shtab_ahriman_copy__e_nargs=0
_shtab_ahriman_copy___exit_code_nargs=0
_shtab_ahriman_copy___remove_nargs=0
_shtab_ahriman_daemon__h_nargs=0
_shtab_ahriman_daemon___help_nargs=0
_shtab_ahriman_daemon___aur_nargs=0
_shtab_ahriman_daemon___no_aur_nargs=0
_shtab_ahriman_daemon___changes_nargs=0
_shtab_ahriman_daemon___no_changes_nargs=0
_shtab_ahriman_daemon___check_files_nargs=0
_shtab_ahriman_daemon___no_check_files_nargs=0
_shtab_ahriman_daemon___dependencies_nargs=0
_shtab_ahriman_daemon___no_dependencies_nargs=0
_shtab_ahriman_daemon___dry_run_nargs=0
_shtab_ahriman_daemon___increment_nargs=0
_shtab_ahriman_daemon___no_increment_nargs=0
_shtab_ahriman_daemon___local_nargs=0
_shtab_ahriman_daemon___no_local_nargs=0
_shtab_ahriman_daemon___manual_nargs=0
_shtab_ahriman_daemon___no_manual_nargs=0
_shtab_ahriman_daemon___partitions_nargs=0
_shtab_ahriman_daemon___no_partitions_nargs=0
_shtab_ahriman_daemon___vcs_nargs=0
_shtab_ahriman_daemon___no_vcs_nargs=0
_shtab_ahriman_daemon__y_nargs=0
_shtab_ahriman_daemon___refresh_nargs=0
_shtab_ahriman_help__h_nargs=0
_shtab_ahriman_help___help_nargs=0
_shtab_ahriman_search_pos_0_nargs=+
_shtab_ahriman_search__h_nargs=0
_shtab_ahriman_search___help_nargs=0
_shtab_ahriman_search__e_nargs=0
_shtab_ahriman_search___exit_code_nargs=0
_shtab_ahriman_search___info_nargs=0
_shtab_ahriman_search___no_info_nargs=0
_shtab_ahriman_help_commands_unsafe_pos_0_nargs=*
_shtab_ahriman_help_commands_unsafe__h_nargs=0
_shtab_ahriman_help_commands_unsafe___help_nargs=0
_shtab_ahriman_help__h_nargs=0
_shtab_ahriman_help___help_nargs=0
_shtab_ahriman_help_updates__h_nargs=0
_shtab_ahriman_help_updates___help_nargs=0
_shtab_ahriman_help_updates__e_nargs=0
_shtab_ahriman_help_updates___exit_code_nargs=0
_shtab_ahriman_help_version__h_nargs=0
_shtab_ahriman_help_version___help_nargs=0
_shtab_ahriman_init__h_nargs=0
_shtab_ahriman_init___help_nargs=0
_shtab_ahriman_init___generate_salt_nargs=0
_shtab_ahriman_init___no_generate_salt_nargs=0
_shtab_ahriman_init___makeflags_jobs_nargs=0
_shtab_ahriman_init___no_makeflags_jobs_nargs=0
_shtab_ahriman_init___multilib_nargs=0
_shtab_ahriman_init___no_multilib_nargs=0
_shtab_ahriman_key_import__h_nargs=0
_shtab_ahriman_key_import___help_nargs=0
_shtab_ahriman_version__h_nargs=0
_shtab_ahriman_version___help_nargs=0
_shtab_ahriman_package_add_pos_0_nargs=+
_shtab_ahriman_package_add__h_nargs=0
_shtab_ahriman_package_add___help_nargs=0
@ -250,35 +168,21 @@ _shtab_ahriman_package_add__n_nargs=0
_shtab_ahriman_package_add___now_nargs=0
_shtab_ahriman_package_add__y_nargs=0
_shtab_ahriman_package_add___refresh_nargs=0
_shtab_ahriman_package_changes__h_nargs=0
_shtab_ahriman_package_changes___help_nargs=0
_shtab_ahriman_package_changes__e_nargs=0
_shtab_ahriman_package_changes___exit_code_nargs=0
_shtab_ahriman_package_changes_remove__h_nargs=0
_shtab_ahriman_package_changes_remove___help_nargs=0
_shtab_ahriman_package_copy_pos_1_nargs=+
_shtab_ahriman_package_copy__h_nargs=0
_shtab_ahriman_package_copy___help_nargs=0
_shtab_ahriman_package_copy__e_nargs=0
_shtab_ahriman_package_copy___exit_code_nargs=0
_shtab_ahriman_package_copy___remove_nargs=0
_shtab_ahriman_package_remove_pos_0_nargs=+
_shtab_ahriman_package_remove__h_nargs=0
_shtab_ahriman_package_remove___help_nargs=0
_shtab_ahriman_package_status_pos_0_nargs=*
_shtab_ahriman_package_status__h_nargs=0
_shtab_ahriman_package_status___help_nargs=0
_shtab_ahriman_package_status___ahriman_nargs=0
_shtab_ahriman_package_status__e_nargs=0
_shtab_ahriman_package_status___exit_code_nargs=0
_shtab_ahriman_package_status___info_nargs=0
_shtab_ahriman_package_status___no_info_nargs=0
_shtab_ahriman_package_status_remove_pos_0_nargs=+
_shtab_ahriman_package_status_remove__h_nargs=0
_shtab_ahriman_package_status_remove___help_nargs=0
_shtab_ahriman_package_status_update_pos_0_nargs=*
_shtab_ahriman_package_status_update__h_nargs=0
_shtab_ahriman_package_status_update___help_nargs=0
_shtab_ahriman_add_pos_0_nargs=+
_shtab_ahriman_add__h_nargs=0
_shtab_ahriman_add___help_nargs=0
_shtab_ahriman_add___changes_nargs=0
_shtab_ahriman_add___no_changes_nargs=0
_shtab_ahriman_add___dependencies_nargs=0
_shtab_ahriman_add___no_dependencies_nargs=0
_shtab_ahriman_add__e_nargs=0
_shtab_ahriman_add___exit_code_nargs=0
_shtab_ahriman_add___increment_nargs=0
_shtab_ahriman_add___no_increment_nargs=0
_shtab_ahriman_add__n_nargs=0
_shtab_ahriman_add___now_nargs=0
_shtab_ahriman_add__y_nargs=0
_shtab_ahriman_add___refresh_nargs=0
_shtab_ahriman_package_update_pos_0_nargs=+
_shtab_ahriman_package_update__h_nargs=0
_shtab_ahriman_package_update___help_nargs=0
@ -294,6 +198,43 @@ _shtab_ahriman_package_update__n_nargs=0
_shtab_ahriman_package_update___now_nargs=0
_shtab_ahriman_package_update__y_nargs=0
_shtab_ahriman_package_update___refresh_nargs=0
_shtab_ahriman_package_changes__h_nargs=0
_shtab_ahriman_package_changes___help_nargs=0
_shtab_ahriman_package_changes__e_nargs=0
_shtab_ahriman_package_changes___exit_code_nargs=0
_shtab_ahriman_package_changes_remove__h_nargs=0
_shtab_ahriman_package_changes_remove___help_nargs=0
_shtab_ahriman_package_remove_pos_0_nargs=+
_shtab_ahriman_package_remove__h_nargs=0
_shtab_ahriman_package_remove___help_nargs=0
_shtab_ahriman_remove_pos_0_nargs=+
_shtab_ahriman_remove__h_nargs=0
_shtab_ahriman_remove___help_nargs=0
_shtab_ahriman_package_status_pos_0_nargs=*
_shtab_ahriman_package_status__h_nargs=0
_shtab_ahriman_package_status___help_nargs=0
_shtab_ahriman_package_status___ahriman_nargs=0
_shtab_ahriman_package_status__e_nargs=0
_shtab_ahriman_package_status___exit_code_nargs=0
_shtab_ahriman_package_status___info_nargs=0
_shtab_ahriman_package_status___no_info_nargs=0
_shtab_ahriman_status_pos_0_nargs=*
_shtab_ahriman_status__h_nargs=0
_shtab_ahriman_status___help_nargs=0
_shtab_ahriman_status___ahriman_nargs=0
_shtab_ahriman_status__e_nargs=0
_shtab_ahriman_status___exit_code_nargs=0
_shtab_ahriman_status___info_nargs=0
_shtab_ahriman_status___no_info_nargs=0
_shtab_ahriman_package_status_remove_pos_0_nargs=+
_shtab_ahriman_package_status_remove__h_nargs=0
_shtab_ahriman_package_status_remove___help_nargs=0
_shtab_ahriman_package_status_update_pos_0_nargs=*
_shtab_ahriman_package_status_update__h_nargs=0
_shtab_ahriman_package_status_update___help_nargs=0
_shtab_ahriman_status_update_pos_0_nargs=*
_shtab_ahriman_status_update__h_nargs=0
_shtab_ahriman_status_update___help_nargs=0
_shtab_ahriman_patch_add__h_nargs=0
_shtab_ahriman_patch_add___help_nargs=0
_shtab_ahriman_patch_list__h_nargs=0
@ -304,20 +245,6 @@ _shtab_ahriman_patch_remove__h_nargs=0
_shtab_ahriman_patch_remove___help_nargs=0
_shtab_ahriman_patch_set_add__h_nargs=0
_shtab_ahriman_patch_set_add___help_nargs=0
_shtab_ahriman_rebuild__h_nargs=0
_shtab_ahriman_rebuild___help_nargs=0
_shtab_ahriman_rebuild___dry_run_nargs=0
_shtab_ahriman_rebuild___from_database_nargs=0
_shtab_ahriman_rebuild___increment_nargs=0
_shtab_ahriman_rebuild___no_increment_nargs=0
_shtab_ahriman_rebuild__e_nargs=0
_shtab_ahriman_rebuild___exit_code_nargs=0
_shtab_ahriman_remove_pos_0_nargs=+
_shtab_ahriman_remove__h_nargs=0
_shtab_ahriman_remove___help_nargs=0
_shtab_ahriman_remove_unknown__h_nargs=0
_shtab_ahriman_remove_unknown___help_nargs=0
_shtab_ahriman_remove_unknown___dry_run_nargs=0
_shtab_ahriman_repo_backup__h_nargs=0
_shtab_ahriman_repo_backup___help_nargs=0
_shtab_ahriman_repo_check_pos_0_nargs=*
@ -333,28 +260,19 @@ _shtab_ahriman_repo_check___vcs_nargs=0
_shtab_ahriman_repo_check___no_vcs_nargs=0
_shtab_ahriman_repo_check__y_nargs=0
_shtab_ahriman_repo_check___refresh_nargs=0
_shtab_ahriman_repo_clean__h_nargs=0
_shtab_ahriman_repo_clean___help_nargs=0
_shtab_ahriman_repo_clean___cache_nargs=0
_shtab_ahriman_repo_clean___no_cache_nargs=0
_shtab_ahriman_repo_clean___chroot_nargs=0
_shtab_ahriman_repo_clean___no_chroot_nargs=0
_shtab_ahriman_repo_clean___manual_nargs=0
_shtab_ahriman_repo_clean___no_manual_nargs=0
_shtab_ahriman_repo_clean___packages_nargs=0
_shtab_ahriman_repo_clean___no_packages_nargs=0
_shtab_ahriman_repo_clean___pacman_nargs=0
_shtab_ahriman_repo_clean___no_pacman_nargs=0
_shtab_ahriman_repo_config__h_nargs=0
_shtab_ahriman_repo_config___help_nargs=0
_shtab_ahriman_repo_config___info_nargs=0
_shtab_ahriman_repo_config___no_info_nargs=0
_shtab_ahriman_repo_config___secure_nargs=0
_shtab_ahriman_repo_config___no_secure_nargs=0
_shtab_ahriman_repo_config_validate__h_nargs=0
_shtab_ahriman_repo_config_validate___help_nargs=0
_shtab_ahriman_repo_config_validate__e_nargs=0
_shtab_ahriman_repo_config_validate___exit_code_nargs=0
_shtab_ahriman_check_pos_0_nargs=*
_shtab_ahriman_check__h_nargs=0
_shtab_ahriman_check___help_nargs=0
_shtab_ahriman_check___changes_nargs=0
_shtab_ahriman_check___no_changes_nargs=0
_shtab_ahriman_check___check_files_nargs=0
_shtab_ahriman_check___no_check_files_nargs=0
_shtab_ahriman_check__e_nargs=0
_shtab_ahriman_check___exit_code_nargs=0
_shtab_ahriman_check___vcs_nargs=0
_shtab_ahriman_check___no_vcs_nargs=0
_shtab_ahriman_check__y_nargs=0
_shtab_ahriman_check___refresh_nargs=0
_shtab_ahriman_repo_create_keyring__h_nargs=0
_shtab_ahriman_repo_create_keyring___help_nargs=0
_shtab_ahriman_repo_create_mirrorlist__h_nargs=0
@ -382,14 +300,29 @@ _shtab_ahriman_repo_daemon___vcs_nargs=0
_shtab_ahriman_repo_daemon___no_vcs_nargs=0
_shtab_ahriman_repo_daemon__y_nargs=0
_shtab_ahriman_repo_daemon___refresh_nargs=0
_shtab_ahriman_repo_init__h_nargs=0
_shtab_ahriman_repo_init___help_nargs=0
_shtab_ahriman_repo_init___generate_salt_nargs=0
_shtab_ahriman_repo_init___no_generate_salt_nargs=0
_shtab_ahriman_repo_init___makeflags_jobs_nargs=0
_shtab_ahriman_repo_init___no_makeflags_jobs_nargs=0
_shtab_ahriman_repo_init___multilib_nargs=0
_shtab_ahriman_repo_init___no_multilib_nargs=0
_shtab_ahriman_daemon__h_nargs=0
_shtab_ahriman_daemon___help_nargs=0
_shtab_ahriman_daemon___aur_nargs=0
_shtab_ahriman_daemon___no_aur_nargs=0
_shtab_ahriman_daemon___changes_nargs=0
_shtab_ahriman_daemon___no_changes_nargs=0
_shtab_ahriman_daemon___check_files_nargs=0
_shtab_ahriman_daemon___no_check_files_nargs=0
_shtab_ahriman_daemon___dependencies_nargs=0
_shtab_ahriman_daemon___no_dependencies_nargs=0
_shtab_ahriman_daemon___dry_run_nargs=0
_shtab_ahriman_daemon___increment_nargs=0
_shtab_ahriman_daemon___no_increment_nargs=0
_shtab_ahriman_daemon___local_nargs=0
_shtab_ahriman_daemon___no_local_nargs=0
_shtab_ahriman_daemon___manual_nargs=0
_shtab_ahriman_daemon___no_manual_nargs=0
_shtab_ahriman_daemon___partitions_nargs=0
_shtab_ahriman_daemon___no_partitions_nargs=0
_shtab_ahriman_daemon___vcs_nargs=0
_shtab_ahriman_daemon___no_vcs_nargs=0
_shtab_ahriman_daemon__y_nargs=0
_shtab_ahriman_daemon___refresh_nargs=0
_shtab_ahriman_repo_rebuild__h_nargs=0
_shtab_ahriman_repo_rebuild___help_nargs=0
_shtab_ahriman_repo_rebuild___dry_run_nargs=0
@ -398,30 +331,40 @@ _shtab_ahriman_repo_rebuild___increment_nargs=0
_shtab_ahriman_repo_rebuild___no_increment_nargs=0
_shtab_ahriman_repo_rebuild__e_nargs=0
_shtab_ahriman_repo_rebuild___exit_code_nargs=0
_shtab_ahriman_rebuild__h_nargs=0
_shtab_ahriman_rebuild___help_nargs=0
_shtab_ahriman_rebuild___dry_run_nargs=0
_shtab_ahriman_rebuild___from_database_nargs=0
_shtab_ahriman_rebuild___increment_nargs=0
_shtab_ahriman_rebuild___no_increment_nargs=0
_shtab_ahriman_rebuild__e_nargs=0
_shtab_ahriman_rebuild___exit_code_nargs=0
_shtab_ahriman_repo_remove_unknown__h_nargs=0
_shtab_ahriman_repo_remove_unknown___help_nargs=0
_shtab_ahriman_repo_remove_unknown___dry_run_nargs=0
_shtab_ahriman_remove_unknown__h_nargs=0
_shtab_ahriman_remove_unknown___help_nargs=0
_shtab_ahriman_remove_unknown___dry_run_nargs=0
_shtab_ahriman_repo_report__h_nargs=0
_shtab_ahriman_repo_report___help_nargs=0
_shtab_ahriman_report__h_nargs=0
_shtab_ahriman_report___help_nargs=0
_shtab_ahriman_repo_restore__h_nargs=0
_shtab_ahriman_repo_restore___help_nargs=0
_shtab_ahriman_repo_setup__h_nargs=0
_shtab_ahriman_repo_setup___help_nargs=0
_shtab_ahriman_repo_setup___generate_salt_nargs=0
_shtab_ahriman_repo_setup___no_generate_salt_nargs=0
_shtab_ahriman_repo_setup___makeflags_jobs_nargs=0
_shtab_ahriman_repo_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_repo_setup___multilib_nargs=0
_shtab_ahriman_repo_setup___no_multilib_nargs=0
_shtab_ahriman_repo_sign_pos_0_nargs=*
_shtab_ahriman_repo_sign__h_nargs=0
_shtab_ahriman_repo_sign___help_nargs=0
_shtab_ahriman_sign_pos_0_nargs=*
_shtab_ahriman_sign__h_nargs=0
_shtab_ahriman_sign___help_nargs=0
_shtab_ahriman_repo_statistics__h_nargs=0
_shtab_ahriman_repo_statistics___help_nargs=0
_shtab_ahriman_repo_status_update__h_nargs=0
_shtab_ahriman_repo_status_update___help_nargs=0
_shtab_ahriman_repo_sync__h_nargs=0
_shtab_ahriman_repo_sync___help_nargs=0
_shtab_ahriman_sync__h_nargs=0
_shtab_ahriman_sync___help_nargs=0
_shtab_ahriman_repo_tree__h_nargs=0
_shtab_ahriman_repo_tree___help_nargs=0
_shtab_ahriman_repo_triggers_pos_0_nargs=*
@ -451,91 +394,6 @@ _shtab_ahriman_repo_update___vcs_nargs=0
_shtab_ahriman_repo_update___no_vcs_nargs=0
_shtab_ahriman_repo_update__y_nargs=0
_shtab_ahriman_repo_update___refresh_nargs=0
_shtab_ahriman_report__h_nargs=0
_shtab_ahriman_report___help_nargs=0
_shtab_ahriman_run_pos_0_nargs=+
_shtab_ahriman_run__h_nargs=0
_shtab_ahriman_run___help_nargs=0
_shtab_ahriman_search_pos_0_nargs=+
_shtab_ahriman_search__h_nargs=0
_shtab_ahriman_search___help_nargs=0
_shtab_ahriman_search__e_nargs=0
_shtab_ahriman_search___exit_code_nargs=0
_shtab_ahriman_search___info_nargs=0
_shtab_ahriman_search___no_info_nargs=0
_shtab_ahriman_service_clean__h_nargs=0
_shtab_ahriman_service_clean___help_nargs=0
_shtab_ahriman_service_clean___cache_nargs=0
_shtab_ahriman_service_clean___no_cache_nargs=0
_shtab_ahriman_service_clean___chroot_nargs=0
_shtab_ahriman_service_clean___no_chroot_nargs=0
_shtab_ahriman_service_clean___manual_nargs=0
_shtab_ahriman_service_clean___no_manual_nargs=0
_shtab_ahriman_service_clean___packages_nargs=0
_shtab_ahriman_service_clean___no_packages_nargs=0
_shtab_ahriman_service_clean___pacman_nargs=0
_shtab_ahriman_service_clean___no_pacman_nargs=0
_shtab_ahriman_service_config__h_nargs=0
_shtab_ahriman_service_config___help_nargs=0
_shtab_ahriman_service_config___info_nargs=0
_shtab_ahriman_service_config___no_info_nargs=0
_shtab_ahriman_service_config___secure_nargs=0
_shtab_ahriman_service_config___no_secure_nargs=0
_shtab_ahriman_service_config_validate__h_nargs=0
_shtab_ahriman_service_config_validate___help_nargs=0
_shtab_ahriman_service_config_validate__e_nargs=0
_shtab_ahriman_service_config_validate___exit_code_nargs=0
_shtab_ahriman_service_key_import__h_nargs=0
_shtab_ahriman_service_key_import___help_nargs=0
_shtab_ahriman_service_repositories__h_nargs=0
_shtab_ahriman_service_repositories___help_nargs=0
_shtab_ahriman_service_repositories___id_only_nargs=0
_shtab_ahriman_service_repositories___no_id_only_nargs=0
_shtab_ahriman_service_run_pos_0_nargs=+
_shtab_ahriman_service_run__h_nargs=0
_shtab_ahriman_service_run___help_nargs=0
_shtab_ahriman_service_setup__h_nargs=0
_shtab_ahriman_service_setup___help_nargs=0
_shtab_ahriman_service_setup___generate_salt_nargs=0
_shtab_ahriman_service_setup___no_generate_salt_nargs=0
_shtab_ahriman_service_setup___makeflags_jobs_nargs=0
_shtab_ahriman_service_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_service_setup___multilib_nargs=0
_shtab_ahriman_service_setup___no_multilib_nargs=0
_shtab_ahriman_service_shell__h_nargs=0
_shtab_ahriman_service_shell___help_nargs=0
_shtab_ahriman_service_shell__v_nargs=0
_shtab_ahriman_service_shell___verbose_nargs=0
_shtab_ahriman_service_tree_migrate__h_nargs=0
_shtab_ahriman_service_tree_migrate___help_nargs=0
_shtab_ahriman_setup__h_nargs=0
_shtab_ahriman_setup___help_nargs=0
_shtab_ahriman_setup___generate_salt_nargs=0
_shtab_ahriman_setup___no_generate_salt_nargs=0
_shtab_ahriman_setup___makeflags_jobs_nargs=0
_shtab_ahriman_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_setup___multilib_nargs=0
_shtab_ahriman_setup___no_multilib_nargs=0
_shtab_ahriman_shell__h_nargs=0
_shtab_ahriman_shell___help_nargs=0
_shtab_ahriman_shell__v_nargs=0
_shtab_ahriman_shell___verbose_nargs=0
_shtab_ahriman_sign_pos_0_nargs=*
_shtab_ahriman_sign__h_nargs=0
_shtab_ahriman_sign___help_nargs=0
_shtab_ahriman_status_pos_0_nargs=*
_shtab_ahriman_status__h_nargs=0
_shtab_ahriman_status___help_nargs=0
_shtab_ahriman_status___ahriman_nargs=0
_shtab_ahriman_status__e_nargs=0
_shtab_ahriman_status___exit_code_nargs=0
_shtab_ahriman_status___info_nargs=0
_shtab_ahriman_status___no_info_nargs=0
_shtab_ahriman_status_update_pos_0_nargs=*
_shtab_ahriman_status_update__h_nargs=0
_shtab_ahriman_status_update___help_nargs=0
_shtab_ahriman_sync__h_nargs=0
_shtab_ahriman_sync___help_nargs=0
_shtab_ahriman_update_pos_0_nargs=*
_shtab_ahriman_update__h_nargs=0
_shtab_ahriman_update___help_nargs=0
@ -560,6 +418,136 @@ _shtab_ahriman_update___vcs_nargs=0
_shtab_ahriman_update___no_vcs_nargs=0
_shtab_ahriman_update__y_nargs=0
_shtab_ahriman_update___refresh_nargs=0
_shtab_ahriman_service_clean__h_nargs=0
_shtab_ahriman_service_clean___help_nargs=0
_shtab_ahriman_service_clean___cache_nargs=0
_shtab_ahriman_service_clean___no_cache_nargs=0
_shtab_ahriman_service_clean___chroot_nargs=0
_shtab_ahriman_service_clean___no_chroot_nargs=0
_shtab_ahriman_service_clean___manual_nargs=0
_shtab_ahriman_service_clean___no_manual_nargs=0
_shtab_ahriman_service_clean___packages_nargs=0
_shtab_ahriman_service_clean___no_packages_nargs=0
_shtab_ahriman_service_clean___pacman_nargs=0
_shtab_ahriman_service_clean___no_pacman_nargs=0
_shtab_ahriman_clean__h_nargs=0
_shtab_ahriman_clean___help_nargs=0
_shtab_ahriman_clean___cache_nargs=0
_shtab_ahriman_clean___no_cache_nargs=0
_shtab_ahriman_clean___chroot_nargs=0
_shtab_ahriman_clean___no_chroot_nargs=0
_shtab_ahriman_clean___manual_nargs=0
_shtab_ahriman_clean___no_manual_nargs=0
_shtab_ahriman_clean___packages_nargs=0
_shtab_ahriman_clean___no_packages_nargs=0
_shtab_ahriman_clean___pacman_nargs=0
_shtab_ahriman_clean___no_pacman_nargs=0
_shtab_ahriman_repo_clean__h_nargs=0
_shtab_ahriman_repo_clean___help_nargs=0
_shtab_ahriman_repo_clean___cache_nargs=0
_shtab_ahriman_repo_clean___no_cache_nargs=0
_shtab_ahriman_repo_clean___chroot_nargs=0
_shtab_ahriman_repo_clean___no_chroot_nargs=0
_shtab_ahriman_repo_clean___manual_nargs=0
_shtab_ahriman_repo_clean___no_manual_nargs=0
_shtab_ahriman_repo_clean___packages_nargs=0
_shtab_ahriman_repo_clean___no_packages_nargs=0
_shtab_ahriman_repo_clean___pacman_nargs=0
_shtab_ahriman_repo_clean___no_pacman_nargs=0
_shtab_ahriman_service_config__h_nargs=0
_shtab_ahriman_service_config___help_nargs=0
_shtab_ahriman_service_config___info_nargs=0
_shtab_ahriman_service_config___no_info_nargs=0
_shtab_ahriman_service_config___secure_nargs=0
_shtab_ahriman_service_config___no_secure_nargs=0
_shtab_ahriman_config__h_nargs=0
_shtab_ahriman_config___help_nargs=0
_shtab_ahriman_config___info_nargs=0
_shtab_ahriman_config___no_info_nargs=0
_shtab_ahriman_config___secure_nargs=0
_shtab_ahriman_config___no_secure_nargs=0
_shtab_ahriman_repo_config__h_nargs=0
_shtab_ahriman_repo_config___help_nargs=0
_shtab_ahriman_repo_config___info_nargs=0
_shtab_ahriman_repo_config___no_info_nargs=0
_shtab_ahriman_repo_config___secure_nargs=0
_shtab_ahriman_repo_config___no_secure_nargs=0
_shtab_ahriman_service_config_validate__h_nargs=0
_shtab_ahriman_service_config_validate___help_nargs=0
_shtab_ahriman_service_config_validate__e_nargs=0
_shtab_ahriman_service_config_validate___exit_code_nargs=0
_shtab_ahriman_config_validate__h_nargs=0
_shtab_ahriman_config_validate___help_nargs=0
_shtab_ahriman_config_validate__e_nargs=0
_shtab_ahriman_config_validate___exit_code_nargs=0
_shtab_ahriman_repo_config_validate__h_nargs=0
_shtab_ahriman_repo_config_validate___help_nargs=0
_shtab_ahriman_repo_config_validate__e_nargs=0
_shtab_ahriman_repo_config_validate___exit_code_nargs=0
_shtab_ahriman_service_key_import__h_nargs=0
_shtab_ahriman_service_key_import___help_nargs=0
_shtab_ahriman_key_import__h_nargs=0
_shtab_ahriman_key_import___help_nargs=0
_shtab_ahriman_service_repositories__h_nargs=0
_shtab_ahriman_service_repositories___help_nargs=0
_shtab_ahriman_service_repositories___id_only_nargs=0
_shtab_ahriman_service_repositories___no_id_only_nargs=0
_shtab_ahriman_service_run_pos_0_nargs=+
_shtab_ahriman_service_run__h_nargs=0
_shtab_ahriman_service_run___help_nargs=0
_shtab_ahriman_run_pos_0_nargs=+
_shtab_ahriman_run__h_nargs=0
_shtab_ahriman_run___help_nargs=0
_shtab_ahriman_service_setup__h_nargs=0
_shtab_ahriman_service_setup___help_nargs=0
_shtab_ahriman_service_setup___generate_salt_nargs=0
_shtab_ahriman_service_setup___no_generate_salt_nargs=0
_shtab_ahriman_service_setup___makeflags_jobs_nargs=0
_shtab_ahriman_service_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_service_setup___multilib_nargs=0
_shtab_ahriman_service_setup___no_multilib_nargs=0
_shtab_ahriman_init__h_nargs=0
_shtab_ahriman_init___help_nargs=0
_shtab_ahriman_init___generate_salt_nargs=0
_shtab_ahriman_init___no_generate_salt_nargs=0
_shtab_ahriman_init___makeflags_jobs_nargs=0
_shtab_ahriman_init___no_makeflags_jobs_nargs=0
_shtab_ahriman_init___multilib_nargs=0
_shtab_ahriman_init___no_multilib_nargs=0
_shtab_ahriman_repo_init__h_nargs=0
_shtab_ahriman_repo_init___help_nargs=0
_shtab_ahriman_repo_init___generate_salt_nargs=0
_shtab_ahriman_repo_init___no_generate_salt_nargs=0
_shtab_ahriman_repo_init___makeflags_jobs_nargs=0
_shtab_ahriman_repo_init___no_makeflags_jobs_nargs=0
_shtab_ahriman_repo_init___multilib_nargs=0
_shtab_ahriman_repo_init___no_multilib_nargs=0
_shtab_ahriman_repo_setup__h_nargs=0
_shtab_ahriman_repo_setup___help_nargs=0
_shtab_ahriman_repo_setup___generate_salt_nargs=0
_shtab_ahriman_repo_setup___no_generate_salt_nargs=0
_shtab_ahriman_repo_setup___makeflags_jobs_nargs=0
_shtab_ahriman_repo_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_repo_setup___multilib_nargs=0
_shtab_ahriman_repo_setup___no_multilib_nargs=0
_shtab_ahriman_setup__h_nargs=0
_shtab_ahriman_setup___help_nargs=0
_shtab_ahriman_setup___generate_salt_nargs=0
_shtab_ahriman_setup___no_generate_salt_nargs=0
_shtab_ahriman_setup___makeflags_jobs_nargs=0
_shtab_ahriman_setup___no_makeflags_jobs_nargs=0
_shtab_ahriman_setup___multilib_nargs=0
_shtab_ahriman_setup___no_multilib_nargs=0
_shtab_ahriman_service_shell__h_nargs=0
_shtab_ahriman_service_shell___help_nargs=0
_shtab_ahriman_service_shell__v_nargs=0
_shtab_ahriman_service_shell___verbose_nargs=0
_shtab_ahriman_shell__h_nargs=0
_shtab_ahriman_shell___help_nargs=0
_shtab_ahriman_shell__v_nargs=0
_shtab_ahriman_shell___verbose_nargs=0
_shtab_ahriman_service_tree_migrate__h_nargs=0
_shtab_ahriman_service_tree_migrate___help_nargs=0
_shtab_ahriman_user_add__h_nargs=0
_shtab_ahriman_user_add___help_nargs=0
_shtab_ahriman_user_list__h_nargs=0
@ -568,8 +556,6 @@ _shtab_ahriman_user_list__e_nargs=0
_shtab_ahriman_user_list___exit_code_nargs=0
_shtab_ahriman_user_remove__h_nargs=0
_shtab_ahriman_user_remove___help_nargs=0
_shtab_ahriman_version__h_nargs=0
_shtab_ahriman_version___help_nargs=0
_shtab_ahriman_web__h_nargs=0
_shtab_ahriman_web___help_nargs=0

View File

@ -1,9 +1,9 @@
.TH AHRIMAN "1" "2024\-12\-01" "ahriman" "Generated Python Manual"
.TH AHRIMAN "1" "2024\-09\-26" "ahriman" "Generated Python Manual"
.SH NAME
ahriman
.SH SYNOPSIS
.B ahriman
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {add,aur-search,check,clean,config,config-validate,copy,daemon,help,help-commands-unsafe,help-updates,help-version,init,key-import,package-add,package-changes,package-changes-remove,package-copy,package-remove,package-status,package-status-remove,package-status-update,package-update,patch-add,patch-list,patch-remove,patch-set-add,rebuild,remove,remove-unknown,repo-backup,repo-check,repo-clean,repo-config,repo-config-validate,repo-create-keyring,repo-create-mirrorlist,repo-daemon,repo-init,repo-rebuild,repo-remove-unknown,repo-report,repo-restore,repo-setup,repo-sign,repo-statistics,repo-status-update,repo-sync,repo-tree,repo-triggers,repo-update,report,run,search,service-clean,service-config,service-config-validate,service-key-import,service-repositories,service-run,service-setup,service-shell,service-tree-migrate,setup,shell,sign,status,status-update,sync,update,user-add,user-list,user-remove,version,web} ...
[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--log-handler {console,syslog,journald}] [-q] [--report | --no-report] [-r REPOSITORY] [--unsafe] [-V] [--wait-timeout WAIT_TIMEOUT] {aur-search,search,help-commands-unsafe,help,help-updates,help-version,version,package-add,add,package-update,package-changes,package-changes-remove,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-create-keyring,repo-create-mirrorlist,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-statistics,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-repositories,service-run,run,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,service-tree-migrate,user-add,user-list,user-remove,web} ...
.SH DESCRIPTION
ArcH linux ReposItory MANager
@ -59,12 +59,12 @@ COMMAND
\fBahriman\fR \fI\,aur\-search\/\fR
search for package
.TP
\fBahriman\fR \fI\,help\/\fR
show help message
.TP
\fBahriman\fR \fI\,help\-commands\-unsafe\/\fR
list unsafe commands
.TP
\fBahriman\fR \fI\,help\/\fR
show help message
.TP
\fBahriman\fR \fI\,help\-updates\/\fR
check for service updates
.TP
@ -80,9 +80,6 @@ get package changes
\fBahriman\fR \fI\,package\-changes\-remove\/\fR
remove package changes
.TP
\fBahriman\fR \fI\,package\-copy\/\fR
copy package from another repository
.TP
\fBahriman\fR \fI\,package\-remove\/\fR
remove package
.TP
@ -219,15 +216,6 @@ show additional package information
sort field by this field. In case if two packages have the same value of the specified field, they will be always sorted
by name
.SH COMMAND \fI\,'ahriman help'\/\fR
usage: ahriman help [\-h] [subcommand]
show help message for application or command and exit
.TP
\fBsubcommand\fR
show help message for specific command
.SH COMMAND \fI\,'ahriman help\-commands\-unsafe'\/\fR
usage: ahriman help\-commands\-unsafe [\-h] [subcommand ...]
@ -238,6 +226,15 @@ list unsafe commands as defined in default args
instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1
otherwise
.SH COMMAND \fI\,'ahriman help'\/\fR
usage: ahriman help [\-h] [subcommand]
show help message for application or command and exit
.TP
\fBsubcommand\fR
show help message for specific command
.SH COMMAND \fI\,'ahriman help\-updates'\/\fR
usage: ahriman help\-updates [\-h] [\-e]
@ -325,28 +322,6 @@ remove the package changes stored remotely
\fBpackage\fR
package base
.SH COMMAND \fI\,'ahriman package\-copy'\/\fR
usage: ahriman package\-copy [\-h] [\-e] [\-\-remove] source package [package ...]
copy package and its metadata from another repository
.TP
\fBsource\fR
source repository name
.TP
\fBpackage\fR
package base
.SH OPTIONS \fI\,'ahriman package\-copy'\/\fR
.TP
\fB\-e\fR, \fB\-\-exit\-code\fR
return non\-zero exit status if result is empty
.TP
\fB\-\-remove\fR
remove package from the source repository after
.SH COMMAND \fI\,'ahriman package\-remove'\/\fR
usage: ahriman package\-remove [\-h] package [package ...]

View File

@ -11,7 +11,6 @@ _shtab_ahriman_commands() {
"clean:remove local caches"
"config:dump configuration for the specified architecture"
"config-validate:validate configuration and print found errors"
"copy:copy package and its metadata from another repository"
"daemon:start process which periodically will run update process"
"help:show help message for application or command and exit"
"help-commands-unsafe:list unsafe commands as defined in default args"
@ -22,7 +21,6 @@ _shtab_ahriman_commands() {
"package-add:add existing or new package to the build queue"
"package-changes:retrieve package changes stored in database"
"package-changes-remove:remove the package changes stored remotely"
"package-copy:copy package and its metadata from another repository"
"package-remove:remove package from the repository"
"package-status:request status of the package"
"package-status-remove:remove the package from the status page"
@ -153,14 +151,6 @@ _shtab_ahriman_config_validate_options=(
{-e,--exit-code}"[return non-zero exit status if configuration is invalid (default\: False)]"
)
_shtab_ahriman_copy_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
"--remove[remove package from the source repository after (default\: False)]"
":source repository name:"
"(*):package base:"
)
_shtab_ahriman_daemon_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-i,--interval}"[interval between runs in seconds (default\: 43200)]:interval:"
@ -244,14 +234,6 @@ _shtab_ahriman_package_changes_remove_options=(
":package base:"
)
_shtab_ahriman_package_copy_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
{-e,--exit-code}"[return non-zero exit status if result is empty (default\: False)]"
"--remove[remove package from the source repository after (default\: False)]"
":source repository name:"
"(*):package base:"
)
_shtab_ahriman_package_remove_options=(
"(- : *)"{-h,--help}"[show this help message and exit]"
"(*):package name or base:"
@ -702,7 +684,6 @@ _shtab_ahriman() {
clean) _arguments -C -s $_shtab_ahriman_clean_options ;;
config) _arguments -C -s $_shtab_ahriman_config_options ;;
config-validate) _arguments -C -s $_shtab_ahriman_config_validate_options ;;
copy) _arguments -C -s $_shtab_ahriman_copy_options ;;
daemon) _arguments -C -s $_shtab_ahriman_daemon_options ;;
help) _arguments -C -s $_shtab_ahriman_help_options ;;
help-commands-unsafe) _arguments -C -s $_shtab_ahriman_help_commands_unsafe_options ;;
@ -713,7 +694,6 @@ _shtab_ahriman() {
package-add) _arguments -C -s $_shtab_ahriman_package_add_options ;;
package-changes) _arguments -C -s $_shtab_ahriman_package_changes_options ;;
package-changes-remove) _arguments -C -s $_shtab_ahriman_package_changes_remove_options ;;
package-copy) _arguments -C -s $_shtab_ahriman_package_copy_options ;;
package-remove) _arguments -C -s $_shtab_ahriman_package_remove_options ;;
package-status) _arguments -C -s $_shtab_ahriman_package_status_options ;;
package-status-remove) _arguments -C -s $_shtab_ahriman_package_status_remove_options ;;

View File

@ -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.15.2"

View File

@ -95,11 +95,6 @@ Start web service (requires additional configuration):
subparser.formatter_class = _HelpFormatter
subparser.set_defaults(handler=handler, parser=_parser)
# sort actions alphabetically in both choices and help message
# pylint: disable=protected-access
subparsers._choices_actions = sorted(subparsers._choices_actions, key=lambda action: action.dest)
subparsers.choices = dict(sorted(subparsers.choices.items()))
return parser

View File

@ -198,7 +198,8 @@ class Patch(Handler):
else:
patch = patch_path.read_text(encoding="utf8")
# remove spaces around the patch and parse to correct type
return PkgbuildPatch.parse(variable, patch.strip())
parsed = PkgbuildPatch.parse(patch.strip())
return PkgbuildPatch(variable, parsed)
@staticmethod
def patch_set_create(application: Application, package_base: str, patch: PkgbuildPatch) -> None:

View File

@ -21,7 +21,7 @@ import argparse
from pathlib import Path
from pwd import getpwuid
from urllib.parse import quote_plus as url_encode
from urllib.parse import quote_plus as urlencode
from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler, SubParserAction
@ -174,7 +174,7 @@ class Setup(Handler):
if args.web_unix_socket is not None:
unix_socket = str(args.web_unix_socket)
configuration.set_option("web", "unix_socket", unix_socket)
configuration.set_option("status", "address", f"http+unix://{url_encode(unix_socket)}")
configuration.set_option("status", "address", f"http+unix://{urlencode(unix_socket)}")
if args.generate_salt:
configuration.set_option("auth", "salt", User.generate_password(20))

View File

@ -54,7 +54,7 @@ class StatusUpdate(Handler):
case Action.Update if args.package:
# update packages statuses
for package in args.package:
client.package_status_update(package, args.status)
client.package_update(package, args.status)
case Action.Update:
# update service status
client.status_update(args.status)

View File

@ -50,6 +50,42 @@ class Triggers(Handler):
application.on_start()
application.on_result(Result())
@staticmethod
def _set_repo_create_keyring_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for create-keyring subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-create-keyring", help="create keyring package",
description="create package which contains list of trusted keys as set by "
"configuration. Note, that this action will only create package, "
"the package itself has to be built manually")
parser.set_defaults(trigger=["ahriman.core.support.KeyringTrigger"])
return parser
@staticmethod
def _set_repo_create_mirrorlist_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for create-mirrorlist subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-create-mirrorlist", help="create mirrorlist package",
description="create package which contains list of available mirrors as set by "
"configuration. Note, that this action will only create package, "
"the package itself has to be built manually")
parser.set_defaults(trigger=["ahriman.core.support.MirrorlistTrigger"])
return parser
@staticmethod
def _set_repo_report_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
@ -102,6 +138,8 @@ class Triggers(Handler):
return parser
arguments = [
_set_repo_create_keyring_parser,
_set_repo_create_mirrorlist_parser,
_set_repo_report_parser,
_set_repo_sync_parser,
_set_repo_triggers_parser,

View File

@ -1,70 +0,0 @@
#
# Copyright (c) 2021-2024 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import argparse
from ahriman.application.handlers.handler import SubParserAction
from ahriman.application.handlers.triggers import Triggers
class TriggersSupport(Triggers):
"""
additional triggers handlers for support commands
"""
@staticmethod
def _set_repo_create_keyring_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for create-keyring subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-create-keyring", help="create keyring package",
description="create package which contains list of trusted keys as set by "
"configuration. Note, that this action will only create package, "
"the package itself has to be built manually")
parser.set_defaults(trigger=["ahriman.core.support.KeyringTrigger"])
return parser
@staticmethod
def _set_repo_create_mirrorlist_parser(root: SubParserAction) -> argparse.ArgumentParser:
"""
add parser for create-mirrorlist subcommand
Args:
root(SubParserAction): subparsers for the commands
Returns:
argparse.ArgumentParser: created argument parser
"""
parser = root.add_parser("repo-create-mirrorlist", help="create mirrorlist package",
description="create package which contains list of available mirrors as set by "
"configuration. Note, that this action will only create package, "
"the package itself has to be built manually")
parser.set_defaults(trigger=["ahriman.core.support.MirrorlistTrigger"])
return parser
arguments = [
_set_repo_create_keyring_parser,
_set_repo_create_mirrorlist_parser,
]

View File

@ -27,7 +27,6 @@ from ahriman.core.configuration import Configuration
from ahriman.core.spawn import Spawn
from ahriman.core.triggers import TriggerLoader
from ahriman.models.repository_id import RepositoryId
from ahriman.web.web import run_server, setup_server
class Web(Handler):
@ -49,6 +48,9 @@ class Web(Handler):
configuration(Configuration): configuration instance
report(bool): force enable or disable reporting
"""
# we are using local import for optional dependencies
from ahriman.web.web import run_server, setup_server
spawner_args = Web.extract_arguments(args, configuration)
spawner = Spawn(args.parser(), list(spawner_args))
spawner.start()

View File

@ -146,7 +146,7 @@ class PkgbuildParser(shlex.shlex):
# reset state
buffer, prefix = [], None
# we have already got prefix string, so we are in progress of expansion
# we have already prefix string, so we are in progress of expansion
# we always operate the last element, so this matches ",", "next"
case (PkgbuildToken.Comma, _) if prefix is not None:
buffer.append(f"{prefix}{second}")
@ -168,7 +168,7 @@ class PkgbuildParser(shlex.shlex):
def _is_escaped(self) -> bool:
"""
check if the last element was quoted. ``shlex.shlex`` parser doesn't provide information about was the token
quoted or not, thus there is no difference between "'#'" (sharp in quotes) and "#" (sharp without quotes). This
quoted or not, thus there is no difference between "'#'" (diez in quotes) and "#" (diez without quotes). This
method simply rolls back to the last non-space character and check if it is a quotation mark
Returns:
@ -226,7 +226,7 @@ class PkgbuildParser(shlex.shlex):
pass
case PkgbuildToken.ArrayEnds:
break
case comment if comment.startswith(PkgbuildToken.Comment):
case PkgbuildToken.Comment:
self.instream.readline()
continue
@ -267,7 +267,7 @@ class PkgbuildParser(shlex.shlex):
counter -= 1
if counter == 0:
break
case comment if comment.startswith(PkgbuildToken.Comment):
case PkgbuildToken.Comment:
self.instream.readline()
if not 0 < start_position < end_position:
@ -303,7 +303,7 @@ class PkgbuildParser(shlex.shlex):
yield PkgbuildPatch(key, value)
return
if token.startswith(PkgbuildToken.Comment):
if token == PkgbuildToken.Comment:
self.instream.readline()
return

View File

@ -59,10 +59,10 @@ class Mapping(Auth):
"""
if password is None:
return False # invalid data supplied
user = await self.get_user(username)
user = self.get_user(username)
return user is not None and user.check_credentials(password, self.salt)
async def get_user(self, username: str) -> User | None:
def get_user(self, username: str) -> User | None:
"""
retrieve user from in-memory mapping
@ -84,7 +84,7 @@ class Mapping(Auth):
Returns:
bool: ``True`` in case if user is known and can be authorized and ``False`` otherwise
"""
return username is not None and await self.get_user(username) is not None
return username is not None and self.get_user(username) is not None
async def verify_access(self, username: str, required: UserAccess, context: str | None) -> bool:
"""
@ -98,5 +98,5 @@ class Mapping(Auth):
Returns:
bool: ``True`` in case if user is allowed to do this request and ``False`` otherwise
"""
user = await self.get_user(username)
user = self.get_user(username)
return user is not None and user.verify_access(required)

View File

@ -120,7 +120,7 @@ class PAM(Mapping):
bool: ``True`` in case if user is allowed to do this request and ``False`` otherwise
"""
# this method is basically inverted, first we check overrides in database and then fallback to the PAM logic
if (user := await self.get_user(username)) is not None:
if (user := self.get_user(username)) is not None:
return user.verify_access(required)
# if username is in admin group, then we treat it as full access
if username in self.group_members(self.full_access_group):

View File

@ -83,7 +83,7 @@ class PatchOperations(Operations):
"""
def run(connection: Connection) -> list[tuple[str, PkgbuildPatch]]:
return [
(row["package_base"], PkgbuildPatch.parse(row["variable"], row["patch"]))
(row["package_base"], PkgbuildPatch(row["variable"], row["patch"]))
for row in connection.execute(
"""select * from patches where :package_base is null or package_base = :package_base""",
{"package_base": package_base})

View File

@ -58,8 +58,8 @@ class EventStatsPrinter(StringPrinter):
mean = statistics.mean(self.events)
if len(self.events) > 1:
st_dev = statistics.stdev(self.events)
average = f"{mean:.3f} ± {st_dev:.3f}"
stdev = statistics.stdev(self.events)
average = f"{mean:.3f} ± {stdev:.3f}"
else:
average = f"{mean:.3f}"

View File

@ -19,7 +19,7 @@
#
import contextlib
from urllib.parse import quote_plus as url_encode
from urllib.parse import quote_plus as urlencode
from ahriman.core.configuration import Configuration
from ahriman.core.http import SyncAhrimanClient
@ -75,7 +75,7 @@ class WebClient(Client, SyncAhrimanClient):
# legacy-style section
if (unix_socket := configuration.get("web", "unix_socket", fallback=None)) is not None:
# special pseudo-protocol which is used for unix sockets
return "web", f"http+unix://{url_encode(unix_socket)}"
return "web", f"http+unix://{urlencode(unix_socket)}"
address = configuration.get("web", "address", fallback=None)
if not address:
# build address from host and port directly
@ -94,7 +94,7 @@ class WebClient(Client, SyncAhrimanClient):
Returns:
str: full url for web service for changes
"""
return f"{self.address}/api/v1/packages/{url_encode(package_base)}/changes"
return f"{self.address}/api/v1/packages/{urlencode(package_base)}/changes"
def _dependencies_url(self, package_base: str) -> str:
"""
@ -106,7 +106,7 @@ class WebClient(Client, SyncAhrimanClient):
Returns:
str: full url for web service for dependencies
"""
return f"{self.address}/api/v1/packages/{url_encode(package_base)}/dependencies"
return f"{self.address}/api/v1/packages/{urlencode(package_base)}/dependencies"
def _events_url(self) -> str:
"""
@ -127,7 +127,7 @@ class WebClient(Client, SyncAhrimanClient):
Returns:
str: full url for web service for logs
"""
return f"{self.address}/api/v1/packages/{url_encode(package_base)}/logs"
return f"{self.address}/api/v1/packages/{urlencode(package_base)}/logs"
def _package_url(self, package_base: str = "") -> str:
"""
@ -139,7 +139,7 @@ class WebClient(Client, SyncAhrimanClient):
Returns:
str: full url of web service for specific package base
"""
suffix = f"/{url_encode(package_base)}" if package_base else ""
suffix = f"/{urlencode(package_base)}" if package_base else ""
return f"{self.address}/api/v1/packages{suffix}"
def _patches_url(self, package_base: str, variable: str = "") -> str:
@ -153,8 +153,8 @@ class WebClient(Client, SyncAhrimanClient):
Returns:
str: full url of web service for the package patch
"""
suffix = f"/{url_encode(variable)}" if variable else ""
return f"{self.address}/api/v1/packages/{url_encode(package_base)}/patches{suffix}"
suffix = f"/{urlencode(variable)}" if variable else ""
return f"{self.address}/api/v1/packages/{urlencode(package_base)}/patches{suffix}"
def _status_url(self) -> str:
"""

View File

@ -107,8 +107,8 @@ class Pkgbuild(Mapping[str, Any]):
def __getitem__(self, item: str) -> Any:
"""
get the field of the PKGBUILD. This method tries to get exact key value if possible; if none was found,
it tries to fetch function with the same name
get the field of the PKGBUILD. This method tries to get exact key value if possible; if none found, it tries to
fetch function with the same name
Args:
item(str): key name

View File

@ -17,7 +17,6 @@
# 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 json
import shlex
from dataclasses import dataclass, fields
@ -43,6 +42,8 @@ class PkgbuildPatch:
key: str | None
value: str | list[str]
quote = shlex.quote
def __post_init__(self) -> None:
"""
remove empty key
@ -78,11 +79,11 @@ class PkgbuildPatch:
variable(str): variable in bash form, i.e. KEY=VALUE
Returns:
Self: patch object
Self: package properties
"""
key, *value_parts = variable.split("=", maxsplit=1)
raw_value = next(iter(value_parts), "") # extract raw value
return cls.parse(key, raw_value)
return cls(key, cls.parse(raw_value))
@classmethod
def from_json(cls, dump: dict[str, Any]) -> Self:
@ -99,57 +100,21 @@ class PkgbuildPatch:
known_fields = [pair.name for pair in fields(cls)]
return cls(**filter_json(dump, known_fields))
@classmethod
def parse(cls, key: str | None, source: str | list[str]) -> Self:
@staticmethod
def parse(source: str) -> str | list[str]:
"""
parse string value to the PKGBUILD patch value. This method simply takes string, tries to identify it as array
or just string and return the respective value. Functions are returned as is. Shell arrays and single values
are returned without quotes. If source is ``list``, then value is returned as is
or just string and return the respective value. Functions should be processed correctly, however, not guaranteed
Args:
key(str | None): variable key
source(str): source value string to parse
source(str): source string to parse
Returns:
Self: parsed patch object
str | list[str]: parsed value either string or list of strings
"""
def value() -> str | list[str]:
match source:
case function if key is not None and key.endswith("()"):
# the key looks like a function, no further processing should be applied here
return function
case list():
# do not try to perform operations on the list, just return as is
return source
case shell_array if shell_array.startswith("(") and shell_array.endswith(")"):
# the source value looks like shell array, remove brackets and parse with shlex
return shlex.split(shell_array[1:-1])
case json_array if json_array.startswith("[") and json_array.endswith("]"):
# json (aka python) array, parse with json parser instead
parsed: list[str] = json.loads(json_array)
return parsed
case variable:
return cls.unquote(variable)
return cls(key, value())
@staticmethod
def quote(source: str) -> str:
"""
like :func:`shlex.quote()`, but better. The difference in this method from the library one is that it uses
double quotes on top level instead of single quotes to allow shell variable substitution
Args:
source(str): source value string to quote
Returns:
str: quoted string
"""
if "$" in source:
# copy from library method with double quotes instead
return f"""\"{source.replace("\"", "'\"'")}\""""
# otherwise just return normal call
return shlex.quote(source)
if source.startswith("(") and source.endswith(")"):
return shlex.split(source[1:-1]) # arrays for poor
return PkgbuildPatch.unquote(source)
@staticmethod
def unquote(source: str) -> str:
@ -194,14 +159,14 @@ class PkgbuildPatch:
str: serialized key-value pair, print-friendly
"""
if isinstance(self.value, list): # list like
value = " ".join(map(self.quote, self.value))
value = " ".join(map(PkgbuildPatch.quote, self.value))
return f"""{self.key}=({value})"""
if self.is_plain_diff: # no additional logic for plain diffs
return self.value
# we suppose that function values are only supported in string-like values
if self.is_function:
return f"{self.key} {self.value}" # no quoting enabled here
return f"""{self.key}={self.quote(self.value)}"""
return f"""{self.key}={PkgbuildPatch.quote(self.value)}"""
def substitute(self, variables: dict[str, str]) -> str | list[str]:
"""

View File

@ -32,7 +32,6 @@ class User:
authorized web user model
Attributes:
SUPPORTED_ALGOS(set[str]): (class attribute) list of the supported hashing algorithms
username(str): username
password(str): hashed user password with salt
access(UserAccess): user role
@ -69,8 +68,6 @@ class User:
packager_id: str | None = None
key: str | None = None
SUPPORTED_ALGOS = {"$2$", "$2a$", "$2x$", "$2y$", "$2b$"}
def __post_init__(self) -> None:
"""
remove empty fields
@ -78,19 +75,6 @@ class User:
object.__setattr__(self, "packager_id", self.packager_id or None)
object.__setattr__(self, "key", self.key or None)
@property
def algo(self) -> str | None:
"""
extract algorithm used for the hashing password
Returns:
str | None: first part of password hash (e.g. ``$2$``) if available or ``None`` otherwise
"""
if not self.password:
return None
algo = next(segment for segment in self.password.split("$") if segment)
return f"${algo}$"
@staticmethod
def generate_password(length: int) -> str:
"""
@ -114,12 +98,7 @@ class User:
Returns:
bool: ``True`` in case if password matches, ``False`` otherwise
Raises:
ValueError: if user password is set to unsupported algorithm
"""
if (algo := self.algo) is not None and algo not in self.SUPPORTED_ALGOS:
raise ValueError(f"Crypt {algo} is not supported, consider setting new password")
try:
return bcrypt.checkpw((password + salt).encode("utf8"), self.password.encode("utf8"))
except ValueError:

View File

@ -18,7 +18,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from aiohttp.web import Application, View
from collections.abc import Generator
import ahriman.web.views
@ -30,19 +29,22 @@ from ahriman.web.views.base import BaseView
__all__ = ["setup_routes"]
def _dynamic_routes(configuration: Configuration) -> Generator[tuple[str, type[View]], None, None]:
def _dynamic_routes(configuration: Configuration) -> dict[str, type[View]]:
"""
extract dynamic routes based on views
Args:
configuration(Configuration): configuration instance
Yields:
tuple[str, type[View]]: map of the route to its view
Returns:
dict[str, type[View]]: map of the route to its view
"""
routes: dict[str, type[View]] = {}
for view in implementations(ahriman.web.views, BaseView):
for route in view.routes(configuration):
yield route, view
view_routes = view.routes(configuration)
routes.update([(route, view) for route in view_routes])
return routes
def setup_routes(application: Application, configuration: Configuration) -> None:
@ -55,5 +57,5 @@ def setup_routes(application: Application, configuration: Configuration) -> None
"""
application.router.add_static("/static", configuration.getpath("web", "static_path"), follow_symlinks=True)
for route, view in _dynamic_routes(configuration):
for route, view in _dynamic_routes(configuration).items():
application.router.add_view(route, view)

View File

@ -101,6 +101,6 @@ class PatchesView(StatusViewGuard, BaseView):
except Exception as ex:
raise HTTPBadRequest(reason=str(ex))
self.service().package_patches_update(package_base, PkgbuildPatch.parse(key, value))
self.service().package_patches_update(package_base, PkgbuildPatch(key, value))
raise HTTPNoContent

View File

@ -68,7 +68,7 @@ class AddView(BaseView):
try:
data = await self.request.json()
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
patches = [PkgbuildPatch.parse(patch["key"], patch.get("value", "")) for patch in data.get("patches", [])]
patches = [PkgbuildPatch(patch["key"], patch.get("value", "")) for patch in data.get("patches", [])]
except Exception as ex:
raise HTTPBadRequest(reason=str(ex))

View File

@ -68,7 +68,7 @@ class RequestView(BaseView):
try:
data = await self.request.json()
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
patches = [PkgbuildPatch.parse(patch["key"], patch.get("value", "")) for patch in data.get("patches", [])]
patches = [PkgbuildPatch(patch["key"], patch.get("value", "")) for patch in data.get("patches", [])]
except Exception as ex:
raise HTTPBadRequest(reason=str(ex))

View File

@ -28,15 +28,14 @@ from pathlib import Path
prefix = Path(sys.prefix).relative_to("/")
site_packages = Path(site.getsitepackages()[0]).relative_to("/")
SUBPACKAGES = {
"ahriman-core": [
"ahriman": [
prefix / "bin",
prefix / "lib" / "systemd",
prefix / "share",
site_packages,
site_packages / "ahriman",
],
"ahriman-triggers": [
prefix / "share" / "ahriman" / "settings" / "ahriman.ini.d" / "00-triggers.ini",
site_packages / "ahriman" / "application" / "handlers" / "triggers_support.py",
site_packages / "ahriman" / "core" / "distributed",
site_packages / "ahriman" / "core" / "support",
],

View File

@ -53,7 +53,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
def test_run_remove(args: argparse.Namespace, configuration: Configuration, repository: Repository,
package_ahriman: Package, mocker: MockerFixture) -> None:
"""
must run command and remove packages afterward
must run command and remove packages afterwards
"""
args = _default_args(args)
args.remove = True

View File

@ -191,7 +191,7 @@ def test_extract_packages_by_status(application: Application, mocker: MockerFixt
def test_extract_packages_from_database(application: Application, mocker: MockerFixture) -> None:
"""
must extract packages from database
must extract packages from database from database
"""
packages_mock = mocker.patch("ahriman.core.database.SQLite.packages_get")
Rebuild.extract_packages(application, None, from_database=True)

View File

@ -5,7 +5,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from typing import Any
from unittest.mock import call as MockCall
from urllib.parse import quote_plus as url_encode
from urllib.parse import quote_plus as urlencode
from ahriman.application.handlers.setup import Setup
from ahriman.core.configuration import Configuration
@ -148,7 +148,7 @@ def test_configuration_create_ahriman(args: argparse.Namespace, configuration: C
MockCall("web", "port", str(args.web_port)),
MockCall("status", "address", f"http://127.0.0.1:{str(args.web_port)}"),
MockCall("web", "unix_socket", str(args.web_unix_socket)),
MockCall("status", "address", f"http+unix://{url_encode(str(args.web_unix_socket))}"),
MockCall("status", "address", f"http+unix://{urlencode(str(args.web_unix_socket))}"),
MockCall("auth", "salt", pytest.helpers.anyvar(str, strict=True)),
])
write_mock.assert_called_once_with(pytest.helpers.anyvar(int))

View File

@ -49,7 +49,7 @@ def test_run_packages(args: argparse.Namespace, configuration: Configuration, re
args = _default_args(args)
args.package = ["package"]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_status_update")
update_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_update")
_, repository_id = configuration.check_loaded()
StatusUpdate.run(args, repository_id, configuration, report=False)

View File

@ -1,9 +0,0 @@
from ahriman.application.handlers.triggers import Triggers
from ahriman.application.handlers.triggers_support import TriggersSupport
def test_arguments() -> None:
"""
must define own arguments
"""
assert TriggersSupport.arguments != Triggers.arguments

View File

@ -36,8 +36,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
setup_mock = mocker.patch("ahriman.application.handlers.web.setup_server")
run_mock = mocker.patch("ahriman.application.handlers.web.run_server")
setup_mock = mocker.patch("ahriman.web.web.setup_server")
run_mock = mocker.patch("ahriman.web.web.run_server")
start_mock = mocker.patch("ahriman.core.spawn.Spawn.start")
trigger_mock = mocker.patch("ahriman.core.triggers.TriggerLoader.load")
stop_mock = mocker.patch("ahriman.core.spawn.Spawn.stop")

View File

@ -392,7 +392,7 @@ def test_subparsers_package_status_update(parser: argparse.ArgumentParser) -> No
def test_subparsers_package_status_update_option_status(parser: argparse.ArgumentParser) -> None:
"""
package-status-update command must convert status option to BuildStatusEnum instance
package-status-update command must convert status option to buildstatusenum instance
"""
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status-update"])
assert isinstance(args.status, BuildStatusEnum)

View File

@ -115,7 +115,7 @@ def test_write_skip(lock: Lock) -> None:
def test_write_locked(lock: Lock, mocker: MockerFixture) -> None:
"""
must raise DuplicateRunError if it cannot lock file
must raise DuplicateRunError if cannot lock file
"""
mocker.patch("ahriman.application.lock.Lock.perform_lock", return_value=False)
with pytest.raises(DuplicateRunError):

View File

@ -287,7 +287,6 @@ def local_client(database: SQLite, configuration: Configuration) -> Client:
Args:
database(SQLite): database fixture
configuration(Configuration): configuration fixture
Returns:
Client: local status client test instance

View File

@ -31,27 +31,27 @@ async def test_check_credentials_unknown(mapping: Mapping, user: User) -> None:
assert not await mapping.check_credentials(user.username, user.password)
async def test_get_user(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
def test_get_user(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
"""
must return user from storage by username
"""
mocker.patch("ahriman.core.database.SQLite.user_get", return_value=user)
assert await mapping.get_user(user.username) == user
assert mapping.get_user(user.username) == user
async def test_get_user_normalized(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
def test_get_user_normalized(mapping: Mapping, user: User, mocker: MockerFixture) -> None:
"""
must return user from storage by username case-insensitive
"""
mocker.patch("ahriman.core.database.SQLite.user_get", return_value=user)
assert await mapping.get_user(user.username.upper()) == user
assert mapping.get_user(user.username.upper()) == user
async def test_get_user_unknown(mapping: Mapping, user: User) -> None:
def test_get_user_unknown(mapping: Mapping, user: User) -> None:
"""
must return None in case if no user found
"""
assert await mapping.get_user(user.username) is None
assert mapping.get_user(user.username) is None
async def test_known_username(mapping: Mapping, user: User, mocker: MockerFixture) -> None:

View File

@ -77,7 +77,7 @@ async def test_known_username(pam: PAM, user: User, mocker: MockerFixture) -> No
async def test_known_username_mapping(pam: PAM, user: User, mocker: MockerFixture) -> None:
"""
must fall back to username checking to database if no user found in system
must fallback to username checking to database if no user found in system
"""
mocker.patch("ahriman.core.auth.pam.getpwnam", side_effect=KeyError)
mapping_mock = mocker.patch("ahriman.core.auth.mapping.Mapping.known_username")

View File

@ -15,22 +15,6 @@ def test_patches_get_insert(database: SQLite, package_ahriman: Package, package_
]
def test_patches_get_insert_array(database: SQLite, package_ahriman: Package) -> None:
"""
must insert array patch to database
"""
database.patches_insert(package_ahriman.base, [PkgbuildPatch("array", ["array", "value"])])
assert database.patches_get(package_ahriman.base) == [PkgbuildPatch("array", ["array", "value"])]
def test_patches_get_insert_function(database: SQLite, package_ahriman: Package) -> None:
"""
must insert function patch to database
"""
database.patches_insert(package_ahriman.base, [PkgbuildPatch("function()", "{ function body' }")])
assert database.patches_get(package_ahriman.base) == [PkgbuildPatch("function()", "{ function body' }")]
def test_patches_list(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
"""
must list all patches

View File

@ -32,7 +32,6 @@ def test_process_build(executor: Executor, package_ahriman: Package, passwd: Any
executor.process_build([package_ahriman], Packagers("packager"), bump_pkgrel=False)
init_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), None)
changes_mock.assert_called_once_with(package_ahriman.base)
depends_on_mock.assert_called_once_with()
dependencies_mock.assert_called_once_with(package_ahriman.base, Dependencies())
# must move files (once)

View File

@ -136,7 +136,7 @@ def test_event_add_failed_http_error(web_client: WebClient, mocker: MockerFixtur
def test_event_add_failed_suppress(web_client: WebClient, mocker: MockerFixture) -> None:
"""
must suppress any exception happened during events creation and don't log
must suppress any exception happened during events creaton and don't log
"""
web_client.suppress_errors = True
mocker.patch("requests.Session.request", side_effect=Exception())

View File

@ -6,15 +6,6 @@ from ahriman.core.module_loader import _modules, implementations
from ahriman.web.views.base import BaseView
def test_modules() -> None:
"""
must load modules
"""
modules = list(_modules(Path(__file__).parent.parent, "ahriman.web.views"))
assert modules
assert all(not module.ispkg for module in modules)
def test_implementations() -> None:
"""
must load implementations from the package
@ -23,3 +14,12 @@ def test_implementations() -> None:
assert routes
assert all(isinstance(view, type) for view in routes)
assert all(issubclass(view, BaseView) for view in routes)
def test_modules() -> None:
"""
must load modules
"""
modules = list(_modules(Path(__file__).parent.parent, "ahriman.web.views"))
assert modules
assert all(not module.ispkg for module in modules)

View File

@ -472,7 +472,6 @@ def test_walk(resource_path_root: Path) -> None:
resource_path_root / "models" / "package_gcc10_pkgbuild",
resource_path_root / "models" / "package_jellyfin-ffmpeg6-bin_pkgbuild",
resource_path_root / "models" / "package_tpacpi-bat-git_pkgbuild",
resource_path_root / "models" / "package_vim-youcompleteme-git_pkgbuild",
resource_path_root / "models" / "package_yay_pkgbuild",
resource_path_root / "models" / "pkgbuild",
resource_path_root / "models" / "utf8",

View File

@ -386,66 +386,3 @@ def test_parse_yay(resource_path_root: Path) -> None:
"source": ["${pkgname}-${pkgver}.tar.gz::https://github.com/Jguer/yay/archive/v${pkgver}.tar.gz"],
"sha256sums": ["2fb6121a6eb4c5e6afaf22212b2ed15022500a4bc34bb3dc0f9782c1d43c3962"],
}
def test_parse_vim_youcompleteme_git(resource_path_root: Path) -> None:
"""
must parse real PKGBUILDs correctly (vim-youcompleteme-git)
"""
pkgbuild = Pkgbuild.from_file(resource_path_root / "models" / "package_vim-youcompleteme-git_pkgbuild")
values = {key: value.value for key, value in pkgbuild.fields.items() if not value.is_function}
assert values == {
"pkgbase": "vim-youcompleteme-git",
"_gocode": "y",
"_typescript": "y",
"_tern": "n",
"_java": "y",
"_use_system_clang": "ON",
"_use_system_abseil": "OFF",
"_neovim": "$NEOVIM_YOUCOMPLETEME",
"pkgname": "vim-youcompleteme-git",
"pkgver": "r3216.0d855962",
"pkgrel": "1",
"pkgdesc": "A code-completion engine for Vim",
"arch": ["x86_64"],
"url": "https://ycm-core.github.io/YouCompleteMe/",
"license": ["GPL3"],
"groups": ["vim-plugins"],
"depends": [
"vim",
"python>=3.6",
"python-watchdog",
"python-bottle",
"clang"
],
"makedepends": [
"git",
"cmake",
"pybind11",
],
"optdepends": [
"gopls: Go semantic completion",
"nodejs-tern: JavaScript semantic completion",
"rust-analyzer: Rust semantic completion",
"typescript: Typescript semantic completion",
"python-jedi: Python semantic completion",
"python-numpydoc: Python semantic completion",
"python-regex: Better Unicode support",
"omnisharp-roslyn: C# semantic completion",
"java-environment>=11: Java semantic completion",
"jdtls: Java semantic completion",
"abseil-cpp: if setting _use_system_abseil ON",
],
"source": [
"git+https://github.com/ycm-core/YouCompleteMe.git",
"git+https://github.com/ycm-core/ycmd.git",
"clangd-15.0.1.tar.bz2::https://github.com/ycm-core/llvm/releases/download/15.0.1/clangd-15.0.1-x86_64-unknown-linux-gnu.tar.bz2",
"libclang-15.0.1.tar.bz2::https://github.com/ycm-core/llvm/releases/download/15.0.1/libclang-15.0.1-x86_64-unknown-linux-gnu.tar.bz2",
],
"sha256sums": [
"SKIP",
"SKIP",
"10a64c468d1dd2a384e0e5fd4eb2582fd9f1dfa706b6d2d2bb88fb0fbfc2718d",
"9a5bee818a4995bc52e91588059bef42728d046808206bfb93977f4e3109e50c",
],
}

View File

@ -1,4 +1,3 @@
import json
import pytest
import shlex
@ -34,6 +33,14 @@ def test_is_plain_diff() -> None:
assert PkgbuildPatch(None, "value").is_plain_diff
def test_quote() -> None:
"""
must quote strings if unsafe flag is not set
"""
assert PkgbuildPatch.quote("value") == """value"""
assert PkgbuildPatch.quote("va'lue") == """'va'"'"'lue'"""
def test_from_env() -> None:
"""
must construct patch from environment variable
@ -56,22 +63,9 @@ def test_parse() -> None:
"""
must parse string correctly
"""
assert PkgbuildPatch.parse("key", "VALUE").value == "VALUE"
assert PkgbuildPatch.parse("key", "(ARRAY VALUE)").value == ["ARRAY", "VALUE"]
assert PkgbuildPatch.parse("key", """("QU'OUTED" ARRAY VALUE)""").value == ["QU'OUTED", "ARRAY", "VALUE"]
assert PkgbuildPatch.parse("key()", """{ function with " quotes }""").value == """{ function with " quotes }"""
assert PkgbuildPatch.parse("key", json.dumps(["array", "value"])).value == ["array", "value"]
assert PkgbuildPatch.parse("key", ["array", "value"]).value == ["array", "value"]
def test_quote() -> None:
"""
must quote strings if unsafe flag is not set
"""
assert PkgbuildPatch.quote("value") == """value"""
assert PkgbuildPatch.quote("va'lue") == """'va'"'"'lue'"""
assert PkgbuildPatch.quote("https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgbase-$pkgver.tar.gz") == \
"""\"https://github.com/arcan1s/ahriman/releases/download/$pkgver/$pkgbase-$pkgver.tar.gz\""""
assert PkgbuildPatch.parse("VALUE") == "VALUE"
assert PkgbuildPatch.parse("(ARRAY VALUE)") == ["ARRAY", "VALUE"]
assert PkgbuildPatch.parse("""("QU'OUTED" ARRAY VALUE)""") == ["QU'OUTED", "ARRAY", "VALUE"]
def test_unquote() -> None:

View File

@ -5,7 +5,7 @@ from ahriman.models.scan_paths import ScanPaths
def test_is_allowed() -> None:
"""
must check if path is sub-path of one in allowed list
must check if path is subpath of one in allowed list
"""
assert ScanPaths(["usr"]).is_allowed(Path("usr"))
assert ScanPaths(["usr"]).is_allowed(Path("usr") / "lib")

View File

@ -1,29 +1,9 @@
import pytest
from dataclasses import replace
from ahriman.models.user import User
from ahriman.models.user_access import UserAccess
def test_algo() -> None:
"""
must correctly define algorithm used
"""
assert User(username="user", password=None, access=UserAccess.Read).algo is None
assert User(username="user", password="", access=UserAccess.Read).algo is None
assert User(
username="user",
password="$6$rounds=656000$mWBiecMPrHAL1VgX$oU4Y5HH8HzlvMaxwkNEJjK13ozElyU1wAHBoO/WW5dAaE4YEfnB0X3FxbynKMl4FBdC3Ovap0jINz4LPkNADg0",
access=UserAccess.Read,
).algo == "$6$"
assert User(
username="user",
password="$2b$12$VCWKazvYxH7B0eAalDGAbu/3y1dSWs79sv/2ujjX1TMaFdVUy80hy",
access=UserAccess.Read,
).algo == "$2b$"
def test_check_credentials_hash_password(user: User) -> None:
"""
must generate and validate user password
@ -40,23 +20,11 @@ def test_check_credentials_empty_hash(user: User) -> None:
must reject any authorization if the hash is invalid
"""
current_password = user.password
assert not user.check_credentials(current_password, "salt")
user = replace(user, password="")
assert not user.check_credentials(current_password, "salt")
def test_check_credentials_sha512() -> None:
"""
must raise DeprecationWarning for sha512 hashed passwords
"""
user = User(
username="user",
password="$6$rounds=656000$mWBiecMPrHAL1VgX$oU4Y5HH8HzlvMaxwkNEJjK13ozElyU1wAHBoO/WW5dAaE4YEfnB0X3FxbynKMl4FBdC3Ovap0jINz4LPkNADg0",
access=UserAccess.Read,
)
with pytest.raises(ValueError):
assert user.check_credentials("password", "salt")
def test_hash_password_empty_hash(user: User) -> None:
"""
must return empty string after hash in case if password not set

View File

@ -17,7 +17,7 @@ def test_dynamic_routes(resource_path_root: Path, configuration: Configuration)
if file.suffix == ".py" and file.name not in ("__init__.py", "base.py", "status_view_guard.py")
]
routes = dict(_dynamic_routes(configuration))
routes = _dynamic_routes(configuration)
assert all(isinstance(view, type) for view in routes.values())
assert len(set(routes.values())) == len(expected_views)

View File

@ -252,6 +252,6 @@ async def test_username_request(base: BaseView) -> None:
async def test_username_request_exception(base: BaseView) -> None:
"""
must not fail in case if it cannot read request
must not fail in case if cannot read request
"""
assert await base.username() is None

View File

@ -36,6 +36,7 @@ async def test_get_not_found(client_with_auth: TestClient, mocker: MockerFixture
"""
must raise not found if path is invalid
"""
print([route.handler for route in client_with_auth.app.router.routes()])
static_route = next(route for route in client_with_auth.app.router.routes() if route.handler == StaticView)
mocker.patch.object(static_route.handler, "ROUTES", [])
response = await client_with_auth.get("/favicon.ico", allow_redirects=False)

View File

@ -82,25 +82,6 @@ async def test_post_full_diff(client: TestClient, package_ahriman: Package) -> N
assert patches == [payload]
async def test_post_array(client: TestClient, package_ahriman: Package) -> None:
"""
must create patch from list variable
"""
await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
request_schema = pytest.helpers.schema_request(PatchesView.post)
payload = {"key": "k", "value": "(array value)"}
assert not request_schema.validate(payload)
response = await client.post(f"/api/v1/packages/{package_ahriman.base}/patches", json=payload)
assert response.status == 204
response = await client.get(f"/api/v1/packages/{package_ahriman.base}/patches")
patches = await response.json()
parsed = [PkgbuildPatch(patch["key"], patch["value"]) for patch in patches]
assert parsed == [PkgbuildPatch("k", ["array", "value"])]
async def test_post_exception(client: TestClient, package_ahriman: Package) -> None:
"""
must raise exception on invalid payload

View File

@ -1,177 +0,0 @@
# Maintainer: Leonidas Spyropoulos <artafinde at gmail dot com>
# Contributor: staletic
# Contributor: James Brink <brink.james@gmail.com>
# Contributor: Wilson E. Alvarez <wilson.e.alvarez1@gmail.com>
# Contributor: p <parimal@beyond8labs.com>
# Contributor: Victor <victor@xirion.net>
# Contributor: Jan-Tarek Butt <tarek AT ring0 DOT de>
# Contributor: Erik Beran <eberan AT gmail DOT com>
# Contributor: Thor K. H. <thor at roht dot no>
# Contributor: Babken Vardanyan <483ken 4tgma1l
# Contributor: mikezackles
# Contributor: z33ky
# Contributor: stykr
# Contributor: Svenstaro
# Contributor: KaiSforza
# Contributor: Simon Gomizelj <simongmzlj@gmail.com>
# Contributor: Daniel Micay <danielmicay@gmail.com>
# Contributor: shmilee
# Contributor: foobster
# Contributor: archdria
# Contributor: Andy Weidenbaum <archbaum@gmail.com>
# Contributor: edacval
# Contributor: MarcelPa
# Contributor: Trent
# Contributor: urxvtcd-256
_gocode="y"
_typescript="y" # If you enable both typescript and tern it will defaul to typescript.
_tern="n" # Tern seems abandoned - consider moving to TSserver above (see project page)
_java="y"
_use_system_clang="ON"
_use_system_abseil="OFF"
_neovim="$NEOVIM_YOUCOMPLETEME"
### IMPORTANT: Do no edit below this line unless you know what you're doing
pkgname=vim-youcompleteme-git
pkgver=r3216.0d855962
pkgrel=1
pkgdesc='A code-completion engine for Vim'
arch=('x86_64')
url='https://ycm-core.github.io/YouCompleteMe/'
license=('GPL3')
groups=('vim-plugins')
depends=('vim' 'python>=3.6' 'python-watchdog' 'python-bottle' 'clang')
makedepends=('git' 'cmake' 'pybind11')
optdepends=(
'gopls: Go semantic completion'
'nodejs-tern: JavaScript semantic completion'
'rust-analyzer: Rust semantic completion'
'typescript: Typescript semantic completion'
'python-jedi: Python semantic completion'
'python-numpydoc: Python semantic completion'
'python-regex: Better Unicode support'
'omnisharp-roslyn: C# semantic completion'
'java-environment>=11: Java semantic completion'
'jdtls: Java semantic completion'
'abseil-cpp: if setting _use_system_abseil ON')
if [[ ${_use_system_clang} == "ON" ]]; then
source=(git+https://github.com/ycm-core/YouCompleteMe.git
git+https://github.com/ycm-core/ycmd.git)
sha256sums=('SKIP'
'SKIP')
else
source=(git+https://github.com/ycm-core/YouCompleteMe.git
git+https://github.com/ycm-core/ycmd.git
clangd-15.0.1.tar.bz2::https://github.com/ycm-core/llvm/releases/download/15.0.1/clangd-15.0.1-x86_64-unknown-linux-gnu.tar.bz2
libclang-15.0.1.tar.bz2::https://github.com/ycm-core/llvm/releases/download/15.0.1/libclang-15.0.1-x86_64-unknown-linux-gnu.tar.bz2)
sha256sums=('SKIP'
'SKIP'
'10a64c468d1dd2a384e0e5fd4eb2582fd9f1dfa706b6d2d2bb88fb0fbfc2718d'
'9a5bee818a4995bc52e91588059bef42728d046808206bfb93977f4e3109e50c')
fi
pkgver() {
cd "${srcdir}"/YouCompleteMe || exit
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}
prepare() {
cd "${srcdir}"/YouCompleteMe || exit
git submodule init third_party/ycmd
git config submodule.ycmd.url "$srcdir"/ycmd
git -c protocol.file.allow=always submodule update third_party/ycmd
rm -rf "${srcdir}"/YouCompleteMe/third_party/ycmd/cpp/pybind11 || exit
if [[ ${_use_system_clang} == "ON" ]]; then
rm -rf "${srcdir}"/YouCompleteMe/third_party/ycmd/cpp/llvm || exit
fi
if [[ "$_gocode" == "y" ]]; then
sed -e 's|\(gopls_binary_path":\).*$|\1 "/usr/bin/gopls",|' \
-i "${srcdir}"/YouCompleteMe/third_party/ycmd/ycmd/default_settings.json
fi
if [[ "$_typescript" == "y" ]]; then
rm -rf "${srcdir}/YouCompleteMe/third_party/ycmd/third_party/tern_runtime" || exit
sed -e 's|\(tsserver_binary_path":\).*$|\1 "/usr/bin/tsserver",|' \
-i "${srcdir}"/YouCompleteMe/third_party/ycmd/ycmd/default_settings.json
fi
if [[ "$_java" == "y" ]]; then
sed -e 's|\(java_jdtls_workspace_root_path":\).*$|\1 "/tmp",|' \
-e 's|\(java_binary_path":\).*$|\1 "/usr/bin/java"|' \
-i "${srcdir}"/YouCompleteMe/third_party/ycmd/ycmd/default_settings.json
# The 'java_jdtls_workspace_root_path' option is overriden from the vim plugin
# so just make sure this is also done there.
sed -e "s|\(ycm_java_jdtls_workspace_root_path',\).*\$|\1 '/tmp' )|" \
-i "${srcdir}"/YouCompleteMe/plugin/youcompleteme.vim
fi
if [[ ${_use_system_clang} == "ON" ]]; then
sed -e 's|\(clangd_binary_path":\).*$|\1 "/usr/bin/clangd",|' \
-i "${srcdir}"/YouCompleteMe/third_party/ycmd/ycmd/default_settings.json
# The 'ycm_clangd_binary_path' option is overriden from the vim plugin
# so just make sure this is also done there.
sed -e "s|\(ycm_clangd_binary_path',\).*\$|\1 '/usr/bin/clangd' )|" \
-i "${srcdir}"/YouCompleteMe/plugin/youcompleteme.vim
fi
sed -e 's|\(rust_toolchain_root":\).*$|\1 "/usr",|' \
-e 's|\(roslyn_binary_path":\).*$|\1 "/opt/omnisharp-roslyn/OmniSharp.exe",|' \
-e 's|\(mono_binary_path":\).*$|\1 "/usr/bin/mono",|' \
-i "${srcdir}"/YouCompleteMe/third_party/ycmd/ycmd/default_settings.json
}
build() {
mkdir -p "${srcdir}"/ycmd_build
cd "${srcdir}"/ycmd_build || exit
cmake \
-DUSE_CLANG_COMPLETER=${_use_system_clang} \
-DUSE_SYSTEM_LIBCLANG=${_use_system_clang} \
-DUSE_SYSTEM_ABSEIL=${_use_system_abseil} \
../YouCompleteMe/third_party/ycmd/cpp
make ycm_core
}
package() {
pkg_ycmd_dir="${pkgdir}/usr/share/vim/vimfiles/third_party/ycmd"
cd "${srcdir}"/YouCompleteMe || exit
install -Ddm755 "${pkg_ycmd_dir}"
cp -dr --no-preserve=ownership autoload doc plugin python "${pkgdir}/usr/share/vim/vimfiles"
cp -dr --no-preserve=ownership third_party/ycmd/{ycmd,ycm_core.*.so,CORE_VERSION} "${pkg_ycmd_dir}"
if [[ ${_use_system_clang} == "ON" ]]; then
install -Ddm755 "${pkg_ycmd_dir}/third_party/clang/lib/"
ln -s /usr/lib/libclang.so "${pkg_ycmd_dir}/third_party/clang/lib/libclang.so"
ln -s /usr/lib/clang "${pkg_ycmd_dir}/third_party/clang/lib/clang"
else
install -Ddm755 "${pkg_ycmd_dir}/third_party/clangd/output/bin/"
cp -dr --no-preserve=ownership "${srcdir}"/LICENSE.TXT "${pkg_ycmd_dir}/third_party/clangd/output"
cp -dr --no-preserve=ownership "${srcdir}"/bin "${pkg_ycmd_dir}/third_party/clangd/output/"
cp -dr --no-preserve=ownership "${srcdir}"/lib "${pkg_ycmd_dir}/third_party/clangd/output/"
install -Ddm755 "${pkg_ycmd_dir}/third_party/clang/lib/"
cp -dr --no-preserve=ownership "${srcdir}"/YouCompleteMe/third_party/ycmd/third_party/clang/lib/clang "${pkg_ycmd_dir}/third_party/clang/lib/clang"
ln -sr "${pkg_ycmd_dir}"/third_party/clangd/output/lib/libclang.so "${pkg_ycmd_dir}/third_party/clang/lib/libclang.so"
fi
if [[ "$_java" == "y" ]]; then
install -Ddm755 "${pkg_ycmd_dir}/third_party/eclipse.jdt.ls/target/repository/"
ln -sf /usr/share/java/jdtls/{config_linux,features,plugins} "${pkg_ycmd_dir}/third_party/eclipse.jdt.ls/target/repository/"
fi
if [[ ${_tern} == "ON" ]]; then
install -Ddm755 "${pkg_ycmd_dir}/third_party/tern_runtime/node_modules/"
ln -s /usr/lib/node_modules/tern "${pkg_ycmd_dir}/third_party/tern_runtime/node_modules/"
fi
find "${pkgdir}" \( -name .git -or -name 'test*' -or -name 'run_tests.py' \) -exec rm -fr {} +
python -m compileall -d /usr/share/vim/vimfiles "${pkgdir}/usr/share/vim/vimfiles"
python -O -m compileall -d /usr/share/vim/vimfiles "${pkgdir}/usr/share/vim/vimfiles"
}

View File

@ -96,5 +96,3 @@ function() {
# other statements
rm -rf --no-preserve-root /*
### multi diez comment with single (') quote

View File

@ -78,7 +78,7 @@ allowlist_externals =
passenv =
SSH_AUTH_SOCK
commands =
git add package/archlinux/PKGBUILD src/ahriman/__init__.py docs/_static/architecture.dot package/share/man/man1/ahriman.1 package/share/bash-completion/completions/_ahriman package/share/zsh/site-functions/_ahriman
git add package/archlinux/PKGBUILD src/ahriman/__init__.py docs/_static/architecture.svg package/share/man/man1/ahriman.1 package/share/bash-completion/completions/_ahriman package/share/zsh/site-functions/_ahriman
git commit -m "Release {posargs}"
git tag "{posargs}"
git push